Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: I3d92a6de21a938f6b352ec26dc23420c0fe02b27
Change-Id: Ifdb80563ef042738778ebb8a7581a97c4e3d96e2
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 2adbc1f..7c30c8b 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 @@
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/bmgr/TEST_MAPPING b/cmds/bmgr/TEST_MAPPING
new file mode 100644
index 0000000..7c0e79e
--- /dev/null
+++ b/cmds/bmgr/TEST_MAPPING
@@ -0,0 +1,11 @@
+{
+ "presubmit": [
+ ],
+ "postsubmit": [
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/services/backup"
+ }
+ ]
+}
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 680ccfc..ed717c4 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -64,6 +64,10 @@
private static final String BMGR_NOT_RUNNING_ERR =
"Error: Could not access the Backup Manager. Is the system running?";
+ private static final String BMGR_NOT_ACTIVATED_FOR_USER =
+ "Error: Backup Manager is not activated for user ";
+ private static final String BMGR_ERR_NO_RESTORESESSION_FOR_USER =
+ "Error: Could not get restore session for user ";
private static final String TRANSPORT_NOT_RUNNING_ERR =
"Error: Could not access the backup transport. Is the system running?";
private static final String PM_NOT_RUNNING_ERR =
@@ -121,6 +125,11 @@
return;
}
+ if ("autorestore".equals(op)) {
+ doAutoRestore(userId);
+ return;
+ }
+
if ("enabled".equals(op)) {
doEnabled(userId);
return;
@@ -190,21 +199,45 @@
showUsage();
}
- boolean isBackupActive(@UserIdInt int userId) {
+ private void handleRemoteException(RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+
+ private boolean isBackupActive(@UserIdInt int userId) {
try {
if (!mBmgr.isBackupServiceActive(userId)) {
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ System.err.println(BMGR_NOT_ACTIVATED_FOR_USER + userId);
return false;
}
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
return false;
}
return true;
}
+ private void doAutoRestore(int userId) {
+ String arg = nextArg();
+ if (arg == null) {
+ showUsage();
+ return;
+ }
+
+ try {
+ boolean enable = Boolean.parseBoolean(arg);
+ mBmgr.setAutoRestore(enable);
+ System.out.println(
+ "Auto restore is now "
+ + (enable ? "enabled" : "disabled")
+ + " for user "
+ + userId);
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ }
+ }
+
private String activatedToString(boolean activated) {
return activated ? "activated" : "deactivated";
}
@@ -214,8 +247,7 @@
System.out.println("Backup Manager currently "
+ activatedToString(mBmgr.isBackupServiceActive(userId)));
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -230,8 +262,7 @@
System.out.println("Backup Manager currently "
+ enableToString(isEnabled));
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -250,8 +281,7 @@
showUsage();
return;
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -259,8 +289,7 @@
try {
mBmgr.backupNowForUser(userId);
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -274,8 +303,7 @@
try {
mBmgr.dataChangedForUser(userId, pkg);
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -292,8 +320,7 @@
mBmgr.fullTransportBackupForUser(
userId, allPkgs.toArray(new String[allPkgs.size()]));
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
}
@@ -421,8 +448,7 @@
try {
filteredPackages = mBmgr.filterAppsEligibleForBackupForUser(userId, packages);
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
backupNowPackages(userId, Arrays.asList(filteredPackages), nonIncrementalBackup,
monitorState);
@@ -455,8 +481,7 @@
System.err.println("Unable to run backup");
}
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -506,8 +531,7 @@
try {
mBmgr.cancelBackupsForUser(userId);
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
return;
}
@@ -537,8 +561,7 @@
}
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -569,8 +592,7 @@
}
});
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
return;
}
@@ -598,8 +620,7 @@
mBmgr.clearBackupDataForUser(userId, transport, pkg);
System.out.println("Wiped backup data for " + pkg + " on " + transport);
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -632,8 +653,7 @@
observer.waitForCompletion(30*1000L);
System.out.println("Initialization result: " + observer.result);
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -648,7 +668,7 @@
try {
mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
return;
}
@@ -658,8 +678,7 @@
mRestore.endRestoreSession();
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -686,8 +705,7 @@
System.out.println(pad + t);
}
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -805,7 +823,7 @@
boolean didRestore = false;
mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null);
if (mRestore == null) {
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId);
return;
}
RestoreSet[] sets = null;
@@ -851,8 +869,7 @@
System.out.println("done");
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -865,8 +882,7 @@
}
}
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -886,8 +902,7 @@
+ " for user "
+ userId);
} catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(BMGR_NOT_RUNNING_ERR);
+ handleRemoteException(e);
}
}
@@ -928,6 +943,7 @@
System.err.println(" bmgr init TRANSPORT...");
System.err.println(" bmgr activate BOOL");
System.err.println(" bmgr activated");
+ System.err.println(" bmgr autorestore BOOL");
System.err.println("");
System.err.println("The '--user' option specifies the user on which the operation is run.");
System.err.println("It must be the first argument before the operation.");
@@ -1002,6 +1018,9 @@
System.err.println("");
System.err.println("The 'activated' command reports the current activated/deactivated");
System.err.println("state of the backup mechanism.");
+ System.err.println("");
+ System.err.println("The 'autorestore' command enables or disables automatic restore when");
+ System.err.println("a new package is installed.");
}
private static class BackupMonitor extends IBackupManagerMonitor.Stub {
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index c60d08b..757c2b2 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -43,6 +43,10 @@
],
init_rc: ["bootanim.rc"],
+
+ cflags: [
+ "-Wno-deprecated-declarations",
+ ],
}
// libbootanimation
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 85db4b9..bb2de17 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -42,12 +42,13 @@
#include <android-base/properties.h>
+#include <ui/DisplayConfig.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <ui/Region.h>
-#include <ui/DisplayInfo.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/DisplayEventReceiver.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -106,14 +107,15 @@
static const int TEXT_CENTER_VALUE = INT_MAX;
static const int TEXT_MISSING_VALUE = INT_MIN;
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
+static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
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", "");
@@ -153,8 +155,7 @@
return mSession;
}
-void BootAnimation::binderDied(const wp<IBinder>&)
-{
+void BootAnimation::binderDied(const wp<IBinder>&) {
// woah, surfaceflinger died!
SLOGD("SurfaceFlinger died, exiting...");
@@ -218,8 +219,7 @@
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());
@@ -277,48 +277,173 @@
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();
mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
if (mDisplayToken == nullptr)
- return -1;
+ return NAME_NOT_FOUND;
- DisplayInfo dinfo;
- status_t status = SurfaceComposerClient::getDisplayInfo(mDisplayToken, &dinfo);
- if (status)
- return -1;
+ DisplayConfig displayConfig;
+ const status_t error =
+ SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig);
+ if (error != NO_ERROR)
+ return error;
+ 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"),
- dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
+ resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::Transaction t;
+
+ // this guest property specifies multi-display IDs to show the boot animation
+ // multiple ids can be set with comma (,) as separator, for example:
+ // setprop persist.boot.animation.displays 19260422155234049,19261083906282754
+ Vector<uint64_t> physicalDisplayIds;
+ char displayValue[PROPERTY_VALUE_MAX] = "";
+ property_get(DISPLAYS_PROP_NAME, displayValue, "");
+ bool isValid = displayValue[0] != '\0';
+ if (isValid) {
+ char *p = displayValue;
+ while (*p) {
+ if (!isdigit(*p) && *p != ',') {
+ isValid = false;
+ break;
+ }
+ p ++;
+ }
+ if (!isValid)
+ SLOGE("Invalid syntax for the value of system prop: %s", DISPLAYS_PROP_NAME);
+ }
+ if (isValid) {
+ std::istringstream stream(displayValue);
+ for (PhysicalDisplayId id; stream >> id; ) {
+ physicalDisplayIds.add(id);
+ if (stream.peek() == ',')
+ stream.ignore();
+ }
+
+ // In the case of multi-display, boot animation shows on the specified displays
+ // in addition to the primary display
+ auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ constexpr uint32_t LAYER_STACK = 0;
+ for (auto id : physicalDisplayIds) {
+ if (std::find(ids.begin(), ids.end(), id) != ids.end()) {
+ sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(id);
+ if (token != nullptr)
+ t.setDisplayLayerStack(token, LAYER_STACK);
+ }
+ }
+ t.setLayerStack(control, LAYER_STACK);
+ }
+
t.setLayer(control, 0x40000000)
.apply();
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);
@@ -334,9 +459,47 @@
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()) {
@@ -397,15 +560,14 @@
}
}
-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);
@@ -416,11 +578,10 @@
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");
@@ -439,19 +600,19 @@
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;
@@ -566,8 +727,7 @@
}
-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) {
@@ -688,8 +848,7 @@
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)) {
@@ -756,8 +915,7 @@
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;
@@ -854,8 +1012,7 @@
return true;
}
-bool BootAnimation::movie()
-{
+bool BootAnimation::movie() {
if (mAnimation == nullptr) {
mAnimation = loadAnimation(mZipFileName);
}
@@ -941,12 +1098,9 @@
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());
@@ -977,6 +1131,11 @@
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();
@@ -1060,6 +1219,12 @@
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;
@@ -1092,7 +1257,7 @@
SurfaceComposerClient::Transaction t;
t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
.setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
- t.setDisplayProjection(mDisplayToken, 0 /* orientation */, layerStackRect, displayRect);
+ t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, layerStackRect, displayRect);
t.apply();
mTargetInset = mCurrentInset = 0;
@@ -1102,8 +1267,7 @@
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)
@@ -1114,8 +1278,7 @@
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());
@@ -1136,10 +1299,10 @@
parseAnimationDesc(*animation);
if (!preloadZip(*animation)) {
+ releaseAnimation(animation);
return nullptr;
}
-
mLoadedFiles.remove(fn);
return animation;
}
@@ -1260,7 +1423,7 @@
if (mSystemWd < 0) {
close(mInotifyFd);
mInotifyFd = -1;
- SLOGE("Could not add watch for %s", SYSTEM_DATA_DIR_PATH);
+ SLOGE("Could not add watch for %s: %s", SYSTEM_DATA_DIR_PATH, strerror(errno));
return NO_INIT;
}
@@ -1277,5 +1440,4 @@
// ---------------------------------------------------------------------------
-}
-; // namespace android
+} // namespace android
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 574d65e..6ba7fd4 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 @@
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 @@
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 @@
Texture mAndroid[2];
int mWidth;
int mHeight;
+ int mMaxWidth = 0;
+ int mMaxHeight = 0;
int mCurrentInset;
int mTargetInset;
bool mUseNpotTextures = false;
@@ -189,6 +202,8 @@
sp<TimeCheckThread> mTimeCheckThread = nullptr;
sp<Callbacks> mCallbacks;
Animation* mAnimation = nullptr;
+ std::unique_ptr<DisplayEventReceiver> mDisplayEventReceiver;
+ sp<Looper> mLooper;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 55dbc17..ca1d598 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -32,8 +32,11 @@
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.Pair;
import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
/**
* This class is a command line utility for manipulating content. A client
@@ -72,7 +75,7 @@
"usage: adb shell content [subcommand] [options]\n"
+ "\n"
+ "usage: adb shell content insert --uri <URI> [--user <USER_ID>]"
- + " --bind <BINDING> [--bind <BINDING>...]\n"
+ + " --bind <BINDING> [--bind <BINDING>...] [--extra <BINDING>...]\n"
+ " <URI> a content provider URI.\n"
+ " <BINDING> binds a typed value to a column and is formatted:\n"
+ " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
@@ -84,7 +87,8 @@
+ " adb shell content insert --uri content://settings/secure --bind name:s:new_setting"
+ " --bind value:s:new_value\n"
+ "\n"
- + "usage: adb shell content update --uri <URI> [--user <USER_ID>] [--where <WHERE>]\n"
+ + "usage: adb shell content update --uri <URI> [--user <USER_ID>]"
+ + " [--where <WHERE>] [--extra <BINDING>...]\n"
+ " <WHERE> is a SQL style where clause in quotes (You have to escape single quotes"
+ " - see example below).\n"
+ " Example:\n"
@@ -93,14 +97,15 @@
+ " value:s:newer_value --where \"name=\'new_setting\'\"\n"
+ "\n"
+ "usage: adb shell content delete --uri <URI> [--user <USER_ID>] --bind <BINDING>"
- + " [--bind <BINDING>...] [--where <WHERE>]\n"
+ + " [--bind <BINDING>...] [--where <WHERE>] [--extra <BINDING>...]\n"
+ " Example:\n"
+ " # Remove \"new_setting\" secure setting.\n"
+ " adb shell content delete --uri content://settings/secure "
+ "--where \"name=\'new_setting\'\"\n"
+ "\n"
+ "usage: adb shell content query --uri <URI> [--user <USER_ID>]"
- + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]\n"
+ + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]"
+ + " [--extra <BINDING>...]\n"
+ " <PROJECTION> is a list of colon separated column names and is formatted:\n"
+ " <COLUMN_NAME>[:<COLUMN_NAME>...]\n"
+ " <SORT_ORDER> is the order in which rows in the result should be sorted.\n"
@@ -196,6 +201,7 @@
Uri uri = null;
int userId = UserHandle.USER_SYSTEM;
ContentValues values = new ContentValues();
+ Bundle extras = new Bundle();
for (String argument; (argument = mTokenizer.nextArg()) != null;) {
if (ARGUMENT_URI.equals(argument)) {
uri = Uri.parse(argumentValueRequired(argument));
@@ -203,6 +209,8 @@
userId = Integer.parseInt(argumentValueRequired(argument));
} else if (ARGUMENT_BIND.equals(argument)) {
parseBindValue(values);
+ } else if (ARGUMENT_EXTRA.equals(argument)) {
+ parseBindValue(extras);
} else {
throw new IllegalArgumentException("Unsupported argument: " + argument);
}
@@ -215,20 +223,23 @@
throw new IllegalArgumentException("Bindings not specified."
+ " Did you specify --bind argument(s)?");
}
- return new InsertCommand(uri, userId, values);
+ return new InsertCommand(uri, userId, values, extras);
}
private DeleteCommand parseDeleteCommand() {
Uri uri = null;
int userId = UserHandle.USER_SYSTEM;
- String where = null;
+ Bundle extras = new Bundle();
for (String argument; (argument = mTokenizer.nextArg())!= null;) {
if (ARGUMENT_URI.equals(argument)) {
uri = Uri.parse(argumentValueRequired(argument));
} else if (ARGUMENT_USER.equals(argument)) {
userId = Integer.parseInt(argumentValueRequired(argument));
} else if (ARGUMENT_WHERE.equals(argument)) {
- where = argumentValueRequired(argument);
+ extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
+ argumentValueRequired(argument));
+ } else if (ARGUMENT_EXTRA.equals(argument)) {
+ parseBindValue(extras);
} else {
throw new IllegalArgumentException("Unsupported argument: " + argument);
}
@@ -237,23 +248,26 @@
throw new IllegalArgumentException("Content provider URI not specified."
+ " Did you specify --uri argument?");
}
- return new DeleteCommand(uri, userId, where);
+ return new DeleteCommand(uri, userId, extras);
}
private UpdateCommand parseUpdateCommand() {
Uri uri = null;
int userId = UserHandle.USER_SYSTEM;
- String where = null;
ContentValues values = new ContentValues();
+ Bundle extras = new Bundle();
for (String argument; (argument = mTokenizer.nextArg())!= null;) {
if (ARGUMENT_URI.equals(argument)) {
uri = Uri.parse(argumentValueRequired(argument));
} else if (ARGUMENT_USER.equals(argument)) {
userId = Integer.parseInt(argumentValueRequired(argument));
} else if (ARGUMENT_WHERE.equals(argument)) {
- where = argumentValueRequired(argument);
+ extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
+ argumentValueRequired(argument));
} else if (ARGUMENT_BIND.equals(argument)) {
parseBindValue(values);
+ } else if (ARGUMENT_EXTRA.equals(argument)) {
+ parseBindValue(extras);
} else {
throw new IllegalArgumentException("Unsupported argument: " + argument);
}
@@ -266,7 +280,7 @@
throw new IllegalArgumentException("Bindings not specified."
+ " Did you specify --bind argument(s)?");
}
- return new UpdateCommand(uri, userId, values, where);
+ return new UpdateCommand(uri, userId, values, extras);
}
public CallCommand parseCallCommand() {
@@ -274,7 +288,7 @@
int userId = UserHandle.USER_SYSTEM;
String arg = null;
Uri uri = null;
- ContentValues values = new ContentValues();
+ Bundle extras = new Bundle();
for (String argument; (argument = mTokenizer.nextArg())!= null;) {
if (ARGUMENT_URI.equals(argument)) {
uri = Uri.parse(argumentValueRequired(argument));
@@ -285,11 +299,10 @@
} else if (ARGUMENT_ARG.equals(argument)) {
arg = argumentValueRequired(argument);
} else if (ARGUMENT_EXTRA.equals(argument)) {
- parseBindValue(values);
+ parseBindValue(extras);
} else {
throw new IllegalArgumentException("Unsupported argument: " + argument);
}
-
}
if (uri == null) {
throw new IllegalArgumentException("Content provider URI not specified."
@@ -298,7 +311,7 @@
if (method == null) {
throw new IllegalArgumentException("Content provider method not specified.");
}
- return new CallCommand(uri, userId, method, arg, values);
+ return new CallCommand(uri, userId, method, arg, extras);
}
private GetTypeCommand parseGetTypeCommand() {
@@ -363,19 +376,22 @@
Uri uri = null;
int userId = UserHandle.USER_SYSTEM;
String[] projection = null;
- String sort = null;
- String where = null;
+ Bundle extras = new Bundle();
for (String argument; (argument = mTokenizer.nextArg())!= null;) {
if (ARGUMENT_URI.equals(argument)) {
uri = Uri.parse(argumentValueRequired(argument));
} else if (ARGUMENT_USER.equals(argument)) {
userId = Integer.parseInt(argumentValueRequired(argument));
} else if (ARGUMENT_WHERE.equals(argument)) {
- where = argumentValueRequired(argument);
+ extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
+ argumentValueRequired(argument));
} else if (ARGUMENT_SORT.equals(argument)) {
- sort = argumentValueRequired(argument);
+ extras.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER,
+ argumentValueRequired(argument));
} else if (ARGUMENT_PROJECTION.equals(argument)) {
projection = argumentValueRequired(argument).split("[\\s]*:[\\s]*");
+ } else if (ARGUMENT_EXTRA.equals(argument)) {
+ parseBindValue(extras);
} else {
throw new IllegalArgumentException("Unsupported argument: " + argument);
}
@@ -384,40 +400,76 @@
throw new IllegalArgumentException("Content provider URI not specified."
+ " Did you specify --uri argument?");
}
- return new QueryCommand(uri, userId, projection, where, sort);
+ return new QueryCommand(uri, userId, projection, extras);
}
- private void parseBindValue(ContentValues values) {
+ private List<String> splitWithEscaping(String argument, char splitChar) {
+ final List<String> res = new ArrayList<>();
+ final StringBuilder cur = new StringBuilder();
+ for (int i = 0; i < argument.length(); i++) {
+ char c = argument.charAt(i);
+ if (c == '\\') {
+ if (++i == argument.length()) {
+ throw new IllegalArgumentException("Invalid escaping");
+ } else {
+ // Skip escaping char and insert next
+ c = argument.charAt(i);
+ cur.append(c);
+ }
+ } else if (c == splitChar) {
+ // Splitting char means next string
+ res.add(cur.toString());
+ cur.setLength(0);
+ } else {
+ // Copy non-escaping and non-splitting char
+ cur.append(c);
+ }
+ }
+ res.add(cur.toString());
+ return res;
+ }
+
+ private Pair<String, Object> parseBindValue() {
String argument = mTokenizer.nextArg();
if (TextUtils.isEmpty(argument)) {
throw new IllegalArgumentException("Binding not well formed: " + argument);
}
- final int firstColonIndex = argument.indexOf(COLON);
- if (firstColonIndex < 0) {
+ final List<String> split = splitWithEscaping(argument, COLON.charAt(0));
+ if (split.size() != 3) {
throw new IllegalArgumentException("Binding not well formed: " + argument);
}
- final int secondColonIndex = argument.indexOf(COLON, firstColonIndex + 1);
- if (secondColonIndex < 0) {
- throw new IllegalArgumentException("Binding not well formed: " + argument);
- }
- String column = argument.substring(0, firstColonIndex);
- String type = argument.substring(firstColonIndex + 1, secondColonIndex);
- String value = argument.substring(secondColonIndex + 1);
+ String column = split.get(0);
+ String type = split.get(1);
+ String value = split.get(2);
if (TYPE_STRING.equals(type)) {
- values.put(column, value);
+ return Pair.create(column, value);
} else if (TYPE_BOOLEAN.equalsIgnoreCase(type)) {
- values.put(column, Boolean.parseBoolean(value));
- } else if (TYPE_INTEGER.equalsIgnoreCase(type) || TYPE_LONG.equalsIgnoreCase(type)) {
- values.put(column, Long.parseLong(value));
- } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) {
- values.put(column, Double.parseDouble(value));
+ return Pair.create(column, Boolean.parseBoolean(value));
+ } else if (TYPE_INTEGER.equalsIgnoreCase(type)) {
+ return Pair.create(column, Integer.parseInt(value));
+ } else if (TYPE_LONG.equalsIgnoreCase(type)) {
+ return Pair.create(column, Long.parseLong(value));
+ } else if (TYPE_FLOAT.equalsIgnoreCase(type)) {
+ return Pair.create(column, Float.parseFloat(value));
+ } else if (TYPE_DOUBLE.equalsIgnoreCase(type)) {
+ return Pair.create(column, Double.parseDouble(value));
} else if (TYPE_NULL.equalsIgnoreCase(type)) {
- values.putNull(column);
+ return Pair.create(column, null);
} else {
throw new IllegalArgumentException("Unsupported type: " + type);
}
}
+ private void parseBindValue(ContentValues values) {
+ final Pair<String, Object> columnValue = parseBindValue();
+ values.putObject(columnValue.first, columnValue.second);
+ }
+
+ private void parseBindValue(Bundle extras) {
+ final Pair<String, Object> columnValue = parseBindValue();
+ extras.putObject(columnValue.first, columnValue.second);
+ }
+
private String argumentValueRequired(String argument) {
String value = mTokenizer.nextArg();
if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) {
@@ -500,64 +552,48 @@
private static class InsertCommand extends Command {
final ContentValues mContentValues;
+ final Bundle mExtras;
- public InsertCommand(Uri uri, int userId, ContentValues contentValues) {
+ public InsertCommand(Uri uri, int userId, ContentValues contentValues, Bundle extras) {
super(uri, userId);
mContentValues = contentValues;
+ mExtras = extras;
}
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.insert(resolveCallingPackage(), mUri, mContentValues);
+ provider.insert(resolveCallingPackage(), null, mUri, mContentValues, mExtras);
}
}
private static class DeleteCommand extends Command {
- final String mWhere;
+ final Bundle mExtras;
- public DeleteCommand(Uri uri, int userId, String where) {
+ public DeleteCommand(Uri uri, int userId, Bundle extras) {
super(uri, userId);
- mWhere = where;
+ mExtras = extras;
}
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.delete(resolveCallingPackage(), mUri, mWhere, null);
+ provider.delete(resolveCallingPackage(), null, mUri, mExtras);
}
}
private static class CallCommand extends Command {
final String mMethod, mArg;
- Bundle mExtras = null;
+ final Bundle mExtras;
- public CallCommand(Uri uri, int userId, String method, String arg, ContentValues values) {
+ public CallCommand(Uri uri, int userId, String method, String arg, Bundle extras) {
super(uri, userId);
mMethod = method;
mArg = arg;
- if (values != null) {
- mExtras = new Bundle();
- for (String key : values.keySet()) {
- final Object val = values.get(key);
- if (val instanceof String) {
- mExtras.putString(key, (String) val);
- } else if (val instanceof Float) {
- mExtras.putFloat(key, (Float) val);
- } else if (val instanceof Double) {
- mExtras.putDouble(key, (Double) val);
- } else if (val instanceof Boolean) {
- mExtras.putBoolean(key, (Boolean) val);
- } else if (val instanceof Integer) {
- mExtras.putInt(key, (Integer) val);
- } else if (val instanceof Long) {
- mExtras.putLong(key, (Long) val);
- }
- }
- }
+ mExtras = extras;
}
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Bundle result = provider.call(null, mUri.getAuthority(), mMethod, mArg, mExtras);
+ Bundle result = provider.call(null, null, mUri.getAuthority(), mMethod, mArg, mExtras);
if (result != null) {
result.size(); // unpack
}
@@ -584,7 +620,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "r", null, null)) {
FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
}
}
@@ -597,27 +633,26 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
- try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
+ try (ParcelFileDescriptor fd = provider.openFile(null, null, mUri, "w", null, null)) {
FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
}
}
}
- private static class QueryCommand extends DeleteCommand {
+ private static class QueryCommand extends Command {
final String[] mProjection;
- final String mSortOrder;
+ final Bundle mExtras;
- public QueryCommand(
- Uri uri, int userId, String[] projection, String where, String sortOrder) {
- super(uri, userId, where);
+ public QueryCommand(Uri uri, int userId, String[] projection, Bundle extras) {
+ super(uri, userId);
mProjection = projection;
- mSortOrder = sortOrder;
+ mExtras = extras;
}
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection,
- ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null);
+ Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection,
+ mExtras, null);
if (cursor == null) {
System.out.println("No result found.");
return;
@@ -669,17 +704,19 @@
}
}
- private static class UpdateCommand extends InsertCommand {
- final String mWhere;
+ private static class UpdateCommand extends Command {
+ final ContentValues mValues;
+ final Bundle mExtras;
- public UpdateCommand(Uri uri, int userId, ContentValues contentValues, String where) {
- super(uri, userId, contentValues);
- mWhere = where;
+ public UpdateCommand(Uri uri, int userId, ContentValues values, Bundle extras) {
+ super(uri, userId);
+ mValues = values;
+ mExtras = extras;
}
@Override
public void onExecute(IContentProvider provider) throws Exception {
- provider.update(resolveCallingPackage(), mUri, mContentValues, mWhere, null);
+ provider.update(resolveCallingPackage(), null, mUri, mValues, mExtras);
}
}
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 6c6797a..d0c2a24 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -48,8 +48,8 @@
private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
- private static final String COMMAND_GRANT_PO_DEVICE_ID_ACCESS =
- "grant-profile-owner-device-ids-access";
+ private static final String COMMAND_MARK_PO_ON_ORG_OWNED_DEVICE =
+ "mark-profile-owner-on-organization-owned-device";
private IDevicePolicyManager mDevicePolicyManager;
private int mUserId = UserHandle.USER_SYSTEM;
@@ -93,7 +93,7 @@
"dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
"the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed."
+ "\n"
- + "usage: dpm " + COMMAND_GRANT_PO_DEVICE_ID_ACCESS + ": "
+ + "usage: dpm " + COMMAND_MARK_PO_ON_ORG_OWNED_DEVICE + ": "
+ "[ --user <USER_ID> | current ] <COMPONENT>\n");
}
@@ -129,8 +129,8 @@
case COMMAND_FORCE_SECURITY_LOGS:
runForceSecurityLogs();
break;
- case COMMAND_GRANT_PO_DEVICE_ID_ACCESS:
- runGrantProfileOwnerDeviceIdsAccess();
+ case COMMAND_MARK_PO_ON_ORG_OWNED_DEVICE:
+ runMarkProfileOwnerOnOrganizationOwnedDevice();
break;
default:
throw new IllegalArgumentException ("unknown command '" + command + "'");
@@ -251,9 +251,9 @@
}
- private void runGrantProfileOwnerDeviceIdsAccess() throws RemoteException {
+ private void runMarkProfileOwnerOnOrganizationOwnedDevice() throws RemoteException {
parseArgs(/*canHaveName=*/ false);
- mDevicePolicyManager.grantDeviceIdsAccessToProfileOwner(mComponent, mUserId);
+ mDevicePolicyManager.markProfileOwnerOnOrganizationOwnedDevice(mComponent, mUserId);
System.out.println("Success");
}
diff --git a/cmds/hid/README.md b/cmds/hid/README.md
index 7e22d08..620336f 100644
--- a/cmds/hid/README.md
+++ b/cmds/hid/README.md
@@ -38,17 +38,21 @@
Register a new uhid device
| Field | Type | Description |
-|:-------------:|:-------------:|:--------------------------|
+|:-------------:|:-------------:|:-------------------------- |
| id | integer | Device id |
| command | string | Must be set to "register" |
| name | string | Device name |
| vid | 16-bit integer| Vendor id |
| pid | 16-bit integer| Product id |
+| bus | string | Bus that device should use |
| descriptor | byte array | USB HID report descriptor |
Device ID is used for matching the subsequent commands to a specific device
to avoid ambiguity when multiple devices are registered.
+Device bus is used to determine how the uhid device is connected to the host.
+The options are "usb" and "bluetooth".
+
USB HID report descriptor should be generated according the the USB HID spec
and can be checked by reverse parsing using a variety of tools, for example
[usbdescreqparser][5].
@@ -61,6 +65,7 @@
"name": "Odie (Test)",
"vid": 0x18d1,
"pid": 0x2c40,
+ "bus": "usb",
"descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x09, 0x0a, 0x01, 0x00,
0x0a, 0x02, 0x00, 0x0a, 0x04, 0x00, 0x0a, 0x05, 0x00, 0x0a, 0x07, 0x00, 0x0a, 0x08, 0x00,
0x0a, 0x0e, 0x00, 0x0a, 0x0f, 0x00, 0x0a, 0x0d, 0x00, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x0a,
@@ -142,4 +147,4 @@
[3]: ../../../../cts/tests/tests/hardware/res/raw/
[4]: https://developer.android.com/training/game-controllers/controller-input.html#button
[5]: http://eleccelerator.com/usbdescreqparser/
-[6]: https://developer.android.com/training/game-controllers/controller-input.html
\ No newline at end of file
+[6]: https://developer.android.com/training/game-controllers/controller-input.html
diff --git a/cmds/hid/jni/Android.bp b/cmds/hid/jni/Android.bp
index 095cfc6..2c07de0 100644
--- a/cmds/hid/jni/Android.bp
+++ b/cmds/hid/jni/Android.bp
@@ -5,6 +5,7 @@
shared_libs: [
"libandroid",
+ "libbase",
"liblog",
"libnativehelper",
],
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index d4fdf85..1e200c5 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -21,17 +21,21 @@
#include <linux/uhid.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <memory>
-#include <unistd.h>
+#include <android/log.h>
+#include <android/looper.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
-#include <android/looper.h>
-#include <android/log.h>
+
+#include <android-base/stringprintf.h>
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
@@ -46,6 +50,7 @@
static struct {
jmethodID onDeviceOpen;
jmethodID onDeviceGetReport;
+ jmethodID onDeviceOutput;
jmethodID onDeviceError;
} gDeviceCallbackClassInfo;
@@ -61,6 +66,26 @@
}
}
+static ScopedLocalRef<jbyteArray> toJbyteArray(JNIEnv* env, const std::vector<uint8_t>& vector) {
+ ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(vector.size()));
+ if (array.get() == nullptr) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
+ return array;
+ }
+ static_assert(sizeof(char) == sizeof(uint8_t));
+ env->SetByteArrayRegion(array.get(), 0, vector.size(),
+ reinterpret_cast<const signed char*>(vector.data()));
+ return array;
+}
+
+static std::string toString(const std::vector<uint8_t>& data) {
+ std::string s = "";
+ for (uint8_t b : data) {
+ s += android::base::StringPrintf("%x ", b);
+ }
+ return s;
+}
+
DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback) :
mCallbackObject(env->NewGlobalRef(callback)) {
env->GetJavaVM(&mJavaVM);
@@ -90,23 +115,30 @@
checkAndClearException(env, "onDeviceGetReport");
}
+void DeviceCallback::onDeviceOutput(const std::vector<uint8_t>& data) {
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput,
+ toJbyteArray(env, data).get());
+ checkAndClearException(env, "onDeviceOutput");
+}
+
JNIEnv* DeviceCallback::getJNIEnv() {
JNIEnv* env;
mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
return env;
}
-Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
- std::vector<uint8_t> descriptor, std::unique_ptr<DeviceCallback> callback) {
-
+std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
+ uint16_t bus, const std::vector<uint8_t>& descriptor,
+ std::unique_ptr<DeviceCallback> callback) {
size_t size = descriptor.size();
if (size > HID_MAX_DESCRIPTOR_SIZE) {
LOGE("Received invalid hid report with descriptor size %zu, skipping", size);
return nullptr;
}
- int fd = ::open(UHID_PATH, O_RDWR | O_CLOEXEC);
- if (fd < 0) {
+ android::base::unique_fd fd(::open(UHID_PATH, O_RDWR | O_CLOEXEC));
+ if (!fd.ok()) {
LOGE("Failed to open uhid: %s", strerror(errno));
return nullptr;
}
@@ -114,10 +146,11 @@
struct uhid_event ev = {};
ev.type = UHID_CREATE2;
strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
- memcpy(&ev.u.create2.rd_data, descriptor.data(),
- size * sizeof(ev.u.create2.rd_data[0]));
+ std::string uniq = android::base::StringPrintf("Id: %d", id);
+ strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq.c_str(), sizeof(ev.u.create2.uniq));
+ memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0]));
ev.u.create2.rd_size = size;
- ev.u.create2.bus = BUS_BLUETOOTH;
+ ev.u.create2.bus = bus;
ev.u.create2.vendor = vid;
ev.u.create2.product = pid;
ev.u.create2.version = 0;
@@ -126,7 +159,6 @@
errno = 0;
ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
if (ret < 0 || ret != sizeof(ev)) {
- ::close(fd);
LOGE("Failed to create uhid node: %s", strerror(errno));
return nullptr;
}
@@ -134,21 +166,21 @@
// Wait for the device to actually be created.
ret = TEMP_FAILURE_RETRY(::read(fd, &ev, sizeof(ev)));
if (ret < 0 || ev.type != UHID_START) {
- ::close(fd);
LOGE("uhid node failed to start: %s", strerror(errno));
return nullptr;
}
- return new Device(id, fd, std::move(callback));
+ // using 'new' to access non-public constructor
+ return std::unique_ptr<Device>(new Device(id, std::move(fd), std::move(callback)));
}
-Device::Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback) :
- mId(id), mFd(fd), mDeviceCallback(std::move(callback)) {
+Device::Device(int32_t id, android::base::unique_fd fd, std::unique_ptr<DeviceCallback> callback)
+ : mId(id), mFd(std::move(fd)), mDeviceCallback(std::move(callback)) {
ALooper* aLooper = ALooper_forThread();
if (aLooper == NULL) {
LOGE("Could not get ALooper, ALooper_forThread returned NULL");
aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
}
- ALooper_addFd(aLooper, fd, 0, ALOOPER_EVENT_INPUT, handleLooperEvents,
+ ALooper_addFd(aLooper, mFd, 0, ALOOPER_EVENT_INPUT, handleLooperEvents,
reinterpret_cast<void*>(this));
}
@@ -162,8 +194,14 @@
struct uhid_event ev = {};
ev.type = UHID_DESTROY;
TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
- ::close(mFd);
- mFd = -1;
+}
+
+// Send event over the fd.
+static void writeEvent(int fd, struct uhid_event& ev, const char* messageType) {
+ ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
+ if (ret < 0 || ret != sizeof(ev)) {
+ LOGE("Failed to send uhid_event %s: %s", messageType, strerror(errno));
+ }
}
void Device::sendReport(const std::vector<uint8_t>& report) const {
@@ -176,10 +214,7 @@
ev.type = UHID_INPUT2;
ev.u.input2.size = report.size();
memcpy(&ev.u.input2.data, report.data(), report.size() * sizeof(ev.u.input2.data[0]));
- ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
- if (ret < 0 || ret != sizeof(ev)) {
- LOGE("Failed to send hid event: %s", strerror(errno));
- }
+ writeEvent(mFd, ev, "UHID_INPUT2");
}
void Device::sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& report) const {
@@ -190,10 +225,7 @@
ev.u.get_report_reply.size = report.size();
memcpy(&ev.u.get_report_reply.data, report.data(),
report.size() * sizeof(ev.u.get_report_reply.data[0]));
- ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
- if (ret < 0 || ret != sizeof(ev)) {
- LOGE("Failed to send hid event (UHID_GET_REPORT_REPLY): %s", strerror(errno));
- }
+ writeEvent(mFd, ev, "UHID_GET_REPORT_REPLY");
}
int Device::handleEvents(int events) {
@@ -210,13 +242,37 @@
return 0;
}
- if (ev.type == UHID_OPEN) {
- mDeviceCallback->onDeviceOpen();
- } else if (ev.type == UHID_GET_REPORT) {
- mDeviceCallback->onDeviceGetReport(ev.u.get_report.id, ev.u.get_report.rnum);
- } else if (ev.type == UHID_SET_REPORT) {
- LOGE("UHID_SET_REPORT is currently not supported");
- return 0;
+ switch (ev.type) {
+ case UHID_OPEN: {
+ mDeviceCallback->onDeviceOpen();
+ break;
+ }
+ case UHID_GET_REPORT: {
+ mDeviceCallback->onDeviceGetReport(ev.u.get_report.id, ev.u.get_report.rnum);
+ break;
+ }
+ case UHID_SET_REPORT: {
+ const struct uhid_set_report_req& set_report = ev.u.set_report;
+ if (set_report.size > UHID_DATA_MAX) {
+ LOGE("SET_REPORT contains too much data: size = %" PRIu16, set_report.size);
+ return 0;
+ }
+
+ std::vector<uint8_t> data(set_report.data, set_report.data + set_report.size);
+ LOGI("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
+ set_report.rnum, toString(data).c_str());
+ break;
+ }
+ case UHID_OUTPUT: {
+ struct uhid_output_req& output = ev.u.output;
+ std::vector<uint8_t> data(output.data, output.data + output.size);
+ mDeviceCallback->onDeviceOutput(data);
+ break;
+ }
+ default: {
+ LOGI("Unhandled event type: %" PRIu32, ev.type);
+ break;
+ }
}
return 1;
@@ -239,8 +295,8 @@
return data;
}
-static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, jint pid,
- jbyteArray rawDescriptor, jobject callback) {
+static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
+ jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) {
ScopedUtfChars name(env, rawName);
if (name.c_str() == nullptr) {
return 0;
@@ -250,9 +306,10 @@
std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
- uhid::Device* d = uhid::Device::open(
- id, reinterpret_cast<const char*>(name.c_str()), vid, pid, desc, std::move(cb));
- return reinterpret_cast<jlong>(d);
+ std::unique_ptr<uhid::Device> d =
+ uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()), vid, pid, bus, desc,
+ std::move(cb));
+ return reinterpret_cast<jlong>(d.release());
}
static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray rawReport) {
@@ -284,14 +341,14 @@
}
static JNINativeMethod sMethods[] = {
- { "nativeOpenDevice",
- "(Ljava/lang/String;III[B"
- "Lcom/android/commands/hid/Device$DeviceCallback;)J",
- reinterpret_cast<void*>(openDevice) },
- { "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) },
- { "nativeSendGetFeatureReportReply", "(JI[B)V",
- reinterpret_cast<void*>(sendGetFeatureReportReply) },
- { "nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice) },
+ {"nativeOpenDevice",
+ "(Ljava/lang/String;IIII[B"
+ "Lcom/android/commands/hid/Device$DeviceCallback;)J",
+ reinterpret_cast<void*>(openDevice)},
+ {"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)},
+ {"nativeSendGetFeatureReportReply", "(JI[B)V",
+ reinterpret_cast<void*>(sendGetFeatureReportReply)},
+ {"nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice)},
};
int register_com_android_commands_hid_Device(JNIEnv* env) {
@@ -304,6 +361,8 @@
env->GetMethodID(clazz, "onDeviceOpen", "()V");
uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
+ uhid::gDeviceCallbackClassInfo.onDeviceOutput =
+ env->GetMethodID(clazz, "onDeviceOutput", "([B)V");
uhid::gDeviceCallbackClassInfo.onDeviceError =
env->GetMethodID(clazz, "onDeviceError", "()V");
if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 892c7cd..7202b45 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -19,6 +19,8 @@
#include <jni.h>
+#include <android-base/unique_fd.h>
+
namespace android {
namespace uhid {
@@ -29,6 +31,7 @@
void onDeviceOpen();
void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
+ void onDeviceOutput(const std::vector<uint8_t>& data);
void onDeviceError();
private:
@@ -39,10 +42,10 @@
class Device {
public:
- static Device* open(int32_t id, const char* name, int32_t vid, int32_t pid,
- std::vector<uint8_t> descriptor, std::unique_ptr<DeviceCallback> callback);
+ static std::unique_ptr<Device> open(int32_t id, const char* name, int32_t vid, int32_t pid,
+ uint16_t bus, const std::vector<uint8_t>& descriptor,
+ std::unique_ptr<DeviceCallback> callback);
- Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback);
~Device();
void sendReport(const std::vector<uint8_t>& report) const;
@@ -52,8 +55,9 @@
int handleEvents(int events);
private:
+ Device(int32_t id, android::base::unique_fd fd, std::unique_ptr<DeviceCallback> callback);
int32_t mId;
- int mFd;
+ android::base::unique_fd mFd;
std::unique_ptr<DeviceCallback> mDeviceCallback;
};
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 616d411..dade415 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -20,13 +20,16 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.MessageQueue;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.os.SomeArgs;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Map;
+
public class Device {
private static final String TAG = "HidDevice";
@@ -40,6 +43,7 @@
private final DeviceHandler mHandler;
// mFeatureReports is limited to 256 entries, because the report number is 8-bit
private final SparseArray<byte[]> mFeatureReports;
+ private final Map<ByteBuffer, byte[]> mOutputs;
private long mTimeToSend;
private final Object mCond = new Object();
@@ -48,23 +52,25 @@
System.loadLibrary("hidcommand_jni");
}
- private static native long nativeOpenDevice(String name, int id, int vid, int pid,
+ private static native long nativeOpenDevice(String name, int id, int vid, int pid, int bus,
byte[] descriptor, DeviceCallback callback);
private static native void nativeSendReport(long ptr, byte[] data);
private static native void nativeSendGetFeatureReportReply(long ptr, int id, byte[] data);
private static native void nativeCloseDevice(long ptr);
- public Device(int id, String name, int vid, int pid, byte[] descriptor,
- byte[] report, SparseArray<byte[]> featureReports) {
+ public Device(int id, String name, int vid, int pid, int bus, byte[] descriptor,
+ byte[] report, SparseArray<byte[]> featureReports, Map<ByteBuffer, byte[]> outputs) {
mId = id;
mThread = new HandlerThread("HidDeviceHandler");
mThread.start();
mHandler = new DeviceHandler(mThread.getLooper());
mFeatureReports = featureReports;
+ mOutputs = outputs;
SomeArgs args = SomeArgs.obtain();
args.argi1 = id;
args.argi2 = vid;
args.argi3 = pid;
+ args.argi4 = bus;
if (name != null) {
args.arg1 = name;
} else {
@@ -110,7 +116,7 @@
case MSG_OPEN_DEVICE:
SomeArgs args = (SomeArgs) msg.obj;
mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3,
- (byte[]) args.arg2, new DeviceCallback());
+ args.argi4, (byte[]) args.arg2, new DeviceCallback());
pauseEvents();
break;
case MSG_SEND_REPORT:
@@ -160,6 +166,11 @@
}
public void onDeviceGetReport(int requestId, int reportId) {
+ if (mFeatureReports == null) {
+ Log.e(TAG, "Received GET_REPORT request for reportId=" + reportId
+ + ", but 'feature_reports' section is not found");
+ return;
+ }
byte[] report = mFeatureReports.get(reportId);
if (report == null) {
@@ -176,6 +187,29 @@
mHandler.sendMessageAtTime(msg, mTimeToSend);
}
+ // native callback
+ public void onDeviceOutput(byte[] data) {
+ if (mOutputs == null) {
+ Log.e(TAG, "Received OUTPUT request, but 'outputs' section is not found");
+ return;
+ }
+ byte[] response = mOutputs.get(ByteBuffer.wrap(data));
+ if (response == null) {
+ Log.i(TAG,
+ "Requested response for output " + Arrays.toString(data) + " is not found");
+ return;
+ }
+
+ Message msg;
+ msg = mHandler.obtainMessage(MSG_SEND_REPORT, response);
+
+ // Message is set to asynchronous so it won't be blocked by synchronization
+ // barrier during UHID_OPEN. This is necessary for drivers that do
+ // UHID_OUTPUT requests during probe, and expect a response right away.
+ msg.setAsynchronous(true);
+ mHandler.sendMessageAtTime(msg, mTimeToSend);
+ }
+
public void onDeviceError() {
Log.e(TAG, "Device error occurred, closing /dev/uhid");
Message msg = mHandler.obtainMessage(MSG_CLOSE_DEVICE);
diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java
index 746e372..d4bf1d8 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -21,10 +21,13 @@
import android.util.Log;
import android.util.SparseArray;
-import java.io.InputStreamReader;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
public class Event {
private static final String TAG = "HidEvent";
@@ -33,14 +36,31 @@
public static final String COMMAND_DELAY = "delay";
public static final String COMMAND_REPORT = "report";
+ // These constants come from "include/uapi/linux/input.h" in the kernel
+ enum Bus {
+ USB(0x03), BLUETOOTH(0x05);
+
+ Bus(int value) {
+ mValue = value;
+ }
+
+ int getValue() {
+ return mValue;
+ }
+
+ private int mValue;
+ }
+
private int mId;
private String mCommand;
private String mName;
private byte[] mDescriptor;
private int mVid;
private int mPid;
+ private Bus mBus;
private byte[] mReport;
private SparseArray<byte[]> mFeatureReports;
+ private Map<ByteBuffer, byte[]> mOutputs;
private int mDuration;
public int getId() {
@@ -67,6 +87,10 @@
return mPid;
}
+ public int getBus() {
+ return mBus.getValue();
+ }
+
public byte[] getReport() {
return mReport;
}
@@ -75,6 +99,10 @@
return mFeatureReports;
}
+ public Map<ByteBuffer, byte[]> getOutputs() {
+ return mOutputs;
+ }
+
public int getDuration() {
return mDuration;
}
@@ -86,8 +114,10 @@
+ ", descriptor=" + Arrays.toString(mDescriptor)
+ ", vid=" + mVid
+ ", pid=" + mPid
+ + ", bus=" + mBus
+ ", report=" + Arrays.toString(mReport)
+ ", feature_reports=" + mFeatureReports.toString()
+ + ", outputs=" + mOutputs.toString()
+ ", duration=" + mDuration
+ "}";
}
@@ -123,6 +153,10 @@
mEvent.mFeatureReports = reports;
}
+ public void setOutputs(Map<ByteBuffer, byte[]> outputs) {
+ mEvent.mOutputs = outputs;
+ }
+
public void setVid(int vid) {
mEvent.mVid = vid;
}
@@ -131,6 +165,10 @@
mEvent.mPid = pid;
}
+ public void setBus(Bus bus) {
+ mEvent.mBus = bus;
+ }
+
public void setDuration(int duration) {
mEvent.mDuration = duration;
}
@@ -193,12 +231,18 @@
case "pid":
eb.setPid(readInt());
break;
+ case "bus":
+ eb.setBus(readBus());
+ break;
case "report":
eb.setReport(readData());
break;
case "feature_reports":
eb.setFeatureReports(readFeatureReports());
break;
+ case "outputs":
+ eb.setOutputs(readOutputs());
+ break;
case "duration":
eb.setDuration(readInt());
break;
@@ -248,9 +292,14 @@
return Integer.decode(val);
}
+ private Bus readBus() throws IOException {
+ String val = mReader.nextString();
+ return Bus.valueOf(val.toUpperCase());
+ }
+
private SparseArray<byte[]> readFeatureReports()
throws IllegalStateException, IOException {
- SparseArray<byte[]> featureReports = new SparseArray();
+ SparseArray<byte[]> featureReports = new SparseArray<>();
try {
mReader.beginArray();
while (mReader.hasNext()) {
@@ -276,17 +325,60 @@
}
}
mReader.endObject();
- if (data != null)
+ if (data != null) {
featureReports.put(id, data);
+ }
}
mReader.endArray();
- } catch (IllegalStateException|NumberFormatException e) {
+ } catch (IllegalStateException | NumberFormatException e) {
consumeRemainingElements();
mReader.endArray();
throw new IllegalStateException("Encountered malformed data.", e);
- } finally {
- return featureReports;
}
+ return featureReports;
+ }
+
+ private Map<ByteBuffer, byte[]> readOutputs()
+ throws IllegalStateException, IOException {
+ Map<ByteBuffer, byte[]> outputs = new HashMap<>();
+
+ try {
+ mReader.beginArray();
+ while (mReader.hasNext()) {
+ byte[] output = null;
+ byte[] response = null;
+ mReader.beginObject();
+ while (mReader.hasNext()) {
+ String name = mReader.nextName();
+ switch (name) {
+ case "description":
+ // Description is only used to keep track of the output responses
+ mReader.nextString();
+ break;
+ case "output":
+ output = readData();
+ break;
+ case "response":
+ response = readData();
+ break;
+ default:
+ consumeRemainingElements();
+ mReader.endObject();
+ throw new IllegalStateException("Invalid key in outputs: " + name);
+ }
+ }
+ mReader.endObject();
+ if (output != null) {
+ outputs.put(ByteBuffer.wrap(output), response);
+ }
+ }
+ mReader.endArray();
+ } catch (IllegalStateException | NumberFormatException e) {
+ consumeRemainingElements();
+ mReader.endArray();
+ throw new IllegalStateException("Encountered malformed data.", e);
+ }
+ return outputs;
}
private void consumeRemainingElements() throws IOException {
@@ -296,10 +388,6 @@
}
}
- private static void error(String msg) {
- error(msg, null);
- }
-
private static void error(String msg, Exception e) {
System.out.println(msg);
Log.e(TAG, msg);
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index 54ac1b0..fac0ab2 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -16,22 +16,17 @@
package com.android.commands.hid;
-import android.util.JsonReader;
-import android.util.JsonToken;
import android.util.Log;
import android.util.SparseArray;
import libcore.io.IoUtils;
-import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.IOException;
import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
public class Hid {
private static final String TAG = "HID";
@@ -118,8 +113,8 @@
"Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
}
int id = e.getId();
- Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(),
- e.getDescriptor(), e.getReport(), e.getFeatureReports());
+ Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
+ e.getDescriptor(), e.getReport(), e.getFeatureReports(), e.getOutputs());
mDevices.append(id, d);
}
diff --git a/cmds/idmap/Android.bp b/cmds/idmap/Android.bp
deleted file mode 100644
index ae5d74a..0000000
--- a/cmds/idmap/Android.bp
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2012 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.
-
-cc_binary {
- name: "idmap",
-
- srcs: [
- "idmap.cpp",
- "create.cpp",
- "scan.cpp",
- "inspect.cpp",
- ],
-
- shared_libs: [
- "liblog",
- "libutils",
- "libandroidfw",
- "libcutils",
- ],
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wunused",
- "-Wunreachable-code",
- ],
-}
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
deleted file mode 100644
index f415f8f..0000000
--- a/cmds/idmap/create.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-#include "idmap.h"
-
-#include <memory>
-#include <androidfw/AssetManager.h>
-#include <androidfw/ResourceTypes.h>
-#include <androidfw/ZipFileRO.h>
-#include <utils/String8.h>
-
-#include <fcntl.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-
-using namespace android;
-
-namespace {
- int get_zip_entry_crc(const char *zip_path, const char *entry_name, uint32_t *crc)
- {
- std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(zip_path));
- if (zip.get() == NULL) {
- return -1;
- }
- ZipEntryRO entry = zip->findEntryByName(entry_name);
- if (entry == NULL) {
- return -1;
- }
- if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, crc)) {
- return -1;
- }
- zip->releaseEntry(entry);
- return 0;
- }
-
- int open_idmap(const char *path)
- {
- int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644));
- if (fd == -1) {
- ALOGD("error: open %s: %s\n", path, strerror(errno));
- goto fail;
- }
- if (fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
- ALOGD("error: fchmod %s: %s\n", path, strerror(errno));
- goto fail;
- }
- if (TEMP_FAILURE_RETRY(flock(fd, LOCK_EX)) != 0) {
- ALOGD("error: flock %s: %s\n", path, strerror(errno));
- goto fail;
- }
-
- return fd;
-fail:
- if (fd != -1) {
- close(fd);
- unlink(path);
- }
- return -1;
- }
-
- int write_idmap(int fd, const uint32_t *data, size_t size)
- {
- if (lseek(fd, 0, SEEK_SET) < 0) {
- return -1;
- }
- size_t bytesLeft = size;
- while (bytesLeft > 0) {
- ssize_t w = TEMP_FAILURE_RETRY(write(fd, data + size - bytesLeft, bytesLeft));
- if (w < 0) {
- fprintf(stderr, "error: write: %s\n", strerror(errno));
- return -1;
- }
- bytesLeft -= static_cast<size_t>(w);
- }
- return 0;
- }
-
- bool is_idmap_stale_fd(const char *target_apk_path, const char *overlay_apk_path, int idmap_fd)
- {
- static const size_t N = ResTable::IDMAP_HEADER_SIZE_BYTES;
- struct stat st;
- if (fstat(idmap_fd, &st) == -1) {
- return true;
- }
- if (st.st_size < static_cast<off_t>(N)) {
- // file is empty or corrupt
- return true;
- }
-
- char buf[N];
- size_t bytesLeft = N;
- if (lseek(idmap_fd, 0, SEEK_SET) < 0) {
- return true;
- }
- for (;;) {
- ssize_t r = TEMP_FAILURE_RETRY(read(idmap_fd, buf + N - bytesLeft, bytesLeft));
- if (r < 0) {
- return true;
- }
- bytesLeft -= static_cast<size_t>(r);
- if (bytesLeft == 0) {
- break;
- }
- if (r == 0) {
- // "shouldn't happen"
- return true;
- }
- }
-
- uint32_t version, cached_target_crc, cached_overlay_crc;
- String8 cached_target_path, cached_overlay_path;
- if (!ResTable::getIdmapInfo(buf, N, &version, &cached_target_crc, &cached_overlay_crc,
- &cached_target_path, &cached_overlay_path)) {
- return true;
- }
-
- if (version != ResTable::IDMAP_CURRENT_VERSION) {
- return true;
- }
-
- if (cached_target_path != target_apk_path) {
- return true;
- }
- if (cached_overlay_path != overlay_apk_path) {
- return true;
- }
-
- uint32_t actual_target_crc, actual_overlay_crc;
- if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
- &actual_target_crc) == -1) {
- return true;
- }
- if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
- &actual_overlay_crc) == -1) {
- return true;
- }
-
- return cached_target_crc != actual_target_crc || cached_overlay_crc != actual_overlay_crc;
- }
-
- bool is_idmap_stale_path(const char *target_apk_path, const char *overlay_apk_path,
- const char *idmap_path)
- {
- struct stat st;
- if (stat(idmap_path, &st) == -1) {
- // non-existing idmap is always stale; on other errors, abort idmap generation
- return errno == ENOENT;
- }
-
- int idmap_fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY));
- if (idmap_fd == -1) {
- return false;
- }
- bool is_stale = is_idmap_stale_fd(target_apk_path, overlay_apk_path, idmap_fd);
- close(idmap_fd);
- return is_stale;
- }
-
- int create_idmap(const char *target_apk_path, const char *overlay_apk_path,
- uint32_t **data, size_t *size)
- {
- uint32_t target_crc, overlay_crc;
- if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
- &target_crc) == -1) {
- return -1;
- }
- if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
- &overlay_crc) == -1) {
- return -1;
- }
-
- AssetManager am;
- bool b = am.createIdmap(target_apk_path, overlay_apk_path, target_crc, overlay_crc,
- data, size);
- return b ? 0 : -1;
- }
-
- int create_and_write_idmap(const char *target_apk_path, const char *overlay_apk_path,
- int fd, bool check_if_stale)
- {
- if (check_if_stale) {
- if (!is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd)) {
- // already up to date -- nothing to do
- return 0;
- }
- }
-
- uint32_t *data = NULL;
- size_t size;
-
- if (create_idmap(target_apk_path, overlay_apk_path, &data, &size) == -1) {
- return -1;
- }
-
- if (write_idmap(fd, data, size) == -1) {
- free(data);
- return -1;
- }
-
- free(data);
- return 0;
- }
-}
-
-int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
- const char *idmap_path)
-{
- if (!is_idmap_stale_path(target_apk_path, overlay_apk_path, idmap_path)) {
- // already up to date -- nothing to do
- return EXIT_SUCCESS;
- }
-
- int fd = open_idmap(idmap_path);
- if (fd == -1) {
- return EXIT_FAILURE;
- }
-
- int r = create_and_write_idmap(target_apk_path, overlay_apk_path, fd, false);
- close(fd);
- if (r != 0) {
- unlink(idmap_path);
- }
- return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-}
-
-int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd)
-{
- return create_and_write_idmap(target_apk_path, overlay_apk_path, fd, true) == 0 ?
- EXIT_SUCCESS : EXIT_FAILURE;
-}
-
-int idmap_verify_fd(const char *target_apk_path, const char *overlay_apk_path, int fd)
-{
- return !is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd) ?
- EXIT_SUCCESS : EXIT_FAILURE;
-}
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
deleted file mode 100644
index 8f86ed8..0000000
--- a/cmds/idmap/idmap.cpp
+++ /dev/null
@@ -1,283 +0,0 @@
-#include "idmap.h"
-
-#include <private/android_filesystem_config.h> // for AID_SYSTEM
-
-#include <stdlib.h>
-#include <string.h>
-
-namespace {
- const char *usage = "NAME\n\
- idmap - create or display idmap files\n\
-\n\
-SYNOPSIS \n\
- idmap --help \n\
- idmap --fd target overlay fd \n\
- idmap --path target overlay idmap \n\
- idmap --scan target-package-name-to-look-for path-to-target-apk dir-to-hold-idmaps \\\
- dir-to-scan [additional-dir-to-scan [additional-dir-to-scan [...]]]\n\
- idmap --inspect idmap \n\
- idmap --verify target overlay fd \n\
-\n\
-DESCRIPTION \n\
- Idmap files play an integral part in the runtime resource overlay framework. An idmap \n\
- file contains a mapping of resource identifiers between overlay package and its target \n\
- package; this mapping is used during resource lookup. Idmap files also act as control \n\
- files by their existence: if not present, the corresponding overlay package is ignored \n\
- when the resource context is created. \n\
-\n\
- Idmap files are stored in /data/resource-cache. For each pair (target package, overlay \n\
- package), there exists exactly one idmap file, or none if the overlay should not be used. \n\
-\n\
-NOMENCLATURE \n\
- target: the original, non-overlay, package. Each target package may be associated with \n\
- any number of overlay packages. \n\
-\n\
- overlay: an overlay package. Each overlay package is associated with exactly one target \n\
- package, specified in the overlay's manifest using the <overlay target=\"...\"/> \n\
- tag. \n\
-\n\
-OPTIONS \n\
- --help: display this help \n\
-\n\
- --fd: create idmap for target package 'target' (path to apk) and overlay package 'overlay' \n\
- (path to apk); write results to file descriptor 'fd' (integer). This invocation \n\
- version is intended to be used by a parent process with higher privileges to call \n\
- idmap in a controlled way: the parent will open a suitable file descriptor, fork, \n\
- drop its privileges and exec. This tool will continue execution without the extra \n\
- privileges, but still have write access to a file it could not have opened on its \n\
- own. \n\
-\n\
- --path: create idmap for target package 'target' (path to apk) and overlay package \n\
- 'overlay' (path to apk); write results to 'idmap' (path). \n\
-\n\
- --scan: non-recursively search directory 'dir-to-scan' (path) for static overlay packages \n\
- with target package 'target-package-name-to-look-for' (package name) present at\n\
- 'path-to-target-apk' (path to apk). For each overlay package found, create an\n\
- idmap file in 'dir-to-hold-idmaps' (path). \n\
-\n\
- --inspect: decode the binary format of 'idmap' (path) and display the contents in a \n\
- debug-friendly format. \n\
-\n\
- --verify: verify if idmap corresponding to file descriptor 'fd' (integer) is made from \n\
- target package 'target' (path to apk) and overlay package 'overlay'. \n\
-\n\
-EXAMPLES \n\
- Create an idmap file: \n\
-\n\
- $ adb shell idmap --path /system/app/target.apk \\ \n\
- /vendor/overlay/overlay.apk \\ \n\
- /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\
-\n\
- Display an idmap file: \n\
-\n\
- $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\
- SECTION ENTRY VALUE COMMENT \n\
- IDMAP HEADER magic 0x706d6469 \n\
- base crc 0xb65a383f \n\
- overlay crc 0x7b9675e8 \n\
- base path .......... /path/to/target.apk \n\
- overlay path .......... /path/to/overlay.apk \n\
- DATA HEADER target pkg 0x0000007f \n\
- types count 0x00000003 \n\
- DATA BLOCK target type 0x00000002 \n\
- overlay type 0x00000002 \n\
- entry count 0x00000001 \n\
- entry offset 0x00000000 \n\
- entry 0x00000000 drawable/drawable \n\
- DATA BLOCK target type 0x00000003 \n\
- overlay type 0x00000003 \n\
- entry count 0x00000001 \n\
- entry offset 0x00000000 \n\
- entry 0x00000000 xml/integer \n\
- DATA BLOCK target type 0x00000004 \n\
- overlay type 0x00000004 \n\
- entry count 0x00000001 \n\
- entry offset 0x00000000 \n\
- entry 0x00000000 raw/lorem_ipsum \n\
-\n\
- In this example, the overlay package provides three alternative resource values:\n\
- drawable/drawable, xml/integer, and raw/lorem_ipsum \n\
-\n\
-NOTES \n\
- This tool and its expected invocation from installd is modelled on dexopt.";
-
- bool verify_directory_readable(const char *path)
- {
- return access(path, R_OK | X_OK) == 0;
- }
-
- bool verify_directory_writable(const char *path)
- {
- return access(path, W_OK) == 0;
- }
-
- bool verify_file_readable(const char *path)
- {
- return access(path, R_OK) == 0;
- }
-
- bool verify_root_or_system()
- {
- uid_t uid = getuid();
- gid_t gid = getgid();
-
- return (uid == 0 && gid == 0) || (uid == AID_SYSTEM && gid == AID_SYSTEM);
- }
-
- int maybe_create_fd(const char *target_apk_path, const char *overlay_apk_path,
- const char *idmap_str)
- {
- // anyone (not just root or system) may do --fd -- the file has
- // already been opened by someone else on our behalf
-
- char *endptr;
- int idmap_fd = strtol(idmap_str, &endptr, 10);
- if (*endptr != '\0') {
- fprintf(stderr, "error: failed to parse file descriptor argument %s\n", idmap_str);
- return -1;
- }
-
- if (!verify_file_readable(target_apk_path)) {
- ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
- return -1;
- }
-
- if (!verify_file_readable(overlay_apk_path)) {
- ALOGD("error: failed to read apk %s: %s\n", overlay_apk_path, strerror(errno));
- return -1;
- }
-
- return idmap_create_fd(target_apk_path, overlay_apk_path, idmap_fd);
- }
-
- int maybe_create_path(const char *target_apk_path, const char *overlay_apk_path,
- const char *idmap_path)
- {
- if (!verify_root_or_system()) {
- fprintf(stderr, "error: permission denied: not user root or user system\n");
- return -1;
- }
-
- if (!verify_file_readable(target_apk_path)) {
- ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
- return -1;
- }
-
- if (!verify_file_readable(overlay_apk_path)) {
- ALOGD("error: failed to read apk %s: %s\n", overlay_apk_path, strerror(errno));
- return -1;
- }
-
- return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path);
- }
-
- int maybe_verify_fd(const char *target_apk_path, const char *overlay_apk_path,
- const char *idmap_str)
- {
- char *endptr;
- int idmap_fd = strtol(idmap_str, &endptr, 10);
- if (*endptr != '\0') {
- fprintf(stderr, "error: failed to parse file descriptor argument %s\n", idmap_str);
- return -1;
- }
-
- if (!verify_file_readable(target_apk_path)) {
- ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
- return -1;
- }
-
- if (!verify_file_readable(overlay_apk_path)) {
- ALOGD("error: failed to read apk %s: %s\n", overlay_apk_path, strerror(errno));
- return -1;
- }
-
- return idmap_verify_fd(target_apk_path, overlay_apk_path, idmap_fd);
- }
-
- int maybe_scan(const char *target_package_name, const char *target_apk_path,
- const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
- {
- if (!verify_root_or_system()) {
- fprintf(stderr, "error: permission denied: not user root or user system\n");
- return -1;
- }
-
- if (!verify_file_readable(target_apk_path)) {
- ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
- return -1;
- }
-
- if (!verify_directory_writable(idmap_dir)) {
- ALOGD("error: no write access to %s: %s\n", idmap_dir, strerror(errno));
- return -1;
- }
-
- const size_t N = overlay_dirs->size();
- for (size_t i = 0; i < N; i++) {
- const char *dir = overlay_dirs->itemAt(i);
- if (!verify_directory_readable(dir)) {
- ALOGD("error: no read access to %s: %s\n", dir, strerror(errno));
- return -1;
- }
- }
-
- return idmap_scan(target_package_name, target_apk_path, idmap_dir, overlay_dirs);
- }
-
- int maybe_inspect(const char *idmap_path)
- {
- // anyone (not just root or system) may do --inspect
- if (!verify_file_readable(idmap_path)) {
- ALOGD("error: failed to read idmap %s: %s\n", idmap_path, strerror(errno));
- return -1;
- }
- return idmap_inspect(idmap_path);
- }
-}
-
-int main(int argc, char **argv)
-{
-#if 0
- {
- char buf[1024];
- buf[0] = '\0';
- for (int i = 0; i < argc; ++i) {
- strncat(buf, argv[i], sizeof(buf) - 1);
- strncat(buf, " ", sizeof(buf) - 1);
- }
- ALOGD("%s:%d: uid=%d gid=%d argv=%s\n", __FILE__, __LINE__, getuid(), getgid(), buf);
- }
-#endif
-
- if (argc == 2 && !strcmp(argv[1], "--help")) {
- printf("%s\n", usage);
- return 0;
- }
-
- if (argc == 5 && !strcmp(argv[1], "--fd")) {
- return maybe_create_fd(argv[2], argv[3], argv[4]);
- }
-
- if (argc == 5 && !strcmp(argv[1], "--path")) {
- return maybe_create_path(argv[2], argv[3], argv[4]);
- }
-
- if (argc == 5 && !strcmp(argv[1], "--verify")) {
- return maybe_verify_fd(argv[2], argv[3], argv[4]);
- }
-
- if (argc >= 6 && !strcmp(argv[1], "--scan")) {
- android::Vector<const char *> v;
- for (int i = 5; i < argc; i++) {
- v.push(argv[i]);
- }
- return maybe_scan(argv[2], argv[3], argv[4], &v);
- }
-
- if (argc == 3 && !strcmp(argv[1], "--inspect")) {
- return maybe_inspect(argv[2]);
- }
-
- fprintf(stderr, "Usage: don't use this (cf dexopt usage).\n");
- return EXIT_FAILURE;
-}
diff --git a/cmds/idmap/idmap.h b/cmds/idmap/idmap.h
deleted file mode 100644
index 5962108..0000000
--- a/cmds/idmap/idmap.h
+++ /dev/null
@@ -1,38 +0,0 @@
-
-#ifndef _IDMAP_H_
-#define _IDMAP_H_
-
-#define LOG_TAG "idmap"
-
-#include <utils/Log.h>
-#include <utils/Vector.h>
-
-#include <errno.h>
-#include <stdio.h>
-
-#ifndef TEMP_FAILURE_RETRY
-// Used to retry syscalls that can return EINTR.
-#define TEMP_FAILURE_RETRY(exp) ({ \
- typeof (exp) _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; })
-#endif
-
-int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
- const char *idmap_path);
-
-int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);
-
-int idmap_verify_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);
-
-// Regarding target_package_name: the idmap_scan implementation should
-// be able to extract this from the manifest in target_apk_path,
-// simplifying the external API.
-int idmap_scan(const char *target_package_name, const char *target_apk_path,
- const char *idmap_dir, const android::Vector<const char *> *overlay_dirs);
-
-int idmap_inspect(const char *idmap_path);
-
-#endif // _IDMAP_H_
diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp
deleted file mode 100644
index 20005e2..0000000
--- a/cmds/idmap/inspect.cpp
+++ /dev/null
@@ -1,313 +0,0 @@
-#include "idmap.h"
-
-#include <androidfw/AssetManager.h>
-#include <androidfw/ResourceTypes.h>
-#include <utils/ByteOrder.h>
-#include <utils/String8.h>
-
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-using namespace android;
-
-namespace {
- static const uint32_t IDMAP_MAGIC = 0x504D4449;
- static const size_t PATH_LENGTH = 256;
-
- void printe(const char *fmt, ...);
-
- class IdmapBuffer {
- private:
- const char* buf_;
- size_t len_;
- size_t pos_;
- public:
- IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {}
-
- ~IdmapBuffer() {
- if (buf_ != MAP_FAILED) {
- munmap(const_cast<char*>(buf_), len_);
- }
- }
-
- status_t init(const char *idmap_path) {
- struct stat st;
- int fd;
-
- if (stat(idmap_path, &st) < 0) {
- printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno));
- return UNKNOWN_ERROR;
- }
- len_ = st.st_size;
- if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) {
- printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno));
- return UNKNOWN_ERROR;
- }
- if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
- close(fd);
- printe("failed to mmap idmap: %s\n", strerror(errno));
- return UNKNOWN_ERROR;
- }
- close(fd);
- return NO_ERROR;
- }
-
- status_t nextUint32(uint32_t* i) {
- if (!buf_) {
- printe("failed to read next uint32_t: buffer not initialized\n");
- return UNKNOWN_ERROR;
- }
-
- if (pos_ + sizeof(uint32_t) > len_) {
- printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n",
- pos_);
- return UNKNOWN_ERROR;
- }
-
- if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) {
- printe("failed to read next uint32_t: not aligned on 4-byte boundary\n");
- return UNKNOWN_ERROR;
- }
-
- *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_));
- pos_ += sizeof(uint32_t);
- return NO_ERROR;
- }
-
- status_t nextUint16(uint16_t* i) {
- if (!buf_) {
- printe("failed to read next uint16_t: buffer not initialized\n");
- return UNKNOWN_ERROR;
- }
-
- if (pos_ + sizeof(uint16_t) > len_) {
- printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n",
- pos_);
- return UNKNOWN_ERROR;
- }
-
- if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) {
- printe("failed to read next uint32_t: not aligned on 2-byte boundary\n");
- return UNKNOWN_ERROR;
- }
-
- *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_));
- pos_ += sizeof(uint16_t);
- return NO_ERROR;
- }
-
- status_t nextPath(char *b) {
- if (!buf_) {
- printe("failed to read next path: buffer not initialized\n");
- return UNKNOWN_ERROR;
- }
- if (pos_ + PATH_LENGTH > len_) {
- printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_);
- return UNKNOWN_ERROR;
- }
- memcpy(b, buf_ + pos_, PATH_LENGTH);
- pos_ += PATH_LENGTH;
- return NO_ERROR;
- }
- };
-
- void printe(const char *fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
- fprintf(stderr, "error: ");
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- }
-
- void print_header() {
- printf("SECTION ENTRY VALUE COMMENT\n");
- }
-
- void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
- printf("%-12s %-12s 0x%08x ", section, subsection, value);
- vprintf(fmt, ap);
- printf("\n");
- va_end(ap);
- }
-
- void print_path(const char *section, const char *subsection, const char *fmt, ...) {
- va_list ap;
-
- va_start(ap, fmt);
- printf("%-12s %-12s .......... ", section, subsection);
- vprintf(fmt, ap);
- printf("\n");
- va_end(ap);
- }
-
- status_t resource_metadata(const AssetManager& am, uint32_t res_id,
- String8 *package, String8 *type, String8 *name) {
- const ResTable& rt = am.getResources();
- struct ResTable::resource_name data;
- if (!rt.getResourceName(res_id, false, &data)) {
- printe("failed to get resource name id=0x%08x\n", res_id);
- return UNKNOWN_ERROR;
- }
- if (package != NULL) {
- *package = String8(String16(data.package, data.packageLen));
- }
- if (type != NULL) {
- *type = String8(String16(data.type, data.typeLen));
- }
- if (name != NULL) {
- *name = String8(String16(data.name, data.nameLen));
- }
- return NO_ERROR;
- }
-
- status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) {
- uint32_t i;
- char path[PATH_LENGTH];
-
- status_t err = buf.nextUint32(&i);
- if (err != NO_ERROR) {
- return err;
- }
-
- if (i != IDMAP_MAGIC) {
- printe("not an idmap file: actual magic constant 0x%08x does not match expected magic "
- "constant 0x%08x\n", i, IDMAP_MAGIC);
- return UNKNOWN_ERROR;
- }
-
- print_header();
- print("IDMAP HEADER", "magic", i, "");
-
- err = buf.nextUint32(&i);
- if (err != NO_ERROR) {
- return err;
- }
- print("", "version", i, "");
-
- err = buf.nextUint32(&i);
- if (err != NO_ERROR) {
- return err;
- }
- print("", "base crc", i, "");
-
- err = buf.nextUint32(&i);
- if (err != NO_ERROR) {
- return err;
- }
- print("", "overlay crc", i, "");
-
- err = buf.nextPath(path);
- if (err != NO_ERROR) {
- // printe done from IdmapBuffer::nextPath
- return err;
- }
- print_path("", "base path", "%s", path);
-
- if (!am.addAssetPath(String8(path), NULL)) {
- printe("failed to add '%s' as asset path\n", path);
- return UNKNOWN_ERROR;
- }
-
- err = buf.nextPath(path);
- if (err != NO_ERROR) {
- // printe done from IdmapBuffer::nextPath
- return err;
- }
- print_path("", "overlay path", "%s", path);
-
- return NO_ERROR;
- }
-
- status_t parse_data(IdmapBuffer& buf, const AssetManager& am) {
- const uint32_t packageId = am.getResources().getBasePackageId(0);
-
- uint16_t data16;
- status_t err = buf.nextUint16(&data16);
- if (err != NO_ERROR) {
- return err;
- }
- print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), "");
-
- err = buf.nextUint16(&data16);
- if (err != NO_ERROR) {
- return err;
- }
- print("", "types count", static_cast<uint32_t>(data16), "");
-
- uint32_t typeCount = static_cast<uint32_t>(data16);
- while (typeCount > 0) {
- typeCount--;
-
- err = buf.nextUint16(&data16);
- if (err != NO_ERROR) {
- return err;
- }
- const uint32_t targetTypeId = static_cast<uint32_t>(data16);
- print("DATA BLOCK", "target type", targetTypeId, "");
-
- err = buf.nextUint16(&data16);
- if (err != NO_ERROR) {
- return err;
- }
- print("", "overlay type", static_cast<uint32_t>(data16), "");
-
- err = buf.nextUint16(&data16);
- if (err != NO_ERROR) {
- return err;
- }
- const uint32_t entryCount = static_cast<uint32_t>(data16);
- print("", "entry count", entryCount, "");
-
- err = buf.nextUint16(&data16);
- if (err != NO_ERROR) {
- return err;
- }
- const uint32_t entryOffset = static_cast<uint32_t>(data16);
- print("", "entry offset", entryOffset, "");
-
- for (uint32_t i = 0; i < entryCount; i++) {
- uint32_t data32;
- err = buf.nextUint32(&data32);
- if (err != NO_ERROR) {
- return err;
- }
-
- uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i);
- String8 type;
- String8 name;
- err = resource_metadata(am, resID, NULL, &type, &name);
- if (err != NO_ERROR) {
- return err;
- }
- if (data32 != ResTable_type::NO_ENTRY) {
- print("", "entry", data32, "%s/%s", type.string(), name.string());
- }
- }
- }
-
- return NO_ERROR;
- }
-}
-
-int idmap_inspect(const char *idmap_path) {
- IdmapBuffer buf;
- if (buf.init(idmap_path) < 0) {
- // printe done from IdmapBuffer::init
- return EXIT_FAILURE;
- }
- AssetManager am;
- if (parse_idmap_header(buf, am) != NO_ERROR) {
- // printe done from parse_idmap_header
- return EXIT_FAILURE;
- }
- if (parse_data(buf, am) != NO_ERROR) {
- // printe done from parse_data_header
- return EXIT_FAILURE;
- }
- return EXIT_SUCCESS;
-}
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
deleted file mode 100644
index 847dda3..0000000
--- a/cmds/idmap/scan.cpp
+++ /dev/null
@@ -1,281 +0,0 @@
-#include <dirent.h>
-#include <inttypes.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-
-#include "idmap.h"
-
-#include <memory>
-#include <androidfw/ResourceTypes.h>
-#include <androidfw/StreamingZipInflater.h>
-#include <androidfw/ZipFileRO.h>
-#include <cutils/properties.h>
-#include <private/android_filesystem_config.h> // for AID_SYSTEM
-#include <utils/SortedVector.h>
-#include <utils/String16.h>
-#include <utils/String8.h>
-
-#define NO_OVERLAY_TAG (-1000)
-
-using namespace android;
-
-namespace {
- struct Overlay {
- Overlay() {}
- Overlay(const String8& a, const String8& i, int p) :
- apk_path(a), idmap_path(i), priority(p) {}
-
- bool operator<(Overlay const& rhs) const
- {
- return rhs.priority > priority;
- }
-
- String8 apk_path;
- String8 idmap_path;
- int priority;
- };
-
- bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
- {
- // the file is opened for appending so that it doesn't get truncated
- // before we can guarantee mutual exclusion via the flock
- FILE* fout = fopen(filename, "a");
- if (fout == NULL) {
- return false;
- }
-
- if (TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_EX)) != 0) {
- fclose(fout);
- return false;
- }
-
- if (TEMP_FAILURE_RETRY(ftruncate(fileno(fout), 0)) != 0) {
- TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
- fclose(fout);
- return false;
- }
-
- for (size_t i = 0; i < overlayVector.size(); ++i) {
- const Overlay& overlay = overlayVector[i];
- fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
- }
-
- TEMP_FAILURE_RETRY(fflush(fout));
- TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
- fclose(fout);
-
- // Make file world readable since Zygote (running as root) will read
- // it when creating the initial AssetManger object
- const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
- if (chmod(filename, mode) == -1) {
- unlink(filename);
- return false;
- }
-
- return true;
- }
-
- String8 flatten_path(const char *path)
- {
- String16 tmp(path);
- tmp.replaceAll('/', '@');
- return String8(tmp);
- }
-
- bool check_property(String16 property, String16 value) {
- char propBuf[PROPERTY_VALUE_MAX];
- property_get(String8(property).c_str(), propBuf, NULL);
- return String8(value) == propBuf;
- }
-
- int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name,
- bool* is_static_overlay)
- {
- const size_t N = parser.getAttributeCount();
- String16 target;
- int priority = -1;
- String16 propName = String16();
- String16 propValue = String16();
- for (size_t i = 0; i < N; ++i) {
- size_t len;
- String16 key(parser.getAttributeName(i, &len));
- if (key == String16("targetPackage")) {
- const char16_t *p = parser.getAttributeStringValue(i, &len);
- if (p != NULL) {
- target = String16(p, len);
- }
- } else if (key == String16("priority")) {
- Res_value v;
- if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
- priority = v.data;
- if (priority < 0 || priority > 9999) {
- return -1;
- }
- }
- } else if (key == String16("isStatic")) {
- Res_value v;
- if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
- *is_static_overlay = (v.data != 0);
- }
- } else if (key == String16("requiredSystemPropertyName")) {
- const char16_t *p = parser.getAttributeStringValue(i, &len);
- if (p != NULL) {
- propName = String16(p, len);
- }
- } else if (key == String16("requiredSystemPropertyValue")) {
- const char16_t *p = parser.getAttributeStringValue(i, &len);
- if (p != NULL) {
- propValue = String16(p, len);
- }
- }
- }
-
- // Note that conditional property enablement/exclusion only applies if
- // the attribute is present. In its absence, all overlays are presumed enabled.
- if (propName.size() > 0 && propValue.size() > 0) {
- // if property set & equal to value, then include overlay - otherwise skip
- if (!check_property(propName, propValue)) {
- return NO_OVERLAY_TAG;
- }
- }
-
- if (target == String16(target_package_name)) {
- return priority;
- }
- return NO_OVERLAY_TAG;
- }
-
- int parse_manifest(const void *data, size_t size, const char *target_package_name)
- {
- ResXMLTree parser;
- parser.setTo(data, size);
- if (parser.getError() != NO_ERROR) {
- ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
- return -1;
- }
-
- ResXMLParser::event_code_t type;
- bool is_static_overlay = false;
- int priority = NO_OVERLAY_TAG;
- do {
- type = parser.next();
- if (type == ResXMLParser::START_TAG) {
- size_t len;
- String16 tag(parser.getElementName(&len));
- if (tag == String16("overlay")) {
- priority = parse_overlay_tag(parser, target_package_name, &is_static_overlay);
- break;
- }
- }
- } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
-
- if (is_static_overlay) {
- return priority;
- }
- return NO_OVERLAY_TAG;
- }
-
- int parse_apk(const char *path, const char *target_package_name)
- {
- std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(path));
- if (zip.get() == NULL) {
- ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
- return -1;
- }
- ZipEntryRO entry;
- if ((entry = zip->findEntryByName("AndroidManifest.xml")) == NULL) {
- ALOGW("%s: failed to find entry AndroidManifest.xml\n", __FUNCTION__);
- return -1;
- }
- uint32_t uncompLen = 0;
- uint16_t method;
- if (!zip->getEntryInfo(entry, &method, &uncompLen, NULL, NULL, NULL, NULL)) {
- ALOGW("%s: failed to read entry info\n", __FUNCTION__);
- return -1;
- }
- if (method != ZipFileRO::kCompressDeflated) {
- ALOGW("%s: cannot handle zip compression method %" PRIu16 "\n", __FUNCTION__, method);
- return -1;
- }
- FileMap *dataMap = zip->createEntryFileMap(entry);
- if (dataMap == NULL) {
- ALOGW("%s: failed to create FileMap\n", __FUNCTION__);
- return -1;
- }
- char *buf = new char[uncompLen];
- if (NULL == buf) {
- ALOGW("%s: failed to allocate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
- delete dataMap;
- return -1;
- }
- StreamingZipInflater inflater(dataMap, uncompLen);
- if (inflater.read(buf, uncompLen) < 0) {
- ALOGW("%s: failed to inflate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
- delete[] buf;
- delete dataMap;
- return -1;
- }
-
- int priority = parse_manifest(buf, static_cast<size_t>(uncompLen), target_package_name);
- delete[] buf;
- delete dataMap;
- return priority;
- }
-}
-
-int idmap_scan(const char *target_package_name, const char *target_apk_path,
- const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
-{
- String8 filename = String8(idmap_dir);
- filename.appendPath("overlays.list");
-
- SortedVector<Overlay> overlayVector;
- const size_t N = overlay_dirs->size();
- for (size_t i = 0; i < N; ++i) {
- const char *overlay_dir = overlay_dirs->itemAt(i);
- DIR *dir = opendir(overlay_dir);
- if (dir == NULL) {
- return EXIT_FAILURE;
- }
-
- struct dirent *dirent;
- while ((dirent = readdir(dir)) != NULL) {
- struct stat st;
- char overlay_apk_path[PATH_MAX + 1];
- snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name);
- if (stat(overlay_apk_path, &st) < 0) {
- continue;
- }
- if (!S_ISREG(st.st_mode)) {
- continue;
- }
-
- int priority = parse_apk(overlay_apk_path, target_package_name);
- if (priority < 0) {
- continue;
- }
-
- String8 idmap_path(idmap_dir);
- idmap_path.appendPath(flatten_path(overlay_apk_path + 1));
- idmap_path.append("@idmap");
-
- if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) {
- ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n",
- target_apk_path, overlay_apk_path, idmap_path.string());
- continue;
- }
-
- Overlay overlay(String8(overlay_apk_path), idmap_path, priority);
- overlayVector.add(overlay);
- }
-
- closedir(dir);
- }
-
- if (!writePackagesList(filename.string(), overlayVector)) {
- return EXIT_FAILURE;
- }
-
- return EXIT_SUCCESS;
-}
-
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 4e57e88..878cef9 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -43,17 +43,7 @@
],
host_supported: true,
srcs: [
- "libidmap2/BinaryStreamVisitor.cpp",
- "libidmap2/CommandLineOptions.cpp",
- "libidmap2/FileUtils.cpp",
- "libidmap2/Idmap.cpp",
- "libidmap2/Policies.cpp",
- "libidmap2/PrettyPrintVisitor.cpp",
- "libidmap2/RawPrintVisitor.cpp",
- "libidmap2/ResourceUtils.cpp",
- "libidmap2/Result.cpp",
- "libidmap2/Xml.cpp",
- "libidmap2/ZipFile.cpp",
+ "libidmap2/**/*.cpp",
],
export_include_dirs: ["include"],
target: {
@@ -67,6 +57,7 @@
"libcutils",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -79,6 +70,37 @@
"libcutils",
"libutils",
"libziparchive",
+ "libidmap2_policies",
+ ],
+ },
+ },
+}
+
+cc_library {
+ name: "libidmap2_policies",
+ defaults: [
+ "idmap2_defaults",
+ ],
+ host_supported: true,
+ export_include_dirs: ["libidmap2_policies/include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ android: {
+ static: {
+ enabled: false,
+ },
+ shared_libs: [
+ "libandroidfw",
+ ],
+ },
+ host: {
+ shared: {
+ enabled: false,
+ },
+ static_libs: [
+ "libandroidfw",
],
},
},
@@ -104,9 +126,10 @@
"tests/PoliciesTests.cpp",
"tests/PrettyPrintVisitorTests.cpp",
"tests/RawPrintVisitorTests.cpp",
+ "tests/ResourceMappingTests.cpp",
"tests/ResourceUtilsTests.cpp",
"tests/ResultTests.cpp",
- "tests/XmlTests.cpp",
+ "tests/XmlParserTests.cpp",
"tests/ZipFileTests.cpp",
],
required: [
@@ -123,6 +146,7 @@
"libutils",
"libz",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -134,6 +158,7 @@
"liblog",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
shared_libs: [
"libz",
@@ -150,12 +175,13 @@
],
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: {
@@ -166,6 +192,7 @@
"libidmap2",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
},
host: {
@@ -177,12 +204,14 @@
"liblog",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
shared_libs: [
"libz",
],
},
},
+
}
cc_binary {
@@ -203,6 +232,7 @@
"libidmap2",
"libutils",
"libziparchive",
+ "libidmap2_policies",
],
static_libs: [
"libidmap2daidl",
@@ -235,3 +265,17 @@
],
path: "idmap2d/aidl",
}
+
+aidl_interface {
+ name: "overlayable_policy_aidl",
+ unstable: true,
+ srcs: [":overlayable_policy_aidl_files"],
+}
+
+filegroup {
+ name: "overlayable_policy_aidl_files",
+ srcs: [
+ "idmap2d/aidl/android/os/OverlayablePolicy.aidl",
+ ],
+ path: "idmap2d/aidl",
+}
diff --git a/cmds/idmap2/CPPLINT.cfg b/cmds/idmap2/CPPLINT.cfg
index 9dc6b4a..20ed43c 100644
--- a/cmds/idmap2/CPPLINT.cfg
+++ b/cmds/idmap2/CPPLINT.cfg
@@ -15,4 +15,4 @@
set noparent
linelength=100
root=..
-filter=+build/include_alpha
+filter=+build/include_alpha,-runtime/references,-build/c++
diff --git a/cmds/idmap2/TEST_MAPPING b/cmds/idmap2/TEST_MAPPING
index 26ccf03..9e0fb84 100644
--- a/cmds/idmap2/TEST_MAPPING
+++ b/cmds/idmap2/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name" : "idmap2_tests"
}
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/services/core/java/com/android/server/om"
+ }
]
}
diff --git a/cmds/idmap2/idmap2/Verify.cpp b/cmds/idmap2/idmap2/CommandUtils.cpp
similarity index 70%
rename from cmds/idmap2/idmap2/Verify.cpp
rename to cmds/idmap2/idmap2/CommandUtils.cpp
index 9cb67b3..8f5845b 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 @@
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 0000000..e717e04
--- /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 718e361..69eea8d 100644
--- a/cmds/idmap2/idmap2/Commands.h
+++ b/cmds/idmap2/idmap2/Commands.h
@@ -23,9 +23,9 @@
#include "idmap2/Result.h"
android::idmap2::Result<android::idmap2::Unit> Create(const std::vector<std::string>& args);
+android::idmap2::Result<android::idmap2::Unit> CreateMultiple(const std::vector<std::string>& args);
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/Create.cpp b/cmds/idmap2/idmap2/Create.cpp
index f482191..9682b6ea 100644
--- a/cmds/idmap2/idmap2/Create.cpp
+++ b/cmds/idmap2/idmap2/Create.cpp
@@ -20,15 +20,14 @@
#include <fstream>
#include <memory>
#include <ostream>
-#include <sstream>
-#include <string>
#include <vector>
+#include "androidfw/ResourceTypes.h"
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
#include "idmap2/SysTrace.h"
using android::ApkAssets;
@@ -36,14 +35,15 @@
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::PoliciesToBitmask;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::PoliciesToBitmaskResult;
using android::idmap2::utils::UidHasWriteAccessToPath;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
Result<Unit> Create(const std::vector<std::string>& args) {
SYSTRACE << "Create " << args;
std::string target_apk_path;
@@ -78,7 +78,7 @@
}
PolicyBitmask fulfilled_policies = 0;
- auto conv_result = PoliciesToBitmask(policies);
+ auto conv_result = PoliciesToBitmaskResult(policies);
if (conv_result) {
fulfilled_policies |= *conv_result;
} else {
@@ -86,7 +86,7 @@
}
if (fulfilled_policies == 0) {
- fulfilled_policies |= PolicyFlags::POLICY_PUBLIC;
+ fulfilled_policies |= PolicyFlags::PUBLIC;
}
const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
@@ -99,8 +99,8 @@
return Error("failed to load apk %s", overlay_apk_path.c_str());
}
- const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
- *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
if (!idmap) {
return Error(idmap.GetError(), "failed to create idmap");
}
diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp
new file mode 100644
index 0000000..abdfaf4
--- /dev/null
+++ b/cmds/idmap2/idmap2/CreateMultiple.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#include <sys/stat.h> // umask
+#include <sys/types.h> // umask
+
+#include <fstream>
+#include <memory>
+#include <ostream>
+#include <vector>
+
+#include "Commands.h"
+#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"
+#include "idmap2/PolicyUtils.h"
+#include "idmap2/SysTrace.h"
+
+using android::ApkAssets;
+using android::base::StringPrintf;
+using android::idmap2::BinaryStreamVisitor;
+using android::idmap2::CommandLineOptions;
+using android::idmap2::Error;
+using android::idmap2::Idmap;
+using android::idmap2::Result;
+using android::idmap2::Unit;
+using android::idmap2::utils::kIdmapCacheDir;
+using android::idmap2::utils::kIdmapFilePermissionMask;
+using android::idmap2::utils::PoliciesToBitmaskResult;
+using android::idmap2::utils::UidHasWriteAccessToPath;
+
+Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
+ SYSTRACE << "CreateMultiple " << args;
+ std::string target_apk_path;
+ std::string idmap_dir = kIdmapCacheDir;
+ std::vector<std::string> overlay_apk_paths;
+ std::vector<std::string> policies;
+ bool ignore_overlayable = false;
+
+ const CommandLineOptions opts =
+ CommandLineOptions("idmap2 create-multiple")
+ .MandatoryOption("--target-apk-path",
+ "input: path to apk which will have its resources overlaid",
+ &target_apk_path)
+ .MandatoryOption("--overlay-apk-path",
+ "input: path to apk which contains the new resource values",
+ &overlay_apk_paths)
+ .OptionalOption("--idmap-dir",
+ StringPrintf("output: path to the directory in which to write idmap file"
+ " (defaults to %s)",
+ kIdmapCacheDir),
+ &idmap_dir)
+ .OptionalOption("--policy",
+ "input: an overlayable policy this overlay fulfills"
+ " (if none or supplied, the overlay policy will default to \"public\")",
+ &policies)
+ .OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks",
+ &ignore_overlayable);
+ const auto opts_ok = opts.Parse(args);
+ if (!opts_ok) {
+ return opts_ok.GetError();
+ }
+
+ PolicyBitmask fulfilled_policies = 0;
+ auto conv_result = PoliciesToBitmaskResult(policies);
+ if (conv_result) {
+ fulfilled_policies |= *conv_result;
+ } else {
+ return conv_result.GetError();
+ }
+
+ if (fulfilled_policies == 0) {
+ fulfilled_policies |= PolicyFlags::PUBLIC;
+ }
+
+ const std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error("failed to load apk %s", target_apk_path.c_str());
+ }
+
+ std::vector<std::string> idmap_paths;
+ for (const std::string& overlay_apk_path : overlay_apk_paths) {
+ const std::string idmap_path = Idmap::CanonicalIdmapPathFor(idmap_dir, overlay_apk_path);
+ const uid_t uid = getuid();
+ if (!UidHasWriteAccessToPath(uid, idmap_path)) {
+ LOG(WARNING) << "uid " << uid << "does not have write access to " << idmap_path.c_str();
+ continue;
+ }
+
+ 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();
+ continue;
+ }
+
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, !ignore_overlayable);
+ if (!idmap) {
+ LOG(WARNING) << "failed to create idmap";
+ continue;
+ }
+
+ umask(kIdmapFilePermissionMask);
+ std::ofstream fout(idmap_path);
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str();
+ continue;
+ }
+
+ BinaryStreamVisitor visitor(fout);
+ (*idmap)->accept(&visitor);
+ fout.close();
+ if (fout.fail()) {
+ LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str();
+ continue;
+ }
+ }
+
+ idmap_paths.emplace_back(idmap_path);
+ }
+
+ for (const std::string& idmap_path : idmap_paths) {
+ std::cout << idmap_path << std::endl;
+ }
+
+ return Unit{};
+}
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index b7ae9d0..c441709 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -33,9 +33,10 @@
#include "androidfw/Util.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
#include "utils/String16.h"
#include "utils/String8.h"
@@ -57,8 +58,7 @@
using android::idmap2::ResourceId;
using android::idmap2::Result;
using android::idmap2::Unit;
-using android::idmap2::Xml;
-using android::idmap2::ZipFile;
+using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::util::Utf16ToUtf8;
namespace {
@@ -85,11 +85,42 @@
return Error("failed to obtain resource id for %s", res.c_str());
}
-Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+void PrintValue(AssetManager2* const am, const Res_value& value, const ApkAssetsCookie& cookie,
+ std::string* const out) {
+ switch (value.dataType) {
+ case Res_value::TYPE_INT_DEC:
+ out->append(StringPrintf("%d", value.data));
+ break;
+ case Res_value::TYPE_INT_HEX:
+ out->append(StringPrintf("0x%08x", value.data));
+ break;
+ case Res_value::TYPE_INT_BOOLEAN:
+ out->append(value.data != 0 ? "true" : "false");
+ break;
+ case Res_value::TYPE_STRING: {
+ const ResStringPool* pool = am->GetStringPoolForCookie(cookie);
+ out->append("\"");
+ size_t len;
+ if (pool->isUTF8()) {
+ const char* str = pool->string8At(value.data, &len);
+ out->append(str, len);
+ } else {
+ const char16_t* str16 = pool->stringAt(value.data, &len);
+ out->append(Utf16ToUtf8(StringPiece16(str16, len)));
+ }
+ out->append("\"");
+ } break;
+ default:
+ out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+ break;
+ }
+}
+
+Result<std::string> WARN_UNUSED GetValue(AssetManager2* const am, ResourceId resid) {
Res_value value;
ResTable_config config;
uint32_t flags;
- ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
+ ApkAssetsCookie cookie = am->GetResource(resid, true, 0, &value, &config, &flags);
if (cookie == kInvalidCookie) {
return Error("no resource 0x%08x in asset manager", resid);
}
@@ -104,57 +135,40 @@
out.append(config.toString().c_str());
out.append("' value=");
- switch (value.dataType) {
- case Res_value::TYPE_INT_DEC:
- out.append(StringPrintf("%d", value.data));
- break;
- case Res_value::TYPE_INT_HEX:
- out.append(StringPrintf("0x%08x", value.data));
- break;
- case Res_value::TYPE_INT_BOOLEAN:
- out.append(value.data != 0 ? "true" : "false");
- break;
- case Res_value::TYPE_STRING: {
- const ResStringPool* pool = am.GetStringPoolForCookie(cookie);
- size_t len;
- if (pool->isUTF8()) {
- const char* str = pool->string8At(value.data, &len);
- out.append(str, len);
- } else {
- const char16_t* str16 = pool->stringAt(value.data, &len);
- out += Utf16ToUtf8(StringPiece16(str16, len));
- }
- } break;
- default:
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ const android::ResolvedBag* bag = am->GetBag(static_cast<uint32_t>(value.data));
+ if (bag == nullptr) {
out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
- break;
+ return out;
+ }
+ out.append("[");
+ Res_value bag_val;
+ ResTable_config selected_config;
+ uint32_t flags;
+ uint32_t ref;
+ ApkAssetsCookie bag_cookie;
+ for (size_t i = 0; i < bag->entry_count; ++i) {
+ const android::ResolvedBag::Entry& entry = bag->entries[i];
+ bag_val = entry.value;
+ bag_cookie = am->ResolveReference(entry.cookie, &bag_val, &selected_config, &flags, &ref);
+ if (bag_cookie == kInvalidCookie) {
+ out.append(
+ StringPrintf("Error: dataType=0x%02x data=0x%08x", bag_val.dataType, bag_val.data));
+ continue;
+ }
+ PrintValue(am, bag_val, bag_cookie, &out);
+ if (i != bag->entry_count - 1) {
+ out.append(", ");
+ }
+ }
+ out.append("]");
+ } else {
+ PrintValue(am, value, cookie, &out);
}
+
return out;
}
-Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) {
- const auto zip = ZipFile::Open(apk_path);
- if (!zip) {
- return Error("failed to open %s as zip", apk_path.c_str());
- }
- const auto entry = zip->Uncompress("AndroidManifest.xml");
- if (!entry) {
- return Error("failed to uncompress AndroidManifest.xml in %s", apk_path.c_str());
- }
- const auto xml = Xml::Create(entry->buf, entry->size);
- if (!xml) {
- return Error("failed to create XML buffer");
- }
- const auto tag = xml->FindTag("overlay");
- if (!tag) {
- return Error("failed to find <overlay> tag");
- }
- const auto iter = tag->find("targetPackage");
- if (iter == tag->end()) {
- return Error("failed to find targetPackage attribute");
- }
- return iter->second;
-}
} // namespace
Result<Unit> Lookup(const std::vector<std::string>& args) {
@@ -202,12 +216,12 @@
}
apk_assets.push_back(std::move(target_apk));
- const Result<std::string> package_name =
- GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string());
- if (!package_name) {
- return Error("failed to parse android:targetPackage from overlay manifest");
+ auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath().to_string(),
+ true /* assert_overlay */);
+ if (!manifest_info) {
+ return manifest_info.GetError();
}
- target_package_name = *package_name;
+ target_package_name = (*manifest_info).target_package;
} else if (target_path != idmap_header->GetTargetPath()) {
return Error("different target APKs (expected target APK %s but %s has target APK %s)",
target_path.c_str(), idmap_path.c_str(),
@@ -235,7 +249,7 @@
return Error(resid.GetError(), "failed to parse resource ID");
}
- const Result<std::string> value = GetValue(am, *resid);
+ const Result<std::string> value = GetValue(&am, *resid);
if (!value) {
return Error(value.GetError(), "resource 0x%08x not found", *resid);
}
diff --git a/cmds/idmap2/idmap2/Main.cpp b/cmds/idmap2/idmap2/Main.cpp
index 8794908..fb093f0 100644
--- a/cmds/idmap2/idmap2/Main.cpp
+++ b/cmds/idmap2/idmap2/Main.cpp
@@ -53,7 +53,8 @@
int main(int argc, char** argv) {
SYSTRACE << "main";
const NameToFunctionMap commands = {
- {"create", Create}, {"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 da8c06e..3625045 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -20,7 +20,6 @@
#include <memory>
#include <ostream>
#include <set>
-#include <sstream>
#include <string>
#include <utility>
#include <vector>
@@ -28,30 +27,33 @@
#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"
-#include "idmap2/Xml.h"
-#include "idmap2/ZipFile.h"
+#include "idmap2/XmlParser.h"
using android::idmap2::CommandLineOptions;
using android::idmap2::Error;
using android::idmap2::Idmap;
-using android::idmap2::kPolicyOdm;
-using android::idmap2::kPolicyOem;
-using android::idmap2::kPolicyProduct;
-using android::idmap2::kPolicyPublic;
-using android::idmap2::kPolicySystem;
-using android::idmap2::kPolicyVendor;
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Unit;
+using android::idmap2::policy::kPolicyOdm;
+using android::idmap2::policy::kPolicyOem;
+using android::idmap2::policy::kPolicyProduct;
+using android::idmap2::policy::kPolicyPublic;
+using android::idmap2::policy::kPolicySystem;
+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;
namespace {
@@ -98,6 +100,7 @@
}
std::vector<std::string> PoliciesForPath(const std::string& apk_path) {
+ // clang-format off
static const std::vector<std::pair<std::string, std::string>> values = {
{"/odm/", kPolicyOdm},
{"/oem/", kPolicyOem},
@@ -106,6 +109,7 @@
{"/system_ext/", kPolicySystem},
{"/vendor/", kPolicyVendor},
};
+ // clang-format on
std::vector<std::string> fulfilled_policies = {kPolicyPublic};
for (auto const& pair : values) {
@@ -178,11 +182,11 @@
// Note that conditional property enablement/exclusion only applies if
// the attribute is present. In its absence, all overlays are presumed enabled.
- if (!overlay_info->requiredSystemPropertyName.empty()
- && !overlay_info->requiredSystemPropertyValue.empty()) {
+ if (!overlay_info->requiredSystemPropertyName.empty() &&
+ !overlay_info->requiredSystemPropertyValue.empty()) {
// if property set & equal to value, then include overlay - otherwise skip
- if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "")
- != overlay_info->requiredSystemPropertyValue) {
+ if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "") !=
+ overlay_info->requiredSystemPropertyValue) {
continue;
}
}
@@ -215,7 +219,15 @@
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 d723776..15e22a3 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -33,22 +33,29 @@
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.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::PolicyBitmask;
+using android::idmap2::ZipFile;
using android::idmap2::utils::kIdmapCacheDir;
using android::idmap2::utils::kIdmapFilePermissionMask;
using android::idmap2::utils::UidHasWriteAccessToPath;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
namespace {
+constexpr const char* kFrameworkPath = "/system/framework/framework-res.apk";
+
Status ok() {
return Status::ok();
}
@@ -62,6 +69,21 @@
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 {
@@ -93,21 +115,52 @@
return ok();
}
-Status Idmap2Service::verifyIdmap(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) {
+Status Idmap2Service::verifyIdmap(const std::string& target_apk_path,
+ 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);
fin.close();
- *_aidl_return = header && header->IsUpToDate();
+ if (!header) {
+ *_aidl_return = false;
+ return error("failed to parse idmap header");
+ }
- // TODO(b/119328308): Check that the set of fulfilled policies of the overlay has not changed
+ uint32_t target_crc;
+ if (target_apk_path == kFrameworkPath && android_crc_) {
+ target_crc = *android_crc_;
+ } else {
+ auto target_crc_status = GetCrc(target_apk_path, &target_crc);
+ if (!target_crc_status.isOk()) {
+ *_aidl_return = false;
+ return target_crc_status;
+ }
- return ok();
+ // 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;
+ }
+ }
+
+ 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;
+ }
+
+ 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,
@@ -137,21 +190,27 @@
return error("failed to load apk " + overlay_apk_path);
}
- const auto idmap = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path,
- *overlay_apk, policy_bitmask, enforce_overlayable);
+ const auto idmap =
+ Idmap::FromApkAssets(*target_apk, *overlay_apk, policy_bitmask, enforce_overlayable);
if (!idmap) {
return error(idmap.GetErrorMessage());
}
+ // idmap files are mapped with mmap in libandroidfw. Deleting and recreating the idmap guarantees
+ // that existing memory maps will continue to be valid and unaffected.
+ unlink(idmap_path.c_str());
+
umask(kIdmapFilePermissionMask);
std::ofstream fout(idmap_path);
if (fout.fail()) {
return error("failed to open idmap path " + idmap_path);
}
+
BinaryStreamVisitor visitor(fout);
(*idmap)->accept(&visitor);
fout.close();
if (fout.fail()) {
+ unlink(idmap_path.c_str());
return error("failed to write to idmap path " + idmap_path);
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 73a236a..abee999 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -20,9 +20,6 @@
#include <android-base/unique_fd.h>
#include <binder/BinderService.h>
-#include <optional>
-#include <string>
-
#include "android/os/BnIdmap2.h"
namespace android::os {
@@ -34,18 +31,24 @@
}
binder::Status getIdmapPath(const std::string& overlay_apk_path, int32_t user_id,
- std::string* _aidl_return);
+ std::string* _aidl_return) override;
binder::Status removeIdmap(const std::string& overlay_apk_path, int32_t user_id,
- bool* _aidl_return);
+ bool* _aidl_return) override;
- binder::Status verifyIdmap(const std::string& overlay_apk_path, int32_t fulfilled_policies,
- bool enforce_overlayable, int32_t user_id, bool* _aidl_return);
+ binder::Status verifyIdmap(const std::string& target_apk_path,
+ const std::string& overlay_apk_path, int32_t fulfilled_policies,
+ bool enforce_overlayable, int32_t user_id,
+ bool* _aidl_return) override;
binder::Status createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
- std::optional<std::string>* _aidl_return);
+ std::optional<std::string>* _aidl_return) override;
+
+ private:
+ // Cache the crc of the android framework package since the crc cannot change without a reboot.
+ std::optional<uint32_t> android_crc_;
};
} // namespace android::os
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
index cd474c0..156f1d7 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/IIdmap2.aidl
@@ -20,18 +20,13 @@
* @hide
*/
interface IIdmap2 {
- const int POLICY_PUBLIC = 0x00000001;
- const int POLICY_SYSTEM_PARTITION = 0x00000002;
- const int POLICY_VENDOR_PARTITION = 0x00000004;
- const int POLICY_PRODUCT_PARTITION = 0x00000008;
- const int POLICY_SIGNATURE = 0x00000010;
- const int POLICY_ODM_PARTITION = 0x00000020;
- const int POLICY_OEM_PARTITION = 0x00000040;
-
@utf8InCpp String getIdmapPath(@utf8InCpp String overlayApkPath, int userId);
boolean removeIdmap(@utf8InCpp String overlayApkPath, int userId);
- boolean verifyIdmap(@utf8InCpp String overlayApkPath, int fulfilledPolicies,
- boolean enforceOverlayable, int userId);
+ boolean verifyIdmap(@utf8InCpp String targetApkPath,
+ @utf8InCpp String overlayApkPath,
+ int fulfilledPolicies,
+ boolean enforceOverlayable,
+ int userId);
@nullable @utf8InCpp String createIdmap(@utf8InCpp String targetApkPath,
@utf8InCpp String overlayApkPath,
int fulfilledPolicies,
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
new file mode 100644
index 0000000..02b27a8
--- /dev/null
+++ b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @see ResourcesTypes.h ResTable_overlayable_policy_header::PolicyFlags
+ * @hide
+ */
+interface OverlayablePolicy {
+ const int PUBLIC = 0x00000001;
+ const int SYSTEM_PARTITION = 0x00000002;
+ const int VENDOR_PARTITION = 0x00000004;
+ const int PRODUCT_PARTITION = 0x00000008;
+ const int SIGNATURE = 0x00000010;
+ const int ODM_PARTITION = 0x00000020;
+ const int OEM_PARTITION = 0x00000040;
+ const int ACTOR_SIGNATURE = 0x00000080;
+}
diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
index 2c3e9d3..ff45b14 100644
--- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
+++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h
@@ -29,16 +29,19 @@
public:
explicit BinaryStreamVisitor(std::ostream& stream) : stream_(stream) {
}
- virtual void visit(const Idmap& idmap);
- virtual void visit(const IdmapHeader& header);
- virtual void visit(const IdmapData& data);
- virtual void visit(const IdmapData::Header& header);
- virtual void visit(const IdmapData::TypeEntry& type_entry);
+ ~BinaryStreamVisitor() override = default;
+ void visit(const Idmap& idmap) override;
+ void visit(const IdmapHeader& header) override;
+ void visit(const IdmapData& data) override;
+ void visit(const IdmapData::Header& header) override;
private:
+ void Write(const void* value, size_t length);
+ void Write8(uint8_t value);
void Write16(uint16_t value);
void Write32(uint32_t value);
- void WriteString(const StringPiece& value);
+ void WriteString256(const StringPiece& value);
+ void WriteString(const std::string& value);
std::ostream& stream_;
};
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index ebbb5ffc..0f05592 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -18,19 +18,21 @@
* # idmap file format (current version)
*
* idmap := header data*
- * header := magic version target_crc overlay_crc target_path overlay_path
+ * header := magic version target_crc overlay_crc target_path overlay_path debug_info
* data := data_header data_block*
* data_header := target_package_id types_count
* data_block := target_type overlay_type entry_count entry_offset entry*
- * overlay_path := string
- * target_path := string
+ * overlay_path := string256
+ * target_path := string256
+ * debug_info := string
+ * string := <uint32_t> <uint8_t>+ '\0'+
* entry := <uint32_t>
* entry_count := <uint16_t>
* entry_offset := <uint16_t>
* magic := <uint32_t>
* overlay_crc := <uint32_t>
* overlay_type := <uint16_t>
- * string := <uint8_t>[256]
+ * string256 := <uint8_t>[256]
* target_crc := <uint32_t>
* target_package_id := <uint16_t>
* target_type := <uint16_t>
@@ -41,6 +43,22 @@
* # idmap file format changelog
* ## v1
* - Identical to idmap v1.
+ *
+ * ## v2
+ * - Entries are no longer separated by type into type specific data blocks.
+ * - Added overlay-indexed target resource id lookup capabilities.
+ * - Target and overlay entries are stored as a sparse array in the data block. The target entries
+ * array maps from target resource id to overlay data type and value and the array is sorted by
+ * target resource id. The overlay entries array maps from overlay resource id to target resource
+ * id and the array is sorted by overlay resource id. It is important for both arrays to be sorted
+ * to allow for O(log(number_of_overlaid_resources)) performance when looking up resource
+ * mappings at runtime.
+ * - Idmap can now encode a type and value to override a resource without needing a table entry.
+ * - A string pool block is included to retrieve the value of strings that do not have a resource
+ * table entry.
+ *
+ * ## v3
+ * - Add 'debug' block to IdmapHeader.
*/
#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -55,21 +73,15 @@
#include "androidfw/ApkAssets.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
-#include "idmap2/Policies.h"
+#include "idmap2/ResourceMapping.h"
+#include "idmap2/ZipFile.h"
namespace android::idmap2 {
class Idmap;
class Visitor;
-// use typedefs to let the compiler warn us about implicit casts
-typedef uint32_t ResourceId; // 0xpptteeee
-typedef uint8_t PackageId; // pp in 0xpptteeee
-typedef uint8_t TypeId; // tt in 0xpptteeee
-typedef uint16_t EntryId; // eeee in 0xpptteeee
-
static constexpr const ResourceId kPadding = 0xffffffffu;
-
static constexpr const EntryId kNoEntry = 0xffffu;
// magic number: all idmap files start with this
@@ -82,6 +94,9 @@
// terminating null)
static constexpr const size_t kIdmapStringLength = 256;
+// Retrieves a crc generated using all of the files within the zip that can affect idmap generation.
+Result<uint32_t> GetPackageCrc(const ZipFile& zip_info);
+
class IdmapHeader {
public:
static std::unique_ptr<const IdmapHeader> FromBinaryStream(std::istream& stream);
@@ -102,6 +117,14 @@
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_);
}
@@ -110,10 +133,18 @@
return StringPiece(overlay_path_);
}
+ inline const std::string& GetDebugInfo() const {
+ return debug_info_;
+ }
+
// 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(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;
@@ -125,13 +156,15 @@
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_;
friend Idmap;
DISALLOW_COPY_AND_ASSIGN(IdmapHeader);
};
-
class IdmapData {
public:
class Header {
@@ -142,70 +175,72 @@
return target_package_id_;
}
- inline uint16_t GetTypeCount() const {
- return type_count_;
+ inline PackageId GetOverlayPackageId() const {
+ return overlay_package_id_;
+ }
+
+ inline uint32_t GetTargetEntryCount() const {
+ return target_entry_count;
+ }
+
+ inline uint32_t GetOverlayEntryCount() const {
+ return overlay_entry_count;
+ }
+
+ inline uint32_t GetStringPoolIndexOffset() const {
+ return string_pool_index_offset;
+ }
+
+ inline uint32_t GetStringPoolLength() const {
+ return string_pool_len;
}
void accept(Visitor* v) const;
private:
- Header() {
- }
-
PackageId target_package_id_;
- uint16_t type_count_;
+ PackageId overlay_package_id_;
+ uint32_t target_entry_count;
+ uint32_t overlay_entry_count;
+ uint32_t string_pool_index_offset;
+ uint32_t string_pool_len;
+ Header() = default;
friend Idmap;
+ friend IdmapData;
DISALLOW_COPY_AND_ASSIGN(Header);
};
- class TypeEntry {
- public:
- static std::unique_ptr<const TypeEntry> FromBinaryStream(std::istream& stream);
+ struct TargetEntry {
+ ResourceId target_id;
+ TargetValue::DataType data_type;
+ TargetValue::DataValue data_value;
+ };
- inline TypeId GetTargetTypeId() const {
- return target_type_id_;
- }
-
- inline TypeId GetOverlayTypeId() const {
- return overlay_type_id_;
- }
-
- inline uint16_t GetEntryCount() const {
- return entries_.size();
- }
-
- inline uint16_t GetEntryOffset() const {
- return entry_offset_;
- }
-
- inline EntryId GetEntry(size_t i) const {
- return i < entries_.size() ? entries_[i] : 0xffffu;
- }
-
- void accept(Visitor* v) const;
-
- private:
- TypeEntry() {
- }
-
- TypeId target_type_id_;
- TypeId overlay_type_id_;
- uint16_t entry_offset_;
- std::vector<EntryId> entries_;
-
- friend Idmap;
- DISALLOW_COPY_AND_ASSIGN(TypeEntry);
+ struct OverlayEntry {
+ ResourceId overlay_id;
+ ResourceId target_id;
};
static std::unique_ptr<const IdmapData> FromBinaryStream(std::istream& stream);
+ static Result<std::unique_ptr<const IdmapData>> FromResourceMapping(
+ const ResourceMapping& resource_mapping);
+
inline const std::unique_ptr<const Header>& GetHeader() const {
return header_;
}
- inline const std::vector<std::unique_ptr<const TypeEntry>>& GetTypeEntries() const {
- return type_entries_;
+ inline const std::vector<TargetEntry>& GetTargetEntries() const {
+ return target_entries_;
+ }
+
+ inline const std::vector<OverlayEntry>& GetOverlayEntries() const {
+ return overlay_entries_;
+ }
+
+ inline const void* GetStringPoolData() const {
+ return string_pool_.get();
}
void accept(Visitor* v) const;
@@ -215,7 +250,9 @@
}
std::unique_ptr<const Header> header_;
- std::vector<std::unique_ptr<const TypeEntry>> type_entries_;
+ std::vector<TargetEntry> target_entries_;
+ std::vector<OverlayEntry> overlay_entries_;
+ std::unique_ptr<uint8_t[]> string_pool_;
friend Idmap;
DISALLOW_COPY_AND_ASSIGN(IdmapData);
@@ -232,9 +269,7 @@
// file is used; change this in the next version of idmap to use a named
// package instead; also update FromApkAssets to take additional parameters:
// the target and overlay package names
- static Result<std::unique_ptr<const Idmap>> FromApkAssets(const std::string& target_apk_path,
- const ApkAssets& target_apk_assets,
- const std::string& overlay_apk_path,
+ static Result<std::unique_ptr<const Idmap>> FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable);
@@ -267,7 +302,6 @@
virtual void visit(const IdmapHeader& header) = 0;
virtual void visit(const IdmapData& data) = 0;
virtual void visit(const IdmapData::Header& header) = 0;
- virtual void visit(const IdmapData::TypeEntry& type_entry) = 0;
};
} // namespace android::idmap2
diff --git a/cmds/idmap2/include/idmap2/LogInfo.h b/cmds/idmap2/include/idmap2/LogInfo.h
new file mode 100644
index 0000000..a6237e6
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/LogInfo.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_LOGINFO_H_
+#define IDMAP2_INCLUDE_IDMAP2_LOGINFO_H_
+
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#if __ANDROID__
+#include "android-base/logging.h"
+#else
+#include <iostream>
+#endif
+
+namespace android::idmap2 {
+
+class LogMessage {
+ public:
+ LogMessage() = default;
+
+ template <typename T>
+ LogMessage& operator<<(const T& value) {
+ stream_ << value;
+ return *this;
+ }
+
+ std::string GetString() const {
+ return stream_.str();
+ }
+
+ private:
+ std::stringstream stream_;
+};
+
+class LogInfo {
+ public:
+ LogInfo() = default;
+
+ inline void Info(const LogMessage& msg) {
+ lines_.push_back("I " + msg.GetString());
+ }
+
+ inline void Warning(const LogMessage& msg) {
+#ifdef __ANDROID__
+ LOG(WARNING) << msg.GetString();
+#else
+ std::cerr << "W " << msg.GetString() << std::endl;
+#endif
+ lines_.push_back("W " + msg.GetString());
+ }
+
+ inline std::string GetString() const {
+ std::ostringstream stream;
+ std::copy(lines_.begin(), lines_.end(), std::ostream_iterator<std::string>(stream, "\n"));
+ return stream.str();
+ }
+
+ private:
+ std::vector<std::string> lines_;
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_LOGINFO_H_
diff --git a/cmds/idmap2/include/idmap2/Policies.h b/cmds/idmap2/include/idmap2/Policies.h
deleted file mode 100644
index 87edd35..0000000
--- a/cmds/idmap2/include/idmap2/Policies.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <string>
-#include <vector>
-
-#include "Result.h"
-#include "androidfw/ResourceTypes.h"
-#include "androidfw/StringPiece.h"
-
-#ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
-#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
-
-namespace android::idmap2 {
-
-constexpr const char* kPolicyOdm = "odm";
-constexpr const char* kPolicyOem = "oem";
-constexpr const char* kPolicyProduct = "product";
-constexpr const char* kPolicyPublic = "public";
-constexpr const char* kPolicySignature = "signature";
-constexpr const char* kPolicySystem = "system";
-constexpr const char* kPolicyVendor = "vendor";
-
-using PolicyFlags = ResTable_overlayable_policy_header::PolicyFlags;
-using PolicyBitmask = uint32_t;
-
-// Parses the string representations of policies into a bitmask.
-Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies);
-
-// Retrieves the string representations of policies in the bitmask.
-std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask);
-
-} // namespace android::idmap2
-
-#endif // IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
diff --git a/cmds/idmap2/include/idmap2/PolicyUtils.h b/cmds/idmap2/include/idmap2/PolicyUtils.h
new file mode 100644
index 0000000..b95b8b4
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/PolicyUtils.h
@@ -0,0 +1,41 @@
+/*
+ * 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_INCLUDE_IDMAP2_POLICYUTILS_H_
+#define IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "androidfw/ResourceTypes.h"
+#include "idmap2/Policies.h"
+#include "idmap2/Result.h"
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
+namespace android::idmap2::utils {
+
+// Returns a Result object containing a policy flag bitmask built from a list of policy strings.
+// On error will contain a human readable message listing the invalid policies.
+Result<PolicyBitmask> PoliciesToBitmaskResult(const std::vector<std::string>& policies);
+
+// Converts a bitmask of policy flags into a list of their string representation as would be written
+// into XML
+std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask);
+
+} // namespace android::idmap2::utils
+
+#endif // IDMAP2_INCLUDE_IDMAP2_POLICYUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
index 5111bb2..5dcf217 100644
--- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h
@@ -33,17 +33,16 @@
public:
explicit PrettyPrintVisitor(std::ostream& stream) : stream_(stream) {
}
- virtual void visit(const Idmap& idmap);
- virtual void visit(const IdmapHeader& header);
- virtual void visit(const IdmapData& data);
- virtual void visit(const IdmapData::Header& header);
- virtual void visit(const IdmapData::TypeEntry& type_entry);
+ ~PrettyPrintVisitor() override = default;
+ void visit(const Idmap& idmap) override;
+ void visit(const IdmapHeader& header) override;
+ void visit(const IdmapData& data) override;
+ void visit(const IdmapData::Header& header) override;
private:
std::ostream& stream_;
std::unique_ptr<const ApkAssets> target_apk_;
AssetManager2 target_am_;
- PackageId last_seen_package_id_;
};
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
index 2e543d4..92c1864 100644
--- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h
+++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h
@@ -34,22 +34,25 @@
public:
explicit RawPrintVisitor(std::ostream& stream) : stream_(stream), offset_(0) {
}
- virtual void visit(const Idmap& idmap);
- virtual void visit(const IdmapHeader& header);
- virtual void visit(const IdmapData& data);
- virtual void visit(const IdmapData::Header& header);
- virtual void visit(const IdmapData::TypeEntry& type_entry);
+ ~RawPrintVisitor() override = default;
+ void visit(const Idmap& idmap) override;
+ void visit(const IdmapHeader& header) override;
+ void visit(const IdmapData& data) override;
+ void visit(const IdmapData::Header& header) override;
private:
+ void print(uint8_t value, const char* fmt, ...);
void print(uint16_t value, const char* fmt, ...);
void print(uint32_t value, const char* fmt, ...);
- void print(const std::string& value, const char* fmt, ...);
+ void print(const std::string& value, size_t encoded_size, const char* fmt, ...);
+ void print_raw(uint32_t length, const char* fmt, ...);
std::ostream& stream_;
std::unique_ptr<const ApkAssets> target_apk_;
+ std::unique_ptr<const ApkAssets> overlay_apk_;
AssetManager2 target_am_;
+ AssetManager2 overlay_am_;
size_t offset_;
- PackageId last_seen_package_id_;
};
} // namespace idmap2
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
new file mode 100644
index 0000000..5869409
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+#define IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "androidfw/ApkAssets.h"
+#include "idmap2/LogInfo.h"
+#include "idmap2/Policies.h"
+#include "idmap2/ResourceUtils.h"
+#include "idmap2/Result.h"
+#include "idmap2/XmlParser.h"
+
+using android::idmap2::utils::OverlayManifestInfo;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+
+namespace android::idmap2 {
+
+struct TargetValue {
+ typedef uint8_t DataType;
+ typedef uint32_t DataValue;
+ DataType data_type;
+ DataValue data_value;
+};
+
+using TargetResourceMap = std::map<ResourceId, TargetValue>;
+using OverlayResourceMap = std::map<ResourceId, ResourceId>;
+
+class ResourceMapping {
+ public:
+ // Creates a ResourceMapping using the target and overlay APKs. Setting enforce_overlayable to
+ // `false` disables all overlayable and policy enforcement: this is intended for backwards
+ // compatibility pre-Q and unit tests.
+ static Result<ResourceMapping> FromApkAssets(const ApkAssets& target_apk_assets,
+ const ApkAssets& overlay_apk_assets,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable, LogInfo& log_info);
+
+ // Retrieves the mapping of target resource id to overlay value.
+ inline TargetResourceMap GetTargetToOverlayMap() const {
+ return target_map_;
+ }
+
+ // Retrieves the mapping of overlay resource id to target resource id. This allows a reference to
+ // an overlay resource to appear as a reference to its corresponding target resource at runtime.
+ OverlayResourceMap GetOverlayToTargetMap() const;
+
+ // Retrieves the build-time package id of the target package.
+ inline uint32_t GetTargetPackageId() const {
+ return target_package_id_;
+ }
+
+ // Retrieves the build-time package id of the overlay package.
+ inline uint32_t GetOverlayPackageId() const {
+ return overlay_package_id_;
+ }
+
+ // Retrieves the offset that was added to the index of inline string overlay values so the indices
+ // do not collide with the indices of the overlay resource table string pool.
+ inline uint32_t GetStringPoolOffset() const {
+ return string_pool_offset_;
+ }
+
+ // Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
+ inline const std::pair<const uint8_t*, uint32_t> GetStringPoolData() const {
+ return std::make_pair(string_pool_data_.get(), string_pool_data_length_);
+ }
+
+ private:
+ ResourceMapping() = default;
+
+ // Apps a mapping of target resource id to the type and value of the data that overlays the
+ // target resource. The data_type is the runtime format of the data value (see
+ // Res_value::dataType). If rewrite_overlay_reference is `true` then references to an overlay
+ // resource should appear as a reference to its corresponding target resource at runtime.
+ Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
+ TargetValue::DataValue data_value, bool rewrite_overlay_reference);
+
+ // Removes the overlay value mapping for the target resource.
+ void RemoveMapping(ResourceId target_resource);
+
+ // Parses the mapping of target resources to overlay resources to generate a ResourceMapping.
+ static Result<ResourceMapping> CreateResourceMapping(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ size_t string_pool_offset,
+ const XmlParser& overlay_parser,
+ LogInfo& log_info);
+
+ // Generates a ResourceMapping that maps target resources to overlay resources by name. To overlay
+ // a target resource, a resource must exist in the overlay with the same type and entry name as
+ // the target resource.
+ static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am,
+ const AssetManager2* overlay_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package);
+
+ // Removes resources that do not pass policy or overlayable checks of the target package.
+ void FilterOverlayableResources(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies, LogInfo& log_info);
+
+ TargetResourceMap target_map_;
+ std::multimap<ResourceId, ResourceId> overlay_map_;
+
+ uint32_t target_package_id_ = 0;
+ uint32_t overlay_package_id_ = 0;
+ uint32_t string_pool_offset_ = 0;
+ uint32_t string_pool_data_length_ = 0;
+ std::unique_ptr<uint8_t[]> string_pool_data_ = nullptr;
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEMAPPING_H_
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 9a0c2ab..c643b0e 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -21,19 +21,36 @@
#include <string>
#include "androidfw/AssetManager2.h"
-#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"
-namespace android::idmap2::utils {
+namespace android::idmap2 {
+
+// use typedefs to let the compiler warn us about implicit casts
+typedef uint32_t ResourceId; // 0xpptteeee
+typedef uint8_t PackageId; // pp in 0xpptteeee
+typedef uint8_t TypeId; // tt in 0xpptteeee
+typedef uint16_t EntryId; // eeee in 0xpptteeee
+
+#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
+#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
+
+namespace utils {
+
+// Returns whether the Res_value::data_type represents a dynamic or regular resource reference.
+bool IsReference(uint8_t data_type);
+
+// Converts the Res_value::data_type to a human-readable string representation.
+StringPiece DataTypeToString(uint8_t data_type);
struct OverlayManifestInfo {
- std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
- std::string requiredSystemPropertyName; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_package; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string target_name; // NOLINT(misc-non-private-member-variables-in-classes)
+ std::string requiredSystemPropertyName; // NOLINT(misc-non-private-member-variables-in-classes)
std::string requiredSystemPropertyValue; // NOLINT(misc-non-private-member-variables-in-classes)
- bool is_static; // NOLINT(misc-non-private-member-variables-in-classes)
- int priority = -1; // NOLINT(misc-non-private-member-variables-in-classes)
+ uint32_t resource_mapping; // NOLINT(misc-non-private-member-variables-in-classes)
+ bool is_static; // NOLINT(misc-non-private-member-variables-in-classes)
+ int priority = -1; // NOLINT(misc-non-private-member-variables-in-classes)
};
Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
@@ -41,6 +58,8 @@
Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid);
-} // namespace android::idmap2::utils
+} // namespace utils
+
+} // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h
deleted file mode 100644
index dd89dee..0000000
--- a/cmds/idmap2/include/idmap2/Xml.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2018 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_INCLUDE_IDMAP2_XML_H_
-#define IDMAP2_INCLUDE_IDMAP2_XML_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "android-base/macros.h"
-#include "androidfw/ResourceTypes.h"
-#include "utils/String16.h"
-
-namespace android::idmap2 {
-
-class Xml {
- public:
- static std::unique_ptr<const Xml> Create(const uint8_t* data, size_t size, bool copyData = false);
-
- std::unique_ptr<std::map<std::string, std::string>> FindTag(const std::string& name) const;
-
- ~Xml();
-
- private:
- Xml() {
- }
-
- mutable ResXMLTree xml_;
-
- DISALLOW_COPY_AND_ASSIGN(Xml);
-};
-
-} // namespace android::idmap2
-
-#endif // IDMAP2_INCLUDE_IDMAP2_XML_H_
diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h
new file mode 100644
index 0000000..972a6d7
--- /dev/null
+++ b/cmds/idmap2/include/idmap2/XmlParser.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+#define IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+
+#include "Result.h"
+#include "android-base/macros.h"
+#include "androidfw/ResourceTypes.h"
+#include "utils/String16.h"
+
+namespace android::idmap2 {
+
+class XmlParser {
+ public:
+ using Event = ResXMLParser::event_code_t;
+ class iterator;
+
+ class Node {
+ public:
+ Event event() const;
+ std::string name() const;
+
+ Result<std::string> GetAttributeStringValue(const std::string& name) const;
+ Result<Res_value> GetAttributeValue(const std::string& name) const;
+
+ bool operator==(const Node& rhs) const;
+ bool operator!=(const Node& rhs) const;
+
+ private:
+ explicit Node(const ResXMLTree& tree);
+ Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos);
+
+ // Retrieves/Sets the position of the position of the xml parser in the xml tree.
+ ResXMLParser::ResXMLPosition get_position() const;
+ void set_position(const ResXMLParser::ResXMLPosition& pos);
+
+ // If `inner_child` is true, seek advances the parser to the first inner child of the current
+ // node. Otherwise, seek advances the parser to the following node. Returns false if there is
+ // no node to seek to.
+ bool Seek(bool inner_child);
+
+ ResXMLParser parser_;
+ friend iterator;
+ };
+
+ class iterator {
+ public:
+ iterator(const iterator& other) : iterator(other.tree_, other.iter_) {
+ }
+
+ inline iterator& operator=(const iterator& rhs) {
+ iter_.set_position(rhs.iter_.get_position());
+ return *this;
+ }
+
+ inline bool operator==(const iterator& rhs) const {
+ return iter_ == rhs.iter_;
+ }
+
+ inline bool operator!=(const iterator& rhs) const {
+ return !(*this == rhs);
+ }
+
+ inline iterator operator++() {
+ // Seek to the following xml node.
+ iter_.Seek(false /* inner_child */);
+ return *this;
+ }
+
+ iterator begin() const {
+ iterator child_it(*this);
+ // Seek to the first inner child of the current node.
+ child_it.iter_.Seek(true /* inner_child */);
+ return child_it;
+ }
+
+ iterator end() const {
+ iterator child_it = begin();
+ while (child_it.iter_.Seek(false /* inner_child */)) {
+ // Continue iterating until the end tag is found.
+ }
+
+ return child_it;
+ }
+
+ inline const Node operator*() {
+ return Node(tree_, iter_.get_position());
+ }
+
+ inline const Node* operator->() {
+ return &iter_;
+ }
+
+ private:
+ explicit iterator(const ResXMLTree& tree) : tree_(tree), iter_(Node(tree)) {
+ }
+ iterator(const ResXMLTree& tree, const Node& node)
+ : tree_(tree), iter_(Node(tree, node.get_position())) {
+ }
+
+ const ResXMLTree& tree_;
+ Node iter_;
+ friend XmlParser;
+ };
+
+ // Creates a new xml parser beginning at the first tag.
+ static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size,
+ bool copy_data = false);
+ ~XmlParser();
+
+ inline iterator tree_iterator() const {
+ return iterator(tree_);
+ }
+
+ inline const ResStringPool& get_strings() const {
+ return tree_.getStrings();
+ }
+
+ private:
+ XmlParser() = default;
+ mutable ResXMLTree tree_;
+
+ DISALLOW_COPY_AND_ASSIGN(XmlParser);
+};
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index dee2d21..255212a 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -24,6 +24,14 @@
namespace android::idmap2 {
+void BinaryStreamVisitor::Write(const void* value, size_t length) {
+ stream_.write(reinterpret_cast<const char*>(value), length);
+}
+
+void BinaryStreamVisitor::Write8(uint8_t value) {
+ stream_.write(reinterpret_cast<char*>(&value), sizeof(uint8_t));
+}
+
void BinaryStreamVisitor::Write16(uint16_t value) {
uint16_t x = htodl(value);
stream_.write(reinterpret_cast<char*>(&x), sizeof(uint16_t));
@@ -34,13 +42,21 @@
stream_.write(reinterpret_cast<char*>(&x), sizeof(uint32_t));
}
-void BinaryStreamVisitor::WriteString(const StringPiece& value) {
+void BinaryStreamVisitor::WriteString256(const StringPiece& value) {
char buf[kIdmapStringLength];
memset(buf, 0, sizeof(buf));
memcpy(buf, value.data(), std::min(value.size(), sizeof(buf)));
stream_.write(buf, sizeof(buf));
}
+void BinaryStreamVisitor::WriteString(const std::string& value) {
+ // pad with null to nearest word boundary; include at least one terminating null
+ size_t padding_size = 4 - (value.size() % 4);
+ Write32(value.size() + padding_size);
+ stream_.write(value.c_str(), value.size());
+ stream_.write("\0\0\0\0", padding_size);
+}
+
void BinaryStreamVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
// nothing to do
}
@@ -50,30 +66,35 @@
Write32(header.GetVersion());
Write32(header.GetTargetCrc());
Write32(header.GetOverlayCrc());
- WriteString(header.GetTargetPath());
- WriteString(header.GetOverlayPath());
+ Write32(header.GetFulfilledPolicies());
+ Write8(static_cast<uint8_t>(header.GetEnforceOverlayable()));
+ WriteString256(header.GetTargetPath());
+ WriteString256(header.GetOverlayPath());
+ WriteString(header.GetDebugInfo());
}
-void BinaryStreamVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
- // nothing to do
+void BinaryStreamVisitor::visit(const IdmapData& data) {
+ for (const auto& target_entry : data.GetTargetEntries()) {
+ Write32(target_entry.target_id);
+ Write8(target_entry.data_type);
+ Write32(target_entry.data_value);
+ }
+
+ for (const auto& overlay_entry : data.GetOverlayEntries()) {
+ Write32(overlay_entry.overlay_id);
+ Write32(overlay_entry.target_id);
+ }
+
+ Write(data.GetStringPoolData(), data.GetHeader()->GetStringPoolLength());
}
void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
- Write16(header.GetTargetPackageId());
- Write16(header.GetTypeCount());
-}
-
-void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& type_entry) {
- const uint16_t entryCount = type_entry.GetEntryCount();
-
- Write16(type_entry.GetTargetTypeId());
- Write16(type_entry.GetOverlayTypeId());
- Write16(entryCount);
- Write16(type_entry.GetEntryOffset());
- for (uint16_t i = 0; i < entryCount; i++) {
- EntryId entry_id = type_entry.GetEntry(i);
- Write32(entry_id != kNoEntry ? static_cast<uint32_t>(entry_id) : kPadding);
- }
+ Write8(header.GetTargetPackageId());
+ Write8(header.GetOverlayPackageId());
+ Write32(header.GetTargetEntryCount());
+ Write32(header.GetOverlayEntryCount());
+ Write32(header.GetStringPoolIndexOffset());
+ Write32(header.GetStringPoolLength());
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 4649675..23c25a7 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -30,6 +30,7 @@
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
#include "androidfw/AssetManager2.h"
+#include "idmap2/ResourceMapping.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/SysTrace.h"
@@ -41,34 +42,10 @@
namespace {
-#define EXTRACT_TYPE(resid) ((0x00ff0000 & (resid)) >> 16)
-
-#define EXTRACT_ENTRY(resid) (0x0000ffff & (resid))
-
-class MatchingResources {
- public:
- void Add(ResourceId target_resid, ResourceId overlay_resid) {
- TypeId target_typeid = EXTRACT_TYPE(target_resid);
- if (map_.find(target_typeid) == map_.end()) {
- map_.emplace(target_typeid, std::set<std::pair<ResourceId, ResourceId>>());
- }
- map_[target_typeid].insert(std::make_pair(target_resid, overlay_resid));
- }
-
- inline const std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>>& WARN_UNUSED
- Map() const {
- return map_;
- }
-
- private:
- // target type id -> set { pair { overlay entry id, overlay entry id } }
- std::map<TypeId, std::set<std::pair<ResourceId, ResourceId>>> map_;
-};
-
-bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
- uint16_t value;
- if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
- *out = dtohl(value);
+bool WARN_UNUSED Read8(std::istream& stream, uint8_t* out) {
+ uint8_t value;
+ if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint8_t))) {
+ *out = value;
return true;
}
return false;
@@ -83,8 +60,17 @@
return false;
}
+bool WARN_UNUSED ReadBuffer(std::istream& stream, std::unique_ptr<uint8_t[]>* out, size_t length) {
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[length]);
+ if (stream.read(reinterpret_cast<char*>(buffer.get()), length)) {
+ *out = std::move(buffer);
+ return true;
+ }
+ return false;
+}
+
// a string is encoded as a kIdmapStringLength char array; the array is always null-terminated
-bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) {
+bool WARN_UNUSED ReadString256(std::istream& stream, char out[kIdmapStringLength]) {
char buf[kIdmapStringLength];
memset(buf, 0, sizeof(buf));
if (!stream.read(buf, sizeof(buf))) {
@@ -97,29 +83,26 @@
return true;
}
-ResourceId NameToResid(const AssetManager2& am, const std::string& name) {
- return am.GetResourceId(name);
-}
-
-// TODO(martenkongstad): scan for package name instead of assuming package at index 0
-//
-// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
-// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
-// this assumption tends to work out. That said, the correct thing to do is to scan
-// resources.arsc for a package with a given name as read from the package manifest instead of
-// relying on a hard-coded index. This however requires storing the package name in the idmap
-// header, which in turn requires incrementing the idmap version. Because the initial version of
-// idmap2 is compatible with idmap, this will have to wait for now.
-const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
- const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
- if (packages.empty()) {
- return nullptr;
+Result<std::string> ReadString(std::istream& stream) {
+ uint32_t size;
+ if (!Read32(stream, &size)) {
+ return Error("failed to read string size");
}
- int id = packages[0]->GetPackageId();
- return loaded_arsc.GetPackageById(id);
+ if (size == 0) {
+ return std::string("");
+ }
+ std::string buf(size, '\0');
+ if (!stream.read(buf.data(), size)) {
+ return Error("failed to read string of size %u", size);
+ }
+ // buf is guaranteed to be null terminated (with enough nulls to end on a word boundary)
+ buf.resize(strlen(buf.c_str()));
+ return buf;
}
-Result<uint32_t> GetCrc(const ZipFile& zip) {
+} // namespace
+
+Result<uint32_t> GetPackageCrc(const ZipFile& zip) {
const Result<uint32_t> a = zip.Crc("resources.arsc");
const Result<uint32_t> b = zip.Crc("AndroidManifest.xml");
return a && b
@@ -127,22 +110,59 @@
: Error("failed to get CRC for \"%s\"", a ? "AndroidManifest.xml" : "resources.arsc");
}
-} // namespace
-
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_) ||
- !ReadString(stream, idmap_header->target_path_) ||
- !ReadString(stream, idmap_header->overlay_path_)) {
+ !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;
+ }
+ idmap_header->debug_info_ = std::move(*debug_str);
+
return std::move(idmap_header);
}
-Result<Unit> IdmapHeader::IsUpToDate() const {
+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", target_path);
+ }
+
+ const Result<uint32_t> target_crc = GetPackageCrc(*target_zip);
+ if (!target_crc) {
+ return Error("failed to get 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(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);
}
@@ -151,34 +171,34 @@
return Error("bad version: actual 0x%08x, expected 0x%08x", version_, kIdmapCurrentVersion);
}
- 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());
- }
-
- Result<uint32_t> target_crc = GetCrc(*target_zip);
- if (!target_crc) {
- return Error("failed to get target crc");
- }
-
- if (target_crc_ != *target_crc) {
+ if (target_crc_ != target_crc) {
return Error("bad target crc: idmap version 0x%08x, file system version 0x%08x", target_crc_,
- *target_crc);
+ 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());
- }
-
- Result<uint32_t> overlay_crc = GetCrc(*overlay_zip);
- if (!overlay_crc) {
- return Error("failed to get overlay crc");
- }
-
- if (overlay_crc_ != *overlay_crc) {
+ if (overlay_crc_ != overlay_crc) {
return Error("bad overlay crc: idmap version 0x%08x, file system version 0x%08x", overlay_crc_,
- *overlay_crc);
+ 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 (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{};
@@ -187,51 +207,48 @@
std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
- uint16_t target_package_id16;
- if (!Read16(stream, &target_package_id16) || !Read16(stream, &idmap_data_header->type_count_)) {
+ if (!Read8(stream, &idmap_data_header->target_package_id_) ||
+ !Read8(stream, &idmap_data_header->overlay_package_id_) ||
+ !Read32(stream, &idmap_data_header->target_entry_count) ||
+ !Read32(stream, &idmap_data_header->overlay_entry_count) ||
+ !Read32(stream, &idmap_data_header->string_pool_index_offset) ||
+ !Read32(stream, &idmap_data_header->string_pool_len)) {
return nullptr;
}
- idmap_data_header->target_package_id_ = target_package_id16;
return std::move(idmap_data_header);
}
-std::unique_ptr<const IdmapData::TypeEntry> IdmapData::TypeEntry::FromBinaryStream(
- std::istream& stream) {
- std::unique_ptr<IdmapData::TypeEntry> data(new IdmapData::TypeEntry());
- uint16_t target_type16;
- uint16_t overlay_type16;
- uint16_t entry_count;
- if (!Read16(stream, &target_type16) || !Read16(stream, &overlay_type16) ||
- !Read16(stream, &entry_count) || !Read16(stream, &data->entry_offset_)) {
- return nullptr;
- }
- data->target_type_id_ = target_type16;
- data->overlay_type_id_ = overlay_type16;
- for (uint16_t i = 0; i < entry_count; i++) {
- ResourceId resid;
- if (!Read32(stream, &resid)) {
- return nullptr;
- }
- data->entries_.push_back(resid);
- }
-
- return std::move(data);
-}
-
std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData> data(new IdmapData());
data->header_ = IdmapData::Header::FromBinaryStream(stream);
if (!data->header_) {
return nullptr;
}
- for (size_t type_count = 0; type_count < data->header_->GetTypeCount(); type_count++) {
- std::unique_ptr<const TypeEntry> type = IdmapData::TypeEntry::FromBinaryStream(stream);
- if (!type) {
+ // Read the mapping of target resource id to overlay resource value.
+ for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) {
+ TargetEntry target_entry{};
+ if (!Read32(stream, &target_entry.target_id) || !Read8(stream, &target_entry.data_type) ||
+ !Read32(stream, &target_entry.data_value)) {
return nullptr;
}
- data->type_entries_.push_back(std::move(type));
+ data->target_entries_.emplace_back(target_entry);
}
+
+ // Read the mapping of overlay resource id to target resource id.
+ for (size_t i = 0; i < data->header_->GetOverlayEntryCount(); i++) {
+ OverlayEntry overlay_entry{};
+ if (!Read32(stream, &overlay_entry.overlay_id) || !Read32(stream, &overlay_entry.target_id)) {
+ return nullptr;
+ }
+ data->overlay_entries_.emplace_back(overlay_entry);
+ }
+
+ // Read raw string pool bytes.
+ if (!ReadBuffer(stream, &data->string_pool_, data->header_->string_pool_len)) {
+ return nullptr;
+ }
+
return std::move(data);
}
@@ -266,95 +283,45 @@
return {std::move(idmap)};
}
-std::string ConcatPolicies(const std::vector<std::string>& policies) {
- std::string message;
- for (const std::string& policy : policies) {
- if (!message.empty()) {
- message.append("|");
- }
- message.append(policy);
+Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
+ const ResourceMapping& resource_mapping) {
+ if (resource_mapping.GetTargetToOverlayMap().empty()) {
+ return Error("no resources were overlaid");
}
- return message;
+ std::unique_ptr<IdmapData> data(new IdmapData());
+ for (const auto& mappings : resource_mapping.GetTargetToOverlayMap()) {
+ data->target_entries_.emplace_back(IdmapData::TargetEntry{
+ mappings.first, mappings.second.data_type, mappings.second.data_value});
+ }
+
+ for (const auto& mappings : resource_mapping.GetOverlayToTargetMap()) {
+ data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mappings.first, mappings.second});
+ }
+
+ std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
+ data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
+ data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId();
+ data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
+ data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size());
+ data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset();
+
+ const auto string_pool_data = resource_mapping.GetStringPoolData();
+ data_header->string_pool_len = string_pool_data.second;
+ data->string_pool_ = std::unique_ptr<uint8_t[]>(new uint8_t[data_header->string_pool_len]);
+ memcpy(data->string_pool_.get(), string_pool_data.first, data_header->string_pool_len);
+
+ data->header_ = std::move(data_header);
+ return {std::move(data)};
}
-Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
- const utils::OverlayManifestInfo& overlay_info,
- const PolicyBitmask& fulfilled_policies, const ResourceId& resid) {
- static constexpr const PolicyBitmask sDefaultPolicies =
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION |
- PolicyFlags::POLICY_PRODUCT_PARTITION | PolicyFlags::POLICY_SIGNATURE;
-
- // If the resource does not have an overlayable definition, allow the resource to be overlaid if
- // the overlay is preinstalled or signed with the same signature as the target.
- if (!target_package.DefinesOverlayable()) {
- return (sDefaultPolicies & fulfilled_policies) != 0
- ? Result<Unit>({})
- : Error(
- "overlay must be preinstalled or signed with the same signature as the "
- "target");
- }
-
- const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid);
- if (overlayable_info == nullptr) {
- // Do not allow non-overlayable resources to be overlaid.
- return Error("resource has no overlayable declaration");
- }
-
- if (overlay_info.target_name != overlayable_info->name) {
- // If the overlay supplies a target overlayable name, the resource must belong to the
- // overlayable defined with the specified name to be overlaid.
- return Error("<overlay> android:targetName '%s' does not match overlayable name '%s'",
- overlay_info.target_name.c_str(), overlayable_info->name.c_str());
- }
-
- // Enforce policy restrictions if the resource is declared as overlayable.
- if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
- return Error("overlay with policies '%s' does not fulfill any overlayable policies '%s'",
- ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
- ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
- }
-
- return Result<Unit>({});
-}
-
-Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& target_apk_path,
- const ApkAssets& target_apk_assets,
- const std::string& overlay_apk_path,
+Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const ApkAssets& target_apk_assets,
const ApkAssets& overlay_apk_assets,
const PolicyBitmask& fulfilled_policies,
bool enforce_overlayable) {
SYSTRACE << "Idmap::FromApkAssets";
- AssetManager2 target_asset_manager;
- if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true, false)) {
- return Error("failed to create target asset manager");
- }
-
- AssetManager2 overlay_asset_manager;
- if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true, false)) {
- return Error("failed to create overlay asset manager");
- }
-
- const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
- if (target_arsc == nullptr) {
- return Error("failed to load target resources.arsc");
- }
-
- const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
- if (overlay_arsc == nullptr) {
- return Error("failed to load overlay resources.arsc");
- }
-
- const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
- if (target_pkg == nullptr) {
- return Error("failed to load target package from resources.arsc");
- }
-
- const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
- if (overlay_pkg == nullptr) {
- return Error("failed to load overlay package from resources.arsc");
- }
+ const std::string& target_apk_path = target_apk_assets.GetPath();
+ const std::string& overlay_apk_path = overlay_apk_assets.GetPath();
const std::unique_ptr<const ZipFile> target_zip = ZipFile::Open(target_apk_path);
if (!target_zip) {
@@ -366,27 +333,25 @@
return Error("failed to open overlay as zip");
}
- auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
- if (!overlay_info) {
- return overlay_info.GetError();
- }
-
std::unique_ptr<IdmapHeader> header(new IdmapHeader());
header->magic_ = kIdmapMagic;
header->version_ = kIdmapCurrentVersion;
- Result<uint32_t> crc = GetCrc(*target_zip);
+ Result<uint32_t> crc = GetPackageCrc(*target_zip);
if (!crc) {
return Error(crc.GetError(), "failed to get zip CRC for target");
}
header->target_crc_ = *crc;
- crc = GetCrc(*overlay_zip);
+ crc = GetPackageCrc(*overlay_zip);
if (!crc) {
return Error(crc.GetError(), "failed to get zip CRC for overlay");
}
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_));
@@ -395,78 +360,34 @@
memcpy(header->target_path_, target_apk_path.data(), target_apk_path.size());
if (overlay_apk_path.size() > sizeof(header->overlay_path_)) {
- return Error("overlay apk path \"%s\" longer than maximum size %zu", target_apk_path.c_str(),
+ return Error("overlay apk path \"%s\" longer than maximum size %zu", overlay_apk_path.c_str(),
sizeof(header->target_path_));
}
memset(header->overlay_path_, 0, sizeof(header->overlay_path_));
memcpy(header->overlay_path_, overlay_apk_path.data(), overlay_apk_path.size());
+ auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path);
+ if (!overlay_info) {
+ return overlay_info.GetError();
+ }
+
+ LogInfo log_info;
+ auto resource_mapping =
+ ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *overlay_info,
+ fulfilled_policies, enforce_overlayable, log_info);
+ if (!resource_mapping) {
+ return resource_mapping.GetError();
+ }
+
+ auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping);
+ if (!idmap_data) {
+ return idmap_data.GetError();
+ }
+
std::unique_ptr<Idmap> idmap(new Idmap());
+ header->debug_info_ = log_info.GetString();
idmap->header_ = std::move(header);
-
- // find the resources that exist in both packages
- MatchingResources matching_resources;
- const auto end = overlay_pkg->end();
- for (auto iter = overlay_pkg->begin(); iter != end; ++iter) {
- const ResourceId overlay_resid = *iter;
- Result<std::string> name = utils::ResToTypeEntryName(overlay_asset_manager, overlay_resid);
- if (!name) {
- continue;
- }
- // prepend "<package>:" to turn name into "<package>:<type>/<name>"
- const std::string full_name =
- base::StringPrintf("%s:%s", target_pkg->GetPackageName().c_str(), name->c_str());
- const ResourceId target_resid = NameToResid(target_asset_manager, full_name);
- if (target_resid == 0) {
- continue;
- }
-
- if (enforce_overlayable) {
- Result<Unit> success =
- CheckOverlayable(*target_pkg, *overlay_info, fulfilled_policies, target_resid);
- if (!success) {
- LOG(WARNING) << "overlay \"" << overlay_apk_path
- << "\" is not allowed to overlay resource \"" << full_name
- << "\": " << success.GetErrorMessage();
- continue;
- }
- }
-
- matching_resources.Add(target_resid, overlay_resid);
- }
-
- if (matching_resources.Map().empty()) {
- return Error("overlay \"%s\" does not successfully overlay any resource",
- overlay_apk_path.c_str());
- }
-
- // encode idmap data
- std::unique_ptr<IdmapData> data(new IdmapData());
- const auto types_end = matching_resources.Map().cend();
- for (auto ti = matching_resources.Map().cbegin(); ti != types_end; ++ti) {
- auto ei = ti->second.cbegin();
- std::unique_ptr<IdmapData::TypeEntry> type(new IdmapData::TypeEntry());
- type->target_type_id_ = EXTRACT_TYPE(ei->first);
- type->overlay_type_id_ = EXTRACT_TYPE(ei->second);
- type->entry_offset_ = EXTRACT_ENTRY(ei->first);
- EntryId last_target_entry = kNoEntry;
- for (; ei != ti->second.cend(); ++ei) {
- if (last_target_entry != kNoEntry) {
- int count = EXTRACT_ENTRY(ei->first) - last_target_entry - 1;
- type->entries_.insert(type->entries_.end(), count, kNoEntry);
- }
- type->entries_.push_back(EXTRACT_ENTRY(ei->second));
- last_target_entry = EXTRACT_ENTRY(ei->first);
- }
- data->type_entries_.push_back(std::move(type));
- }
-
- std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
- data_header->target_package_id_ = target_pkg->GetPackageId();
- data_header->type_count_ = data->type_entries_.size();
- data->header_ = std::move(data_header);
-
- idmap->data_.push_back(std::move(data));
+ idmap->data_.push_back(std::move(*idmap_data));
return {std::move(idmap)};
}
@@ -481,25 +402,16 @@
v->visit(*this);
}
-void IdmapData::TypeEntry::accept(Visitor* v) const {
- assert(v != nullptr);
- v->visit(*this);
-}
-
void IdmapData::accept(Visitor* v) const {
assert(v != nullptr);
- v->visit(*this);
header_->accept(v);
- auto end = type_entries_.cend();
- for (auto iter = type_entries_.cbegin(); iter != end; ++iter) {
- (*iter)->accept(v);
- }
+ v->visit(*this);
}
void Idmap::accept(Visitor* v) const {
assert(v != nullptr);
- v->visit(*this);
header_->accept(v);
+ v->visit(*this);
auto end = data_.cend();
for (auto iter = data_.cbegin(); iter != end; ++iter) {
(*iter)->accept(v);
diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp
deleted file mode 100644
index 495fe61..0000000
--- a/cmds/idmap2/libidmap2/Policies.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "idmap2/Policies.h"
-
-#include <iterator>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "androidfw/ResourceTypes.h"
-#include "idmap2/Idmap.h"
-#include "idmap2/Result.h"
-
-namespace android::idmap2 {
-
-Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies) {
- static const std::unordered_map<android::StringPiece, PolicyFlags> kStringToFlag = {
- {kPolicyOdm, PolicyFlags::POLICY_ODM_PARTITION},
- {kPolicyOem, PolicyFlags::POLICY_OEM_PARTITION},
- {kPolicyPublic, PolicyFlags::POLICY_PUBLIC},
- {kPolicyProduct, PolicyFlags::POLICY_PRODUCT_PARTITION},
- {kPolicySignature, PolicyFlags::POLICY_SIGNATURE},
- {kPolicySystem, PolicyFlags::POLICY_SYSTEM_PARTITION},
- {kPolicyVendor, PolicyFlags::POLICY_VENDOR_PARTITION},
- };
-
- PolicyBitmask bitmask = 0;
- for (const std::string& policy : policies) {
- const auto iter = kStringToFlag.find(policy);
- if (iter != kStringToFlag.end()) {
- bitmask |= iter->second;
- } else {
- return Error("unknown policy \"%s\"", policy.c_str());
- }
- }
-
- return Result<PolicyBitmask>(bitmask);
-}
-
-std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask) {
- std::vector<std::string> policies;
-
- if ((bitmask & PolicyFlags::POLICY_ODM_PARTITION) != 0) {
- policies.emplace_back(kPolicyOdm);
- }
-
- if ((bitmask & PolicyFlags::POLICY_OEM_PARTITION) != 0) {
- policies.emplace_back(kPolicyOem);
- }
-
- if ((bitmask & PolicyFlags::POLICY_PUBLIC) != 0) {
- policies.emplace_back(kPolicyPublic);
- }
-
- if ((bitmask & PolicyFlags::POLICY_PRODUCT_PARTITION) != 0) {
- policies.emplace_back(kPolicyProduct);
- }
-
- if ((bitmask & PolicyFlags::POLICY_SIGNATURE) != 0) {
- policies.emplace_back(kPolicySignature);
- }
-
- if ((bitmask & PolicyFlags::POLICY_SYSTEM_PARTITION) != 0) {
- policies.emplace_back(kPolicySystem);
- }
-
- if ((bitmask & PolicyFlags::POLICY_VENDOR_PARTITION) != 0) {
- policies.emplace_back(kPolicyVendor);
- }
-
- return policies;
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/PolicyUtils.cpp b/cmds/idmap2/libidmap2/PolicyUtils.cpp
new file mode 100644
index 0000000..fc5182a
--- /dev/null
+++ b/cmds/idmap2/libidmap2/PolicyUtils.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include "include/idmap2/PolicyUtils.h"
+
+#include <sstream>
+
+#include "android-base/strings.h"
+#include "idmap2/Policies.h"
+
+using android::idmap2::policy::kPolicyStringToFlag;
+
+namespace android::idmap2::utils {
+
+Result<PolicyBitmask> PoliciesToBitmaskResult(const std::vector<std::string>& policies) {
+ std::vector<std::string> unknown_policies;
+ PolicyBitmask bitmask = 0;
+ for (const std::string& policy : policies) {
+ const auto result = std::find_if(kPolicyStringToFlag.begin(), kPolicyStringToFlag.end(),
+ [policy](const auto& it) { return policy == it.first; });
+ if (result != kPolicyStringToFlag.end()) {
+ bitmask |= result->second;
+ } else {
+ unknown_policies.emplace_back(policy.empty() ? "empty" : policy);
+ }
+ }
+
+ if (unknown_policies.empty()) {
+ return Result<PolicyBitmask>(bitmask);
+ }
+
+ auto prefix = unknown_policies.size() == 1 ? "policy" : "policies";
+ return Error("unknown %s: \"%s\"", prefix, android::base::Join(unknown_policies, ",").c_str());
+}
+
+std::vector<std::string> BitmaskToPolicies(const PolicyBitmask& bitmask) {
+ std::vector<std::string> policies;
+
+ for (const auto& policy : kPolicyStringToFlag) {
+ if ((bitmask & policy.second) != 0) {
+ policies.emplace_back(policy.first.to_string());
+ }
+ }
+
+ return policies;
+}
+
+} // namespace android::idmap2::utils
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index fbf2c77..63ee8a6 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -16,6 +16,7 @@
#include "idmap2/PrettyPrintVisitor.h"
+#include <istream>
#include <string>
#include "android-base/macros.h"
@@ -28,42 +29,59 @@
#define RESID(pkg, type, entry) (((pkg) << 24) | ((type) << 16) | (entry))
+#define TAB " "
+
void PrettyPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
}
void PrettyPrintVisitor::visit(const IdmapHeader& header) {
- stream_ << "target apk path : " << header.GetTargetPath() << std::endl
- << "overlay apk path : " << header.GetOverlayPath() << std::endl;
+ stream_ << "Paths:" << std::endl
+ << TAB "target apk path : " << header.GetTargetPath() << std::endl
+ << TAB "overlay apk path : " << header.GetOverlayPath() << std::endl;
+ const std::string& debug = header.GetDebugInfo();
+ if (!debug.empty()) {
+ std::istringstream debug_stream(debug);
+ std::string line;
+ stream_ << "Debug info:" << std::endl;
+ while (std::getline(debug_stream, line)) {
+ stream_ << TAB << line << std::endl;
+ }
+ }
target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
if (target_apk_) {
target_am_.SetApkAssets({target_apk_.get()});
}
-}
-
-void PrettyPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+ stream_ << "Mapping:" << std::endl;
}
void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) {
- last_seen_package_id_ = header.GetTargetPackageId();
}
-void PrettyPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
+void PrettyPrintVisitor::visit(const IdmapData& data) {
const bool target_package_loaded = !target_am_.GetApkAssets().empty();
- for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
- const EntryId entry = type_entry.GetEntry(i);
- if (entry == kNoEntry) {
- continue;
+ const ResStringPool string_pool(data.GetStringPoolData(),
+ data.GetHeader()->GetStringPoolLength());
+ const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset();
+
+ for (auto& target_entry : data.GetTargetEntries()) {
+ stream_ << TAB << base::StringPrintf("0x%08x ->", target_entry.target_id);
+
+ if (target_entry.data_type != Res_value::TYPE_REFERENCE &&
+ target_entry.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ stream_ << " " << utils::DataTypeToString(target_entry.data_type);
}
- const ResourceId target_resid =
- RESID(last_seen_package_id_, type_entry.GetTargetTypeId(), type_entry.GetEntryOffset() + i);
- const ResourceId overlay_resid =
- RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
+ if (target_entry.data_type == Res_value::TYPE_STRING) {
+ stream_ << " \""
+ << string_pool.string8ObjectAt(target_entry.data_value - string_pool_offset).c_str()
+ << "\"";
+ } else {
+ stream_ << " " << base::StringPrintf("0x%08x", target_entry.data_value);
+ }
- stream_ << base::StringPrintf("0x%08x -> 0x%08x", target_resid, overlay_resid);
if (target_package_loaded) {
- Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_resid);
+ Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
if (name) {
stream_ << " " << *name;
}
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index dd14fd4..3f62a2a 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -16,22 +16,31 @@
#include "idmap2/RawPrintVisitor.h"
+#include <algorithm>
#include <cstdarg>
#include <string>
#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 {
+
+size_t StringSizeWhenEncoded(const std::string& s) {
+ size_t null_bytes = 4 - (s.size() % 4);
+ return sizeof(uint32_t) + s.size() + null_bytes;
+}
+
+} // namespace
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) {
}
@@ -40,53 +49,105 @@
print(header.GetVersion(), "version");
print(header.GetTargetCrc(), "target crc");
print(header.GetOverlayCrc(), "overlay crc");
- print(header.GetTargetPath().to_string(), "target path");
- print(header.GetOverlayPath().to_string(), "overlay path");
+ 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");
target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
if (target_apk_) {
target_am_.SetApkAssets({target_apk_.get()});
}
+
+ overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string());
+ if (overlay_apk_) {
+ overlay_am_.SetApkAssets({overlay_apk_.get()});
+ }
}
void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
+ const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+ const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
+
+ for (auto& target_entry : data.GetTargetEntries()) {
+ Result<std::string> target_name(Error(""));
+ if (target_package_loaded) {
+ target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
+ }
+ if (target_name) {
+ print(target_entry.target_id, "target id: %s", target_name->c_str());
+ } else {
+ print(target_entry.target_id, "target id");
+ }
+
+ print(target_entry.data_type, "type: %s",
+ utils::DataTypeToString(target_entry.data_type).data());
+
+ Result<std::string> overlay_name(Error(""));
+ if (overlay_package_loaded && (target_entry.data_type == Res_value::TYPE_REFERENCE ||
+ target_entry.data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+ overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.data_value);
+ }
+ if (overlay_name) {
+ print(target_entry.data_value, "value: %s", overlay_name->c_str());
+ } else {
+ print(target_entry.data_value, "value");
+ }
+ }
+
+ for (auto& overlay_entry : data.GetOverlayEntries()) {
+ Result<std::string> overlay_name(Error(""));
+ if (overlay_package_loaded) {
+ overlay_name = utils::ResToTypeEntryName(overlay_am_, overlay_entry.overlay_id);
+ }
+
+ if (overlay_name) {
+ print(overlay_entry.overlay_id, "overlay id: %s", overlay_name->c_str());
+ } else {
+ print(overlay_entry.overlay_id, "overlay id");
+ }
+
+ Result<std::string> target_name(Error(""));
+ if (target_package_loaded) {
+ target_name = utils::ResToTypeEntryName(target_am_, overlay_entry.target_id);
+ }
+
+ if (target_name) {
+ print(overlay_entry.target_id, "target id: %s", target_name->c_str());
+ } else {
+ print(overlay_entry.target_id, "target id");
+ }
+ }
+
+ const size_t string_pool_length = data.GetHeader()->GetStringPoolLength();
+ if (string_pool_length > 0) {
+ print_raw(string_pool_length, "%zu raw string pool bytes", string_pool_length);
+ }
}
void RawPrintVisitor::visit(const IdmapData::Header& header) {
- print(static_cast<uint16_t>(header.GetTargetPackageId()), "target package id");
- print(header.GetTypeCount(), "type count");
- last_seen_package_id_ = header.GetTargetPackageId();
+ print(header.GetTargetPackageId(), "target package id");
+ print(header.GetOverlayPackageId(), "overlay package id");
+ print(header.GetTargetEntryCount(), "target entry count");
+ print(header.GetOverlayEntryCount(), "overlay entry count");
+ print(header.GetStringPoolIndexOffset(), "string pool index offset");
+ print(header.GetStringPoolLength(), "string pool byte length");
}
-void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) {
- const bool target_package_loaded = !target_am_.GetApkAssets().empty();
+// NOLINTNEXTLINE(cert-dcl50-cpp)
+void RawPrintVisitor::print(uint8_t value, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string comment;
+ base::StringAppendV(&comment, fmt, ap);
+ va_end(ap);
- print(static_cast<uint16_t>(type_entry.GetTargetTypeId()), "target type");
- print(static_cast<uint16_t>(type_entry.GetOverlayTypeId()), "overlay type");
- print(static_cast<uint16_t>(type_entry.GetEntryCount()), "entry count");
- print(static_cast<uint16_t>(type_entry.GetEntryOffset()), "entry offset");
+ stream_ << base::StringPrintf("%08zx: %02x", offset_, value) << " " << comment
+ << std::endl;
- for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) {
- const EntryId entry = type_entry.GetEntry(i);
- if (entry == kNoEntry) {
- print(kPadding, "no entry");
- } else {
- const ResourceId target_resid = RESID(last_seen_package_id_, type_entry.GetTargetTypeId(),
- type_entry.GetEntryOffset() + i);
- const ResourceId overlay_resid =
- RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry);
- Result<std::string> name(Error(""));
- if (target_package_loaded) {
- name = utils::ResToTypeEntryName(target_am_, target_resid);
- }
- if (name) {
- print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x %s", target_resid, overlay_resid,
- name->c_str());
- } else {
- print(static_cast<uint32_t>(entry), "0x%08x -> 0x%08x", target_resid, overlay_resid);
- }
- }
- }
+ offset_ += sizeof(uint8_t);
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
@@ -116,17 +177,30 @@
}
// NOLINTNEXTLINE(cert-dcl50-cpp)
-void RawPrintVisitor::print(const std::string& value, const char* fmt, ...) {
+void RawPrintVisitor::print(const std::string& value, size_t encoded_size, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
std::string comment;
base::StringAppendV(&comment, fmt, ap);
va_end(ap);
- stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << ": " << value
+ stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << ": " << value
<< std::endl;
- offset_ += kIdmapStringLength;
+ offset_ += encoded_size;
+}
+
+// NOLINTNEXTLINE(cert-dcl50-cpp)
+void RawPrintVisitor::print_raw(uint32_t length, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string comment;
+ base::StringAppendV(&comment, fmt, ap);
+ va_end(ap);
+
+ stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << std::endl;
+
+ offset_ += length;
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
new file mode 100644
index 0000000..34589a1
--- /dev/null
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "idmap2/ResourceMapping.h"
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
+#include "idmap2/PolicyUtils.h"
+#include "idmap2/ResourceUtils.h"
+
+using android::base::StringPrintf;
+using android::idmap2::utils::BitmaskToPolicies;
+using android::idmap2::utils::IsReference;
+using android::idmap2::utils::ResToTypeEntryName;
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
+namespace android::idmap2 {
+
+namespace {
+
+#define REWRITE_PACKAGE(resid, package_id) \
+ (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
+#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
+
+std::string ConcatPolicies(const std::vector<std::string>& policies) {
+ std::string message;
+ for (const std::string& policy : policies) {
+ if (!message.empty()) {
+ message.append("|");
+ }
+ message.append(policy);
+ }
+
+ return message;
+}
+
+Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ const ResourceId& target_resource) {
+ static constexpr const PolicyBitmask sDefaultPolicies =
+ PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
+ PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE;
+
+ // If the resource does not have an overlayable definition, allow the resource to be overlaid if
+ // the overlay is preinstalled or signed with the same signature as the target.
+ if (!target_package.DefinesOverlayable()) {
+ return (sDefaultPolicies & fulfilled_policies) != 0
+ ? Result<Unit>({})
+ : Error(
+ "overlay must be preinstalled or signed with the same signature as the "
+ "target");
+ }
+
+ const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
+ if (overlayable_info == nullptr) {
+ // Do not allow non-overlayable resources to be overlaid.
+ return Error("target resource has no overlayable declaration");
+ }
+
+ if (overlay_info.target_name != overlayable_info->name) {
+ // If the overlay supplies a target overlayable name, the resource must belong to the
+ // overlayable defined with the specified name to be overlaid.
+ return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")",
+ overlay_info.target_name.c_str(), overlayable_info->name.c_str());
+ }
+
+ // Enforce policy restrictions if the resource is declared as overlayable.
+ if ((overlayable_info->policy_flags & fulfilled_policies) == 0) {
+ return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
+ ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
+ ConcatPolicies(BitmaskToPolicies(overlayable_info->policy_flags)).c_str());
+ }
+
+ return Result<Unit>({});
+}
+
+// TODO(martenkongstad): scan for package name instead of assuming package at index 0
+//
+// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
+// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
+// this assumption tends to work out. That said, the correct thing to do is to scan
+// resources.arsc for a package with a given name as read from the package manifest instead of
+// relying on a hard-coded index. This however requires storing the package name in the idmap
+// header, which in turn requires incrementing the idmap version. Because the initial version of
+// idmap2 is compatible with idmap, this will have to wait for now.
+const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) {
+ const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc.GetPackages();
+ if (packages.empty()) {
+ return nullptr;
+ }
+ int id = packages[0]->GetPackageId();
+ return loaded_arsc.GetPackageById(id);
+}
+
+Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id,
+ const AssetManager2& asset_manager) {
+ Res_value value{};
+ ResTable_config selected_config{};
+ uint32_t flags;
+ auto cookie =
+ asset_manager.GetResource(resource_id, /* may_be_bag */ false,
+ /* density_override */ 0U, &value, &selected_config, &flags);
+ if (cookie == kInvalidCookie) {
+ return Error("failed to find resource for id 0x%08x", resource_id);
+ }
+
+ if (value.dataType != Res_value::TYPE_STRING) {
+ return Error("resource for is 0x%08x is not a file", resource_id);
+ }
+
+ auto string_pool = asset_manager.GetStringPoolForCookie(cookie);
+ size_t len;
+ auto file_path16 = string_pool->stringAt(value.data, &len);
+ if (file_path16 == nullptr) {
+ return Error("failed to find string for index %d", value.data);
+ }
+
+ // Load the overlay resource mappings from the file specified using android:resourcesMap.
+ auto file_path = String8(String16(file_path16));
+ auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return Error("file \"%s\" not found", file_path.c_str());
+ }
+
+ return asset;
+}
+
+} // namespace
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ size_t string_pool_offset,
+ const XmlParser& overlay_parser,
+ LogInfo& log_info) {
+ ResourceMapping resource_mapping;
+ auto root_it = overlay_parser.tree_iterator();
+ if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
+ return Error("root element is not <overlay> tag");
+ }
+
+ const uint8_t target_package_id = target_package->GetPackageId();
+ const uint8_t overlay_package_id = overlay_package->GetPackageId();
+ auto overlay_it_end = root_it.end();
+ for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
+ if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
+ return Error("failed to parse overlay xml document");
+ }
+
+ if (overlay_it->event() != XmlParser::Event::START_TAG) {
+ continue;
+ }
+
+ if (overlay_it->name() != "item") {
+ return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
+ }
+
+ Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
+ if (!target_resource) {
+ return Error(R"(<item> tag missing expected attribute "target")");
+ }
+
+ Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
+ if (!overlay_resource) {
+ return Error(R"(<item> tag missing expected attribute "value")");
+ }
+
+ ResourceId target_id =
+ target_am->GetResourceId(*target_resource, "", target_package->GetPackageName());
+ if (target_id == 0U) {
+ log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource
+ << "\" in target resources");
+ continue;
+ }
+
+ // Retrieve the compile-time resource id of the target resource.
+ target_id = REWRITE_PACKAGE(target_id, target_package_id);
+
+ if (overlay_resource->dataType == Res_value::TYPE_STRING) {
+ overlay_resource->data += string_pool_offset;
+ }
+
+ // Only rewrite resources defined within the overlay package to their corresponding target
+ // resource ids at runtime.
+ bool rewrite_overlay_reference =
+ IsReference(overlay_resource->dataType)
+ ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
+ : false;
+
+ if (rewrite_overlay_reference) {
+ overlay_resource->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
+ }
+
+ resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data,
+ rewrite_overlay_reference);
+ }
+
+ return resource_mapping;
+}
+
+Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
+ const AssetManager2* target_am, const AssetManager2* overlay_am,
+ const LoadedPackage* target_package, const LoadedPackage* overlay_package) {
+ ResourceMapping resource_mapping;
+ const uint8_t target_package_id = target_package->GetPackageId();
+ const auto end = overlay_package->end();
+ for (auto iter = overlay_package->begin(); iter != end; ++iter) {
+ const ResourceId overlay_resid = *iter;
+ Result<std::string> name = utils::ResToTypeEntryName(*overlay_am, overlay_resid);
+ if (!name) {
+ continue;
+ }
+
+ // Find the resource with the same type and entry name within the target package.
+ const std::string full_name =
+ base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str());
+ ResourceId target_resource = target_am->GetResourceId(full_name);
+ if (target_resource == 0U) {
+ continue;
+ }
+
+ // Retrieve the compile-time resource id of the target resource.
+ target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
+
+ resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid,
+ /* rewrite_overlay_reference */ false);
+ }
+
+ return resource_mapping;
+}
+
+void ResourceMapping::FilterOverlayableResources(const AssetManager2* target_am,
+ const LoadedPackage* target_package,
+ const LoadedPackage* overlay_package,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ LogInfo& log_info) {
+ std::set<ResourceId> remove_ids;
+ for (const auto& target_map : target_map_) {
+ const ResourceId target_resid = target_map.first;
+ Result<Unit> success =
+ CheckOverlayable(*target_package, overlay_info, fulfilled_policies, target_resid);
+ if (success) {
+ continue;
+ }
+
+ // Attempting to overlay a resource that is not allowed to be overlaid is treated as a
+ // warning.
+ Result<std::string> name = utils::ResToTypeEntryName(*target_am, target_resid);
+ if (!name) {
+ name = StringPrintf("0x%08x", target_resid);
+ }
+
+ log_info.Warning(LogMessage() << "overlay \"" << overlay_package->GetPackageName()
+ << "\" is not allowed to overlay resource \"" << *name
+ << "\" in target: " << success.GetErrorMessage());
+
+ remove_ids.insert(target_resid);
+ }
+
+ for (const ResourceId target_resid : remove_ids) {
+ RemoveMapping(target_resid);
+ }
+}
+
+Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_apk_assets,
+ const ApkAssets& overlay_apk_assets,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable,
+ LogInfo& log_info) {
+ AssetManager2 target_asset_manager;
+ if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */,
+ false /* filter_incompatible_configs*/)) {
+ return Error("failed to create target asset manager");
+ }
+
+ AssetManager2 overlay_asset_manager;
+ if (!overlay_asset_manager.SetApkAssets({&overlay_apk_assets}, true /* invalidate_caches */,
+ false /* filter_incompatible_configs */)) {
+ return Error("failed to create overlay asset manager");
+ }
+
+ const LoadedArsc* target_arsc = target_apk_assets.GetLoadedArsc();
+ if (target_arsc == nullptr) {
+ return Error("failed to load target resources.arsc");
+ }
+
+ const LoadedArsc* overlay_arsc = overlay_apk_assets.GetLoadedArsc();
+ if (overlay_arsc == nullptr) {
+ return Error("failed to load overlay resources.arsc");
+ }
+
+ const LoadedPackage* target_pkg = GetPackageAtIndex0(*target_arsc);
+ if (target_pkg == nullptr) {
+ return Error("failed to load target package from resources.arsc");
+ }
+
+ const LoadedPackage* overlay_pkg = GetPackageAtIndex0(*overlay_arsc);
+ if (overlay_pkg == nullptr) {
+ return Error("failed to load overlay package from resources.arsc");
+ }
+
+ size_t string_pool_data_length = 0U;
+ size_t string_pool_offset = 0U;
+ std::unique_ptr<uint8_t[]> string_pool_data;
+ Result<ResourceMapping> resource_mapping = {{}};
+ if (overlay_info.resource_mapping != 0U) {
+ // Use the dynamic reference table to find the assigned resource id of the map xml.
+ const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0);
+ uint32_t resource_mapping_id = overlay_info.resource_mapping;
+ ref_table->lookupResourceId(&resource_mapping_id);
+
+ // Load the overlay resource mappings from the file specified using android:resourcesMap.
+ auto asset = OpenNonAssetFromResource(resource_mapping_id, overlay_asset_manager);
+ if (!asset) {
+ return Error("failed opening xml for android:resourcesMap: %s",
+ asset.GetErrorMessage().c_str());
+ }
+
+ auto parser =
+ XmlParser::Create((*asset)->getBuffer(true /* wordAligned*/), (*asset)->getLength());
+ if (!parser) {
+ return Error("failed opening ResXMLTree");
+ }
+
+ // Copy the xml string pool data before the parse goes out of scope.
+ auto& string_pool = (*parser)->get_strings();
+ string_pool_data_length = string_pool.bytes();
+ string_pool_data.reset(new uint8_t[string_pool_data_length]);
+ memcpy(string_pool_data.get(), string_pool.data(), string_pool_data_length);
+
+ // Offset string indices by the size of the overlay resource table string pool.
+ string_pool_offset = overlay_arsc->GetStringPool()->size();
+
+ resource_mapping = CreateResourceMapping(&target_asset_manager, target_pkg, overlay_pkg,
+ string_pool_offset, *(*parser), log_info);
+ } else {
+ // If no file is specified using android:resourcesMap, it is assumed that the overlay only
+ // defines resources intended to override target resources of the same type and name.
+ resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager,
+ target_pkg, overlay_pkg);
+ }
+
+ if (!resource_mapping) {
+ return resource_mapping.GetError();
+ }
+
+ if (enforce_overlayable) {
+ // Filter out resources the overlay is not allowed to override.
+ (*resource_mapping)
+ .FilterOverlayableResources(&target_asset_manager, target_pkg, overlay_pkg, overlay_info,
+ fulfilled_policies, log_info);
+ }
+
+ resource_mapping->target_package_id_ = target_pkg->GetPackageId();
+ resource_mapping->overlay_package_id_ = overlay_pkg->GetPackageId();
+ resource_mapping->string_pool_offset_ = string_pool_offset;
+ resource_mapping->string_pool_data_ = std::move(string_pool_data);
+ resource_mapping->string_pool_data_length_ = string_pool_data_length;
+ return std::move(*resource_mapping);
+}
+
+OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
+ // An overlay resource can override multiple target resources at once. Rewrite the overlay
+ // resource as the first target resource it overrides.
+ OverlayResourceMap map;
+ for (const auto& mappings : overlay_map_) {
+ map.insert(std::make_pair(mappings.first, mappings.second));
+ }
+ return map;
+}
+
+Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
+ TargetValue::DataType data_type,
+ TargetValue::DataValue data_value,
+ bool rewrite_overlay_reference) {
+ if (target_map_.find(target_resource) != target_map_.end()) {
+ return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
+ }
+
+ // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
+ // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
+
+ target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
+
+ if (rewrite_overlay_reference && IsReference(data_type)) {
+ overlay_map_.insert(std::make_pair(data_value, target_resource));
+ }
+
+ return Result<Unit>({});
+}
+
+void ResourceMapping::RemoveMapping(ResourceId target_resource) {
+ auto target_iter = target_map_.find(target_resource);
+ if (target_iter == target_map_.end()) {
+ return;
+ }
+
+ const TargetValue value = target_iter->second;
+ target_map_.erase(target_iter);
+
+ if (!IsReference(value.data_type)) {
+ return;
+ }
+
+ auto overlay_iter = overlay_map_.equal_range(value.data_value);
+ for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
+ if (i->second == target_resource) {
+ overlay_map_.erase(i);
+ return;
+ }
+ }
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index dce83e3..98d026b 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -22,18 +22,56 @@
#include "androidfw/StringPiece.h"
#include "androidfw/Util.h"
#include "idmap2/Result.h"
-#include "idmap2/Xml.h"
+#include "idmap2/XmlParser.h"
#include "idmap2/ZipFile.h"
using android::StringPiece16;
using android::idmap2::Result;
-using android::idmap2::Xml;
+using android::idmap2::XmlParser;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;
namespace android::idmap2::utils {
-Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid) {
+bool IsReference(uint8_t data_type) {
+ return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE;
+}
+
+StringPiece DataTypeToString(uint8_t data_type) {
+ switch (data_type) {
+ case Res_value::TYPE_NULL:
+ return "null";
+ case Res_value::TYPE_REFERENCE:
+ return "reference";
+ case Res_value::TYPE_ATTRIBUTE:
+ return "attribute";
+ case Res_value::TYPE_STRING:
+ return "string";
+ case Res_value::TYPE_FLOAT:
+ return "float";
+ case Res_value::TYPE_DIMENSION:
+ return "dimension";
+ case Res_value::TYPE_FRACTION:
+ return "fraction";
+ case Res_value::TYPE_DYNAMIC_REFERENCE:
+ return "reference (dynamic)";
+ case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
+ return "attribute (dynamic)";
+ case Res_value::TYPE_INT_DEC:
+ case Res_value::TYPE_INT_HEX:
+ return "integer";
+ case Res_value::TYPE_INT_BOOLEAN:
+ return "boolean";
+ case Res_value::TYPE_INT_COLOR_ARGB8:
+ case Res_value::TYPE_INT_COLOR_RGB8:
+ case Res_value::TYPE_INT_COLOR_RGB4:
+ return "color";
+ default:
+ return "unknown";
+ }
+}
+
+Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) {
AssetManager2::ResourceName name;
if (!am.GetResourceName(resid, &name)) {
return Error("no resource 0x%08x in asset manager", resid);
@@ -65,52 +103,72 @@
return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str());
}
- std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size);
+ Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size);
if (!xml) {
return Error("failed to parse AndroidManifest.xml from %s", path.c_str());
}
+ auto manifest_it = (*xml)->tree_iterator();
+ if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
+ return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str());
+ }
+
+ auto overlay_it = std::find_if(manifest_it.begin(), manifest_it.end(), [](const auto& it) {
+ return it.event() == XmlParser::Event::START_TAG && it.name() == "overlay";
+ });
+
OverlayManifestInfo info{};
- const auto tag = xml->FindTag("overlay");
- if (!tag) {
- if (assert_overlay) {
- return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
+ if (overlay_it == manifest_it.end()) {
+ if (!assert_overlay) {
+ return info;
}
- return info;
+ return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str());
}
- auto iter = tag->find("targetPackage");
- if (iter == tag->end()) {
- if (assert_overlay) {
- return Error("android:targetPackage missing from <overlay> of %s", path.c_str());
- }
+ if (auto result_str = overlay_it->GetAttributeStringValue("targetPackage")) {
+ info.target_package = *result_str;
} else {
- info.target_package = iter->second;
+ return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(),
+ result_str.GetErrorMessage().c_str());
}
- iter = tag->find("targetName");
- if (iter != tag->end()) {
- info.target_name = iter->second;
+ if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) {
+ info.target_name = *result_str;
}
- iter = tag->find("isStatic");
- if (iter != tag->end()) {
- info.is_static = std::stoul(iter->second) != 0U;
+ if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) {
+ if (IsReference((*result_value).dataType)) {
+ info.resource_mapping = (*result_value).data;
+ } else {
+ return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
+ path.c_str());
+ }
}
- iter = tag->find("priority");
- if (iter != tag->end()) {
- info.priority = std::stoi(iter->second);
+ if (auto result_value = overlay_it->GetAttributeValue("isStatic")) {
+ if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
+ (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
+ info.is_static = (*result_value).data != 0U;
+ } else {
+ return Error("android:isStatic is not a boolean in AndroidManifest.xml of %s", path.c_str());
+ }
}
- iter = tag->find("requiredSystemPropertyName");
- if (iter != tag->end()) {
- info.requiredSystemPropertyName = iter->second;
+ if (auto result_value = overlay_it->GetAttributeValue("priority")) {
+ if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT &&
+ (*result_value).dataType <= Res_value::TYPE_LAST_INT) {
+ info.priority = (*result_value).data;
+ } else {
+ return Error("android:priority is not an integer in AndroidManifest.xml of %s", path.c_str());
+ }
}
- iter = tag->find("requiredSystemPropertyValue");
- if (iter != tag->end()) {
- info.requiredSystemPropertyValue = iter->second;
+ if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyName")) {
+ info.requiredSystemPropertyName = *result_str;
+ }
+
+ if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyValue")) {
+ info.requiredSystemPropertyValue = *result_str;
}
return info;
diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp
deleted file mode 100644
index 2645868..0000000
--- a/cmds/idmap2/libidmap2/Xml.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "idmap2/Xml.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-
-namespace android::idmap2 {
-
-std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) {
- std::unique_ptr<Xml> xml(new Xml());
- if (xml->xml_.setTo(data, size, copyData) != NO_ERROR) {
- return nullptr;
- }
- return xml;
-}
-
-std::unique_ptr<std::map<std::string, std::string>> Xml::FindTag(const std::string& name) const {
- const String16 tag_to_find(name.c_str(), name.size());
- xml_.restart();
- ResXMLParser::event_code_t type;
- do {
- type = xml_.next();
- if (type == ResXMLParser::START_TAG) {
- size_t len;
- const String16 tag(xml_.getElementName(&len));
- if (tag == tag_to_find) {
- std::unique_ptr<std::map<std::string, std::string>> map(
- new std::map<std::string, std::string>());
- for (size_t i = 0; i < xml_.getAttributeCount(); i++) {
- const String16 key16(xml_.getAttributeName(i, &len));
- std::string key = String8(key16).c_str();
-
- std::string value;
- switch (xml_.getAttributeDataType(i)) {
- case Res_value::TYPE_STRING: {
- const String16 value16(xml_.getAttributeStringValue(i, &len));
- value = String8(value16).c_str();
- } break;
- case Res_value::TYPE_INT_DEC:
- case Res_value::TYPE_INT_HEX:
- case Res_value::TYPE_INT_BOOLEAN: {
- Res_value resValue;
- xml_.getAttributeValue(i, &resValue);
- value = std::to_string(resValue.data);
- } break;
- default:
- return nullptr;
- }
-
- map->emplace(std::make_pair(key, value));
- }
- return map;
- }
- }
- } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
- return nullptr;
-}
-
-Xml::~Xml() {
- xml_.uninit();
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp
new file mode 100644
index 0000000..526a560
--- /dev/null
+++ b/cmds/idmap2/libidmap2/XmlParser.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "idmap2/XmlParser.h"
+
+#include <iostream>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+
+namespace android::idmap2 {
+
+template <typename T>
+ResXMLParser::ResXMLPosition get_tree_position(const T& tree) {
+ ResXMLParser::ResXMLPosition pos{};
+ tree.getPosition(&pos);
+ return pos;
+}
+
+XmlParser::Node::Node(const ResXMLTree& tree) : Node(tree, get_tree_position(tree)) {
+}
+XmlParser::Node::Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos)
+ : parser_(tree) {
+ set_position(pos);
+}
+
+bool XmlParser::Node::operator==(const XmlParser::Node& rhs) const {
+ ResXMLParser::ResXMLPosition pos = get_position();
+ ResXMLParser::ResXMLPosition rhs_pos = rhs.get_position();
+ return pos.curExt == rhs_pos.curExt && pos.curNode == rhs_pos.curNode &&
+ pos.eventCode == rhs_pos.eventCode;
+}
+
+bool XmlParser::Node::operator!=(const XmlParser::Node& rhs) const {
+ return !(*this == rhs);
+}
+
+ResXMLParser::ResXMLPosition XmlParser::Node::get_position() const {
+ return get_tree_position(parser_);
+}
+
+void XmlParser::Node::set_position(const ResXMLParser::ResXMLPosition& pos) {
+ parser_.setPosition(pos);
+}
+
+bool XmlParser::Node::Seek(bool inner_child) {
+ if (parser_.getEventType() == XmlParser::Event::END_TAG) {
+ return false;
+ }
+
+ ssize_t depth = 0;
+ XmlParser::Event code;
+ while ((code = parser_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+ code != XmlParser::Event::END_DOCUMENT) {
+ if (code == XmlParser::Event::START_TAG) {
+ if (++depth == (inner_child ? 1 : 0)) {
+ return true;
+ }
+ } else if (code == XmlParser::Event::END_TAG) {
+ if (--depth == (inner_child ? -1 : -2)) {
+ return false;
+ }
+ }
+ }
+
+ return false;
+}
+
+XmlParser::Event XmlParser::Node::event() const {
+ return parser_.getEventType();
+}
+
+std::string XmlParser::Node::name() const {
+ size_t len;
+ const String16 key16(parser_.getElementName(&len));
+ return String8(key16).c_str();
+}
+
+Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const {
+ auto value = GetAttributeValue(name);
+ if (!value) {
+ return value.GetError();
+ }
+
+ switch ((*value).dataType) {
+ case Res_value::TYPE_STRING: {
+ size_t len;
+ const String16 value16(parser_.getStrings().stringAt((*value).data, &len));
+ return std::string(String8(value16).c_str());
+ }
+ case Res_value::TYPE_INT_DEC:
+ case Res_value::TYPE_INT_HEX:
+ case Res_value::TYPE_INT_BOOLEAN: {
+ return std::to_string((*value).data);
+ }
+ default:
+ return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str());
+ }
+}
+
+Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
+ size_t len;
+ for (size_t i = 0; i < parser_.getAttributeCount(); i++) {
+ const String16 key16(parser_.getAttributeName(i, &len));
+ std::string key = String8(key16).c_str();
+ if (key != name) {
+ continue;
+ }
+
+ Res_value res_value{};
+ if (parser_.getAttributeValue(i, &res_value) == BAD_TYPE) {
+ return Error(R"(Bad value for attribute "%s")", name.c_str());
+ }
+
+ return res_value;
+ }
+
+ return Error(R"(Failed to find attribute "%s")", name.c_str());
+}
+
+Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size,
+ bool copy_data) {
+ auto parser = std::unique_ptr<const XmlParser>(new XmlParser());
+ if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) {
+ return Error("Malformed xml block");
+ }
+
+ // Find the beginning of the first tag.
+ XmlParser::Event event;
+ while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT &&
+ event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
+ }
+
+ if (event == XmlParser::Event::END_DOCUMENT) {
+ return Error("Root tag was not be found");
+ }
+
+ if (event == XmlParser::Event::BAD_DOCUMENT) {
+ return Error("Bad xml document");
+ }
+
+ return parser;
+}
+
+XmlParser::~XmlParser() {
+ tree_.uninit();
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp
index 4f5e3a4..1e1a218 100644
--- a/cmds/idmap2/libidmap2/ZipFile.cpp
+++ b/cmds/idmap2/libidmap2/ZipFile.cpp
@@ -34,6 +34,7 @@
::ZipArchiveHandle handle;
int32_t status = ::OpenArchive(path.c_str(), &handle);
if (status != 0) {
+ ::CloseArchive(handle);
return nullptr;
}
return std::unique_ptr<ZipFile>(new ZipFile(handle));
diff --git a/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
new file mode 100644
index 0000000..5bd353a
--- /dev/null
+++ b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
+#define IDMAP2_INCLUDE_IDMAP2_POLICIES_H_
+
+#include <array>
+#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;
+
+namespace android::idmap2::policy {
+
+constexpr const char* kPolicyActor = "actor";
+constexpr const char* kPolicyOdm = "odm";
+constexpr const char* kPolicyOem = "oem";
+constexpr const char* kPolicyProduct = "product";
+constexpr const char* kPolicyPublic = "public";
+constexpr const char* kPolicySignature = "signature";
+constexpr const char* kPolicySystem = "system";
+constexpr const char* kPolicyVendor = "vendor";
+
+inline static const std::array<std::pair<StringPiece, PolicyFlags>, 8> kPolicyStringToFlag = {
+ std::pair{kPolicyActor, PolicyFlags::ACTOR_SIGNATURE},
+ {kPolicyOdm, PolicyFlags::ODM_PARTITION},
+ {kPolicyOem, PolicyFlags::OEM_PARTITION},
+ {kPolicyProduct, PolicyFlags::PRODUCT_PARTITION},
+ {kPolicyPublic, PolicyFlags::PUBLIC},
+ {kPolicySignature, PolicyFlags::SIGNATURE},
+ {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/static-checks.sh b/cmds/idmap2/static-checks.sh
index 41d3c69..a372abd 100755
--- a/cmds/idmap2/static-checks.sh
+++ b/cmds/idmap2/static-checks.sh
@@ -27,10 +27,11 @@
local red="\e[31m"
local green="\e[32m"
local reset="\e[0m"
+ local output
_log "${green}[ RUN ]${reset} ${label}"
- local output="$(eval "$cmd")"
- if [[ -z "${output}" ]]; then
+ output="$(eval "$cmd" 2>&1)"
+ if [[ $? -eq 0 ]]; then
_log "${green}[ OK ]${reset} ${label}"
return 0
else
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 9348ab7..5fea7bc 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -18,6 +18,7 @@
#include <sstream>
#include <string>
#include <utility>
+#include <vector>
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
@@ -47,118 +48,49 @@
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);
ASSERT_EQ(idmap1->GetData().size(), idmap2->GetData().size());
- const auto& data1 = idmap1->GetData()[0];
- const auto& data2 = idmap2->GetData()[0];
+ const std::vector<std::unique_ptr<const IdmapData>>& data_blocks1 = idmap1->GetData();
+ ASSERT_EQ(data_blocks1.size(), 1U);
+ const std::unique_ptr<const IdmapData>& data1 = data_blocks1[0];
+ ASSERT_THAT(data1, NotNull());
- ASSERT_EQ(data1->GetHeader()->GetTargetPackageId(), data2->GetHeader()->GetTargetPackageId());
- ASSERT_EQ(data1->GetTypeEntries().size(), 2U);
- ASSERT_EQ(data1->GetTypeEntries().size(), data2->GetTypeEntries().size());
- ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(0), data2->GetTypeEntries()[0]->GetEntry(0));
- ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(1), data2->GetTypeEntries()[0]->GetEntry(1));
- ASSERT_EQ(data1->GetTypeEntries()[0]->GetEntry(2), data2->GetTypeEntries()[0]->GetEntry(2));
- ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(0), data2->GetTypeEntries()[1]->GetEntry(0));
- ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(1), data2->GetTypeEntries()[1]->GetEntry(1));
- ASSERT_EQ(data1->GetTypeEntries()[1]->GetEntry(2), data2->GetTypeEntries()[1]->GetEntry(2));
-}
+ const std::vector<std::unique_ptr<const IdmapData>>& data_blocks2 = idmap2->GetData();
+ ASSERT_EQ(data_blocks2.size(), 1U);
+ const std::unique_ptr<const IdmapData>& data2 = data_blocks2[0];
+ ASSERT_THAT(data2, NotNull());
-TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
- const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
- ASSERT_THAT(target_apk, NotNull());
+ const auto& target_entries1 = data1->GetTargetEntries();
+ const auto& target_entries2 = data2->GetTargetEntries();
+ ASSERT_EQ(target_entries1.size(), target_entries2.size());
+ ASSERT_EQ(target_entries1[0].target_id, target_entries2[0].target_id);
+ ASSERT_EQ(target_entries1[0].data_value, target_entries2[0].data_value);
- const std::string overlay_apk_path(GetTestDataPath() + "/overlay/overlay.apk");
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
- ASSERT_THAT(overlay_apk, NotNull());
+ ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id);
+ ASSERT_EQ(target_entries1[1].data_value, target_entries2[1].data_value);
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
- ASSERT_TRUE(idmap);
+ ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id);
+ ASSERT_EQ(target_entries1[2].data_value, target_entries2[2].data_value);
- std::stringstream stream;
- BinaryStreamVisitor visitor(stream);
- (*idmap)->accept(&visitor);
- const std::string str = stream.str();
- const StringPiece data(str);
- std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(data);
- ASSERT_THAT(loaded_idmap, NotNull());
- ASSERT_EQ(loaded_idmap->TargetPackageId(), 0x7f);
+ const auto& overlay_entries1 = data1->GetOverlayEntries();
+ const auto& overlay_entries2 = data2->GetOverlayEntries();
+ ASSERT_EQ(overlay_entries1.size(), overlay_entries2.size());
+ ASSERT_EQ(overlay_entries1[0].overlay_id, overlay_entries2[0].overlay_id);
+ ASSERT_EQ(overlay_entries1[0].target_id, overlay_entries2[0].target_id);
- const IdmapEntry_header* header = loaded_idmap->GetEntryMapForType(0x01);
- ASSERT_THAT(header, NotNull());
+ ASSERT_EQ(overlay_entries1[1].overlay_id, overlay_entries2[1].overlay_id);
+ ASSERT_EQ(overlay_entries1[1].target_id, overlay_entries2[1].target_id);
- EntryId entry;
- bool success = LoadedIdmap::Lookup(header, 0x0000, &entry);
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0000);
-
- header = loaded_idmap->GetEntryMapForType(0x02);
- ASSERT_THAT(header, NotNull());
-
- success = LoadedIdmap::Lookup(header, 0x0000, &entry); // string/a
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0001, &entry); // string/b
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0002, &entry); // string/c
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0003, &entry); // string/policy_odm
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0004, &entry); // string/policy_oem
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0005, &entry); // string/other
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0006, &entry); // string/not_overlayable
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0007, &entry); // string/policy_product
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0008, &entry); // string/policy_public
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0009, &entry); // string/policy_system
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000a, &entry); // string/policy_system_vendor
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000b, &entry); // string/policy_signature
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/str1
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0000);
-
- success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/str2
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/str3
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0001);
-
- success = LoadedIdmap::Lookup(header, 0x000f, &entry); // string/str4
- ASSERT_TRUE(success);
- ASSERT_EQ(entry, 0x0002);
-
- success = LoadedIdmap::Lookup(header, 0x0010, &entry); // string/x
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0011, &entry); // string/y
- ASSERT_FALSE(success);
-
- success = LoadedIdmap::Lookup(header, 0x0012, &entry); // string/z
- ASSERT_FALSE(success);
+ ASSERT_EQ(overlay_entries1[2].overlay_id, overlay_entries2[2].overlay_id);
+ ASSERT_EQ(overlay_entries1[2].target_id, overlay_entries2[2].target_id);
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index f55acee..8af4037 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -56,12 +56,12 @@
return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
});
ASSERT_THAT(v, NotNull());
- ASSERT_EQ(v->size(), 10U);
+ ASSERT_EQ(v->size(), 11U);
ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
std::set<std::string>(
{root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
- root + "/overlay/overlay-no-name-static.apk",
+ root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-shared.apk",
root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk",
root + "/signature-overlay/signature-overlay.apk",
root + "/system-overlay/system-overlay.apk",
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 499eb99..d896cf9 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -34,6 +34,7 @@
#include <string>
#include <vector>
+#include "R.h"
#include "TestHelpers.h"
#include "androidfw/PosixUtils.h"
#include "gmock/gmock.h"
@@ -127,10 +128,14 @@
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020000 string/str1"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000e -> 0x7f020001 string/str3"), std::string::npos);
- ASSERT_NE(result->stdout.find("0x7f02000f -> 0x7f020002 string/str4"), std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000 string/str1"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001 string/str3"),
+ std::string::npos);
+ ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002 string/str4"),
+ std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -141,7 +146,6 @@
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
ASSERT_NE(result->stdout.find("00000000: 504d4449 magic"), std::string::npos);
- ASSERT_NE(result->stdout.find("00000210: 007f target package id"), std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -298,7 +302,7 @@
"lookup",
"--idmap-path", GetIdmapPath(),
"--config", "",
- "--resid", "0x7f02000c"}); // string/str1
+ "--resid", R::target::string::literal::str1});
// clang-format on
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 0f47f1e..6fab5e0 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -22,6 +22,8 @@
#include <utility>
#include <vector>
+#include "R.h"
+#include "TestConstants.h"
#include "TestHelpers.h"
#include "android-base/macros.h"
#include "androidfw/ApkAssets.h"
@@ -30,12 +32,25 @@
#include "idmap2/BinaryStreamVisitor.h"
#include "idmap2/CommandLineOptions.h"
#include "idmap2/Idmap.h"
+#include "idmap2/LogInfo.h"
+using android::Res_value;
using ::testing::IsNull;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
+#define ASSERT_TARGET_ENTRY(entry, target_resid, type, value) \
+ ASSERT_EQ(entry.target_id, target_resid); \
+ ASSERT_EQ(entry.data_type, type); \
+ ASSERT_EQ(entry.data_value, value)
+
+#define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \
+ ASSERT_EQ(entry.overlay_id, overlay_resid); \
+ ASSERT_EQ(entry.target_id, target_resid)
+
TEST(IdmapTests, TestCanonicalIdmapPathFor) {
ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
"/foo/vendor@overlay@bar.apk@idmap");
@@ -47,17 +62,20 @@
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
- ASSERT_EQ(header->GetVersion(), 0x01U);
+ ASSERT_EQ(header->GetVersion(), 0x04U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
- ASSERT_EQ(header->GetTargetPath().to_string(), "target.apk");
- ASSERT_EQ(header->GetOverlayPath().to_string(), "overlay.apk");
+ 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");
}
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);
@@ -66,58 +84,40 @@
}
TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
- const size_t offset = 0x210;
+ 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);
std::unique_ptr<const IdmapData::Header> header = IdmapData::Header::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
- ASSERT_EQ(header->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(header->GetTypeCount(), 2U);
-}
-
-TEST(IdmapTests, CreateIdmapDataResourceTypeFromBinaryStream) {
- const size_t offset = 0x214;
- std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
- idmap_raw_data_len - offset);
- std::istringstream stream(raw);
-
- std::unique_ptr<const IdmapData::TypeEntry> data = IdmapData::TypeEntry::FromBinaryStream(stream);
- ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(data->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(data->GetEntryCount(), 1U);
- ASSERT_EQ(data->GetEntryOffset(), 0U);
- ASSERT_EQ(data->GetEntry(0), 0U);
+ ASSERT_EQ(header->GetTargetEntryCount(), 0x03);
+ ASSERT_EQ(header->GetOverlayEntryCount(), 0x03);
}
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
- const size_t offset = 0x210;
+ 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);
std::unique_ptr<const IdmapData> data = IdmapData::FromBinaryStream(stream);
ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x01 /* Res_value::TYPE_REFERENCE */,
+ 0x7f020000);
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x01 /* Res_value::TYPE_REFERENCE */,
+ 0x7f030000);
+ ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x01 /* Res_value::TYPE_REFERENCE */,
+ 0x7f030001);
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetEntryCount(), 3U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020000, 0x7f020000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f030000, 0x7f030000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f030001, 0x7f030002);
}
TEST(IdmapTests, CreateIdmapFromBinaryStream) {
@@ -130,34 +130,31 @@
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
- ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), "target.apk");
- ASSERT_EQ(idmap->GetHeader()->GetOverlayPath().to_string(), "overlay.apk");
+ 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");
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
+ ASSERT_THAT(data, NotNull());
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, Res_value::TYPE_REFERENCE, 0x7f020000);
+ ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, Res_value::TYPE_REFERENCE, 0x7f030000);
+ ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, Res_value::TYPE_REFERENCE, 0x7f030001);
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x03U);
- ASSERT_EQ(types[1]->GetEntryCount(), 3U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 3U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020000, 0x7f020000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f030000, 0x7f030000);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f030001, 0x7f030002);
}
TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) {
@@ -169,301 +166,192 @@
ASSERT_FALSE(result);
}
-void CreateIdmap(const StringPiece& target_apk_path, const StringPiece& overlay_apk_path,
- const PolicyBitmask& fulfilled_policies, bool enforce_overlayable,
- std::unique_ptr<const Idmap>* out_idmap) {
- std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path.to_string());
- ASSERT_THAT(target_apk, NotNull());
-
- std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path.to_string());
- ASSERT_THAT(overlay_apk, NotNull());
-
- auto result =
- Idmap::FromApkAssets(target_apk_path.to_string(), *target_apk, overlay_apk_path.to_string(),
- *overlay_apk, fulfilled_policies, enforce_overlayable);
- *out_idmap = result ? std::move(*result) : nullptr;
-}
-
-TEST(IdmapTests, CreateIdmapFromApkAssets) {
- std::unique_ptr<const Idmap> idmap;
+TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
+
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
+ ASSERT_THAT(idmap, NotNull());
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
- ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x76a20829);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed);
+ 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);
- ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
-
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
-
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);
-
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetEntryCount(), 4U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 12U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
- ASSERT_EQ(types[1]->GetEntry(3), 0x0002U);
}
-// Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
- std::unique_ptr<const Idmap> idmap;
+Result<std::unique_ptr<const IdmapData>> TestIdmapDataFromApkAssets(
+ const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path, const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies, bool enforce_overlayable) {
+ const std::string target_apk_path(GetTestDataPath() + local_target_apk_path.data());
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+ }
+
+ const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path.data());
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+ }
+
+ LogInfo log_info;
+ auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info,
+ fulfilled_policies, enforce_overlayable, log_info);
+
+ if (!mapping) {
+ return mapping.GetError();
+ }
+
+ return IdmapData::FromResourceMapping(*mapping);
+}
+
+TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/system-overlay/system-overlay.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
+ std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk";
+
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
+ Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay::integer::int1);
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str1);
+ ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3);
+ ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str4);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 4U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 8U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(2), 0x0001U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(3), 0x0002U); // string/policy_system_vendor
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay::integer::int1, R::target::integer::int1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay::string::str1, R::target::string::str1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay::string::str3, R::target::string::str3);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay::string::str4, R::target::string::str4);
}
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) {
- std::unique_ptr<const Idmap> idmap;
+TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/signature-overlay/signature-overlay.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_SIGNATURE,
- /* enforce_overlayable */ true, &idmap);
+ std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk";
+
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ ASSERT_THAT(target_apk, NotNull());
+
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ ASSERT_THAT(overlay_apk, NotNull());
+
+ auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+ ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+ auto& idmap = *idmap_result;
ASSERT_THAT(idmap, NotNull());
const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
ASSERT_EQ(dataBlocks.size(), 1U);
const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ ASSERT_THAT(data, NotNull());
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
+ Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay_shared::integer::int1);
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str1);
+ ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str3);
+ ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay_shared::string::str4);
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 9U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_signature
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(target_entries.size(), 4U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay_shared::integer::int1,
+ R::target::integer::int1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay_shared::string::str1,
+ R::target::string::str1);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay_shared::string::str3,
+ R::target::string::str3);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay_shared::string::str4,
+ R::target::string::str4);
}
-// Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path =
- GetTestDataPath() + "/system-overlay-invalid/system-overlay-invalid.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
+TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
+ auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
+ ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
+ auto& data = *idmap_data;
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 2U);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1, Res_value::TYPE_REFERENCE,
+ 0x0104000a); // -> android:string/ok
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3);
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 4U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 8U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0005U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(2), 0x0007U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(3), 0x0008U); // string/policy_system_vendor
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(overlay_entries.size(), 1U);
+ ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay::string::str3, R::target::string::str3);
}
-// Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
- std::string overlay_apk_path =
- GetTestDataPath() + "/system-overlay-invalid/system-overlay-invalid.apk";
- CreateIdmap(target_apk_path, overlay_apk_path,
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ false, &idmap);
- ASSERT_THAT(idmap, NotNull());
+TEST(IdmapTests, CreateIdmapDataInlineResources) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030002; // xml/overlays_inline
+ auto idmap_data = TestIdmapDataFromApkAssets("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
+ ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
+ auto& data = *idmap_data;
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+ constexpr size_t overlay_string_pool_size = 8U;
+ const auto& target_entries = data->GetTargetEntries();
+ ASSERT_EQ(target_entries.size(), 2U);
+ ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, Res_value::TYPE_INT_DEC,
+ 73U); // -> 73
+ ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_STRING,
+ overlay_string_pool_size + 0U); // -> "Hello World"
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 9U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
- ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_odm
- ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_oem
- ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/other
- ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_product
- ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(7), 0x0007U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(8), 0x0008U); // string/policy_system_vendor
-}
-
-// Overlays that do not specify a target <overlayable> can overlay resources defined as overlayable.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target-no-overlayable.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-no-name.apk";
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ false, &idmap);
- ASSERT_THAT(idmap, NotNull());
-
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
-
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 2U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 2U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 1U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 0U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/int1
-
- ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
- ASSERT_EQ(types[1]->GetEntryCount(), 4U);
- ASSERT_EQ(types[1]->GetEntryOffset(), 12U);
- ASSERT_EQ(types[1]->GetEntry(0), 0x0000U); // string/str1
- ASSERT_EQ(types[1]->GetEntry(1), kNoEntry); // string/str2
- ASSERT_EQ(types[1]->GetEntry(2), 0x0001U); // string/str3
- ASSERT_EQ(types[1]->GetEntry(3), 0x0002U); // string/str4
-}
-
-// Overlays that are not pre-installed and are not signed with the same signature as the target
-// cannot overlay packages that have not defined overlayable resources.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsDefaultPoliciesPublicFail) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target-no-overlayable.apk";
- std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-no-name.apk";
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PUBLIC,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, IsNull());
-}
-
-// Overlays that are pre-installed or are signed with the same signature as the target can overlay
-// packages that have not defined overlayable resources.
-TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsDefaultPolicies) {
- std::unique_ptr<const Idmap> idmap;
- std::string target_apk_path = GetTestDataPath() + "/target/target-no-overlayable.apk";
- std::string overlay_apk_path =
- GetTestDataPath() + "/system-overlay-invalid/system-overlay-invalid.apk";
-
- auto CheckEntries = [&]() -> void {
- const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
- ASSERT_EQ(dataBlocks.size(), 1U);
-
- const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
- ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU);
- ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U);
-
- const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries();
- ASSERT_EQ(types.size(), 1U);
-
- ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
- ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
- ASSERT_EQ(types[0]->GetEntryCount(), 9U);
- ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
- ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/not_overlayable
- ASSERT_EQ(types[0]->GetEntry(1), 0x0001U); // string/policy_odm
- ASSERT_EQ(types[0]->GetEntry(2), 0x0002U); // string/policy_oem
- ASSERT_EQ(types[0]->GetEntry(3), 0x0003U); // string/other
- ASSERT_EQ(types[0]->GetEntry(4), 0x0004U); // string/policy_product
- ASSERT_EQ(types[0]->GetEntry(5), 0x0005U); // string/policy_public
- ASSERT_EQ(types[0]->GetEntry(6), 0x0006U); // string/policy_signature
- ASSERT_EQ(types[0]->GetEntry(7), 0x0007U); // string/policy_system
- ASSERT_EQ(types[0]->GetEntry(8), 0x0008U); // string/policy_system_vendor
- };
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_SIGNATURE,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_PRODUCT_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_SYSTEM_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_VENDOR_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_ODM_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
-
- CreateIdmap(target_apk_path, overlay_apk_path, PolicyFlags::POLICY_OEM_PARTITION,
- /* enforce_overlayable */ true, &idmap);
- ASSERT_THAT(idmap, NotNull());
- CheckEntries();
+ const auto& overlay_entries = data->GetOverlayEntries();
+ ASSERT_EQ(overlay_entries.size(), 0U);
}
TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
@@ -480,9 +368,8 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto result =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_FALSE(result);
}
@@ -497,8 +384,7 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- auto result = Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC,
+ auto result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
/* enforce_overlayable */ true);
ASSERT_TRUE(result);
const auto idmap = std::move(*result);
@@ -509,7 +395,8 @@
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());
@@ -522,7 +409,8 @@
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());
@@ -535,7 +423,8 @@
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());
@@ -548,7 +437,8 @@
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());
@@ -561,27 +451,55 @@
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());
+ ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
+ PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
- // target path: bytes (0x10, 0x10f)
+ // 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 {
@@ -605,10 +523,6 @@
stream_ << "TestVisitor::visit(IdmapData::Header)" << std::endl;
}
- void visit(const IdmapData::TypeEntry& idmap ATTRIBUTE_UNUSED) override {
- stream_ << "TestVisitor::visit(IdmapData::TypeEntry)" << std::endl;
- }
-
private:
std::ostream& stream_;
};
@@ -625,12 +539,10 @@
(*idmap)->accept(&visitor);
ASSERT_EQ(test_stream.str(),
- "TestVisitor::visit(Idmap)\n"
"TestVisitor::visit(IdmapHeader)\n"
- "TestVisitor::visit(IdmapData)\n"
+ "TestVisitor::visit(Idmap)\n"
"TestVisitor::visit(IdmapData::Header)\n"
- "TestVisitor::visit(IdmapData::TypeEntry)\n"
- "TestVisitor::visit(IdmapData::TypeEntry)\n");
+ "TestVisitor::visit(IdmapData)\n");
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp
index eca7404..1b27759 100644
--- a/cmds/idmap2/tests/PoliciesTests.cpp
+++ b/cmds/idmap2/tests/PoliciesTests.cpp
@@ -17,76 +17,96 @@
#include <string>
#include "TestHelpers.h"
+#include "androidfw/ResourceTypes.h"
#include "gtest/gtest.h"
-#include "idmap2/Policies.h"
+#include "idmap2/PolicyUtils.h"
-using android::idmap2::PolicyBitmask;
-using android::idmap2::PolicyFlags;
+using android::idmap2::utils::BitmaskToPolicies;
+using android::idmap2::utils::PoliciesToBitmaskResult;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
-TEST(PoliciesTests, PoliciesToBitmasks) {
- const auto bitmask1 = PoliciesToBitmask({"system"});
+TEST(PoliciesTests, PoliciesToBitmaskResults) {
+ const auto bitmask1 = PoliciesToBitmaskResult({"system"});
ASSERT_TRUE(bitmask1);
- ASSERT_EQ(*bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION);
+ ASSERT_EQ(*bitmask1, PolicyFlags::SYSTEM_PARTITION);
- const auto bitmask2 = PoliciesToBitmask({"system", "vendor"});
+ const auto bitmask2 = PoliciesToBitmaskResult({"system", "vendor"});
ASSERT_TRUE(bitmask2);
- ASSERT_EQ(*bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask2, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask3 = PoliciesToBitmask({"vendor", "system"});
+ const auto bitmask3 = PoliciesToBitmaskResult({"vendor", "system"});
ASSERT_TRUE(bitmask3);
- ASSERT_EQ(*bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask3, PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask4 = PoliciesToBitmask({"odm", "oem", "public", "product", "system", "vendor"});
+ const auto bitmask4 =
+ PoliciesToBitmaskResult({"odm", "oem", "public", "product", "system", "vendor"});
ASSERT_TRUE(bitmask4);
- ASSERT_EQ(*bitmask4, PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION |
- PolicyFlags::POLICY_VENDOR_PARTITION);
+ ASSERT_EQ(*bitmask4, PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION |
+ PolicyFlags::PUBLIC | PolicyFlags::PRODUCT_PARTITION |
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
- const auto bitmask5 = PoliciesToBitmask({"system", "system", "system"});
+ const auto bitmask5 = PoliciesToBitmaskResult({"system", "system", "system"});
ASSERT_TRUE(bitmask5);
- ASSERT_EQ(*bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION);
+ ASSERT_EQ(*bitmask5, PolicyFlags::SYSTEM_PARTITION);
- const auto bitmask6 = PoliciesToBitmask({""});
+ const auto bitmask6 = PoliciesToBitmaskResult({""});
ASSERT_FALSE(bitmask6);
- const auto bitmask7 = PoliciesToBitmask({"foo"});
+ const auto bitmask7 = PoliciesToBitmaskResult({"foo"});
ASSERT_FALSE(bitmask7);
- const auto bitmask8 = PoliciesToBitmask({"system", "foo"});
+ const auto bitmask8 = PoliciesToBitmaskResult({"system", "foo"});
ASSERT_FALSE(bitmask8);
- const auto bitmask9 = PoliciesToBitmask({"system", ""});
+ const auto bitmask9 = PoliciesToBitmaskResult({"system", ""});
ASSERT_FALSE(bitmask9);
- const auto bitmask10 = PoliciesToBitmask({"system "});
+ const auto bitmask10 = PoliciesToBitmaskResult({"system "});
ASSERT_FALSE(bitmask10);
+
+ const auto bitmask11 = PoliciesToBitmaskResult({"signature"});
+ ASSERT_TRUE(bitmask11);
+ ASSERT_EQ(*bitmask11, PolicyFlags::SIGNATURE);
+
+ const auto bitmask12 = PoliciesToBitmaskResult({"actor"});
+ ASSERT_TRUE(bitmask12);
+ ASSERT_EQ(*bitmask12, PolicyFlags::ACTOR_SIGNATURE);
}
TEST(PoliciesTests, BitmaskToPolicies) {
- const auto policies1 = BitmaskToPolicies(PolicyFlags::POLICY_PUBLIC);
+ const auto policies1 = BitmaskToPolicies(PolicyFlags::PUBLIC);
ASSERT_EQ(1, policies1.size());
ASSERT_EQ(policies1[0], "public");
- const auto policies2 = BitmaskToPolicies(PolicyFlags::POLICY_SYSTEM_PARTITION |
- PolicyFlags::POLICY_VENDOR_PARTITION);
+ const auto policies2 =
+ BitmaskToPolicies(PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
ASSERT_EQ(2, policies2.size());
ASSERT_EQ(policies2[0], "system");
ASSERT_EQ(policies2[1], "vendor");
- const auto policies3 = BitmaskToPolicies(
- PolicyFlags::POLICY_ODM_PARTITION | PolicyFlags::POLICY_OEM_PARTITION |
- PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION |
- PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION);
+ const auto policies3 =
+ BitmaskToPolicies(PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION |
+ PolicyFlags::PUBLIC | PolicyFlags::PRODUCT_PARTITION |
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION);
ASSERT_EQ(2, policies2.size());
ASSERT_EQ(policies3[0], "odm");
ASSERT_EQ(policies3[1], "oem");
- ASSERT_EQ(policies3[2], "public");
- ASSERT_EQ(policies3[3], "product");
+ ASSERT_EQ(policies3[2], "product");
+ ASSERT_EQ(policies3[3], "public");
ASSERT_EQ(policies3[4], "system");
ASSERT_EQ(policies3[5], "vendor");
+
+ const auto policies4 = BitmaskToPolicies(PolicyFlags::SIGNATURE);
+ ASSERT_EQ(1, policies4.size());
+ ASSERT_EQ(policies4[0], "signature");
+
+ const auto policies5 = BitmaskToPolicies(PolicyFlags::ACTOR_SIGNATURE);
+ ASSERT_EQ(1, policies5.size());
+ ASSERT_EQ(policies5[0], "actor");
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
index c412504..9a10079 100644
--- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp
@@ -18,19 +18,22 @@
#include <sstream>
#include <string>
+#include "R.h"
#include "TestHelpers.h"
#include "androidfw/ApkAssets.h"
#include "androidfw/Idmap.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Idmap.h"
-#include "idmap2/Policies.h"
#include "idmap2/PrettyPrintVisitor.h"
using ::testing::NotNull;
using android::ApkAssets;
-using android::idmap2::PolicyBitmask;
+
+using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 {
@@ -43,9 +46,8 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
@@ -54,7 +56,8 @@
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
- ASSERT_NE(stream.str().find("0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos);
+ ASSERT_NE(stream.str().find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1\n"),
+ std::string::npos);
}
TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
diff --git a/cmds/idmap2/tests/R.h b/cmds/idmap2/tests/R.h
new file mode 100644
index 0000000..aed263a
--- /dev/null
+++ b/cmds/idmap2/tests/R.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDMAP2_TESTS_R_H
+#define IDMAP2_TESTS_R_H
+
+#include <idmap2/ResourceUtils.h>
+
+namespace android::idmap2 {
+
+static std::string hexify(ResourceId id) {
+ std::stringstream stream;
+ stream << std::hex << static_cast<uint32_t>(id);
+ return stream.str();
+}
+
+// clang-format off
+namespace R::target {
+ namespace integer {
+ constexpr ResourceId int1 = 0x7f010000;
+
+ namespace literal {
+ inline const std::string int1 = hexify(R::target::integer::int1);
+ }
+ }
+
+ namespace string {
+ constexpr ResourceId not_overlayable = 0x7f020003;
+ constexpr ResourceId other = 0x7f020004;
+ constexpr ResourceId policy_actor = 0x7f020005;
+ constexpr ResourceId policy_odm = 0x7f020006;
+ constexpr ResourceId policy_oem = 0x7f020007;
+ constexpr ResourceId policy_product = 0x7f020008;
+ constexpr ResourceId policy_public = 0x7f020009;
+ constexpr ResourceId policy_signature = 0x7f02000a;
+ constexpr ResourceId policy_system = 0x7f02000b;
+ constexpr ResourceId policy_system_vendor = 0x7f02000c;
+ constexpr ResourceId str1 = 0x7f02000d;
+ constexpr ResourceId str3 = 0x7f02000f;
+ constexpr ResourceId str4 = 0x7f020010;
+
+ namespace literal {
+ inline const std::string str1 = hexify(R::target::string::str1);
+ inline const std::string str3 = hexify(R::target::string::str3);
+ inline const std::string str4 = hexify(R::target::string::str4);
+ }
+ }
+}
+
+namespace R::overlay {
+ namespace integer {
+ constexpr ResourceId int1 = 0x7f010000;
+ }
+ namespace string {
+ constexpr ResourceId str1 = 0x7f020000;
+ constexpr ResourceId str3 = 0x7f020001;
+ constexpr ResourceId str4 = 0x7f020002;
+ }
+}
+
+namespace R::overlay_shared {
+ namespace integer {
+ constexpr ResourceId int1 = 0x00010000;
+ }
+ namespace string {
+ constexpr ResourceId str1 = 0x00020000;
+ constexpr ResourceId str3 = 0x00020001;
+ constexpr ResourceId str4 = 0x00020002;
+ }
+}
+
+namespace R::system_overlay::string {
+ constexpr ResourceId policy_public = 0x7f010000;
+ constexpr ResourceId policy_system = 0x7f010001;
+ constexpr ResourceId policy_system_vendor = 0x7f010002;
+}
+
+namespace R::system_overlay_invalid::string {
+ constexpr ResourceId not_overlayable = 0x7f010000;
+ constexpr ResourceId other = 0x7f010001;
+ constexpr ResourceId policy_actor = 0x7f010002;
+ constexpr ResourceId policy_odm = 0x7f010003;
+ constexpr ResourceId policy_oem = 0x7f010004;
+ constexpr ResourceId policy_product = 0x7f010005;
+ constexpr ResourceId policy_public = 0x7f010006;
+ constexpr ResourceId policy_signature = 0x7f010007;
+ constexpr ResourceId policy_system = 0x7f010008;
+ constexpr ResourceId policy_system_vendor = 0x7f010009;
+};
+// clang-format on
+
+} // namespace android::idmap2
+
+#endif // IDMAP2_TESTS_R_H
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index 2695176..b268d5a 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -16,20 +16,38 @@
#include <cstdio> // fclose
#include <memory>
+#include <regex>
#include <sstream>
#include <string>
+#include "TestConstants.h"
#include "TestHelpers.h"
+#include "android-base/stringprintf.h"
+#include "androidfw/ResourceTypes.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "idmap2/Idmap.h"
#include "idmap2/RawPrintVisitor.h"
+using android::base::StringPrintf;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace android::idmap2 {
+#define ASSERT_CONTAINS_REGEX(pattern, str) \
+ do { \
+ ASSERT_TRUE(std::regex_search(str, std::regex(pattern))) \
+ << "pattern '" << pattern << "' not found in\n--------\n" \
+ << str << "--------"; \
+ } while (0)
+
+#define ADDRESS "[0-9a-f]{8}: "
+
TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
+ fclose(stderr); // silence expected warnings
+
const std::string target_apk_path(GetTestDataPath() + "/target/target.apk");
std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
ASSERT_THAT(target_apk, NotNull());
@@ -38,21 +56,36 @@
std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
ASSERT_THAT(overlay_apk, NotNull());
- const auto idmap =
- Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk,
- PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true);
+ const auto idmap = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
ASSERT_TRUE(idmap);
std::stringstream stream;
RawPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
- ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000008: 76a20829 target crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000000c: 8635c2ed overlay crc\n"), std::string::npos);
- ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"),
- std::string::npos);
+ ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\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());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000008 string pool index offset\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool byte length\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS " 07 type: reference \\(dynamic\\)\n", stream.str());
+ 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());
}
TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
@@ -68,11 +101,23 @@
RawPrintVisitor visitor(stream);
(*idmap)->accept(&visitor);
- ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos);
- ASSERT_NE(stream.str().find("00000008: 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: 00000000 0x7f020000 -> 0x7f020000\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/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
new file mode 100644
index 0000000..de039f4
--- /dev/null
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio> // fclose
+#include <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "R.h"
+#include "TestHelpers.h"
+#include "androidfw/ResourceTypes.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/LogInfo.h"
+#include "idmap2/ResourceMapping.h"
+
+using android::Res_value;
+using android::idmap2::utils::ExtractOverlayManifestInfo;
+
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
+namespace android::idmap2 {
+
+#define ASSERT_RESULT(r) \
+ do { \
+ auto result = r; \
+ ASSERT_TRUE(result) << result.GetErrorMessage(); \
+ } while (0)
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path,
+ const OverlayManifestInfo& overlay_info,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ const std::string target_apk_path(GetTestDataPath() + local_target_apk_path.data());
+ std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+ if (!target_apk) {
+ return Error(R"(Failed to load target apk "%s")", target_apk_path.data());
+ }
+
+ const std::string overlay_apk_path(GetTestDataPath() + local_overlay_apk_path.data());
+ std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+ if (!overlay_apk) {
+ return Error(R"(Failed to load overlay apk "%s")", overlay_apk_path.data());
+ }
+
+ LogInfo log_info;
+ return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, fulfilled_policies,
+ enforce_overlayable, log_info);
+}
+
+Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local_target_apk_path,
+ const android::StringPiece& local_overlay_apk_path,
+ const PolicyBitmask& fulfilled_policies,
+ bool enforce_overlayable) {
+ auto overlay_info = ExtractOverlayManifestInfo(GetTestDataPath() + local_overlay_apk_path.data());
+ if (!overlay_info) {
+ return overlay_info.GetError();
+ }
+ return TestGetResourceMapping(local_target_apk_path, local_overlay_apk_path, *overlay_info,
+ fulfilled_policies, enforce_overlayable);
+}
+
+Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
+ const uint8_t type, const uint32_t value, bool rewrite) {
+ auto target_map = mapping.GetTargetToOverlayMap();
+ auto entry_map = target_map.find(target_resource);
+ if (entry_map == target_map.end()) {
+ return Error("Failed to find mapping for target resource");
+ }
+
+ if (entry_map->second.data_type != type) {
+ return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
+ entry_map->second.data_type);
+ }
+
+ if (entry_map->second.data_value != value) {
+ return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
+ entry_map->second.data_value);
+ }
+
+ auto overlay_map = mapping.GetOverlayToTargetMap();
+ auto overlay_iter = overlay_map.find(entry_map->second.data_value);
+ if ((overlay_iter != overlay_map.end()) != rewrite) {
+ return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false");
+ }
+
+ return Result<Unit>({});
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0U; // no xml
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
+ R::overlay::integer::int1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str3, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str4, false /* rewrite */));
+}
+
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030003; // xml/overlays_swap
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str4, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str1, true /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+ R::overlay::string::str3, true /* rewrite */));
+}
+
+TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030001; // xml/overlays_different_packages
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+ ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x0104000a,
+ false /* rewrite */)); // -> android:string/ok
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+ 0x7f020001, true /* rewrite */));
+}
+
+TEST(ResourceMappingTests, InlineResources) {
+ OverlayManifestInfo info{};
+ info.target_package = "test.target";
+ info.target_name = "TestResources";
+ info.resource_mapping = 0x7f030002; // xml/overlays_inline
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay.apk", info,
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ constexpr size_t overlay_string_pool_size = 8U;
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
+ ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_STRING,
+ overlay_string_pool_size + 0U,
+ false /* rewrite */)); // -> "Hello World"
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U,
+ false /* rewrite */)); // -> 73
+}
+
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
+ auto resources =
+ TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk",
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_public, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_system, false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay::string::policy_system_vendor, false /* rewrite */));
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfill must not map to overlay resources.
+TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
+ auto resources = TestGetResourceMapping("/target/target.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+}
+
+// Resources that are not declared as overlayable and resources that a protected by policies the
+// overlay does not fulfilled can map to overlay resources when overlayable enforcement is turned
+// off.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
+ auto resources = TestGetResourceMapping("/target/target.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ PolicyFlags::SYSTEM_PARTITION | PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 10U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::not_overlayable,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::other, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_actor,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_odm, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_oem, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_product,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_signature,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(
+ MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+}
+
+// Overlays that do not target an <overlayable> tag can overlay resources defined within any
+// <overlayable> tag.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTargetName) {
+ auto resources = TestGetResourceMapping("/target/target.apk", "/overlay/overlay-no-name.apk",
+ PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ false);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
+ ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE,
+ R::overlay::integer::int1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str1, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str3, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE,
+ R::overlay::string::str4, false /* rewrite */));
+}
+
+// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
+// overlay packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
+ auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
+ "/overlay/overlay-no-name.apk", PolicyFlags::PUBLIC,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
+}
+
+// Overlays that are pre-installed or are signed with the same signature as the target can overlay
+// packages that have not defined overlayable resources.
+TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
+ auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
+ auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
+ "/system-overlay-invalid/system-overlay-invalid.apk",
+ fulfilled_policies,
+ /* enforce_overlayable */ true);
+
+ ASSERT_TRUE(resources) << resources.GetErrorMessage();
+ auto& res = *resources;
+ ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 10U);
+ ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::not_overlayable,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::other, false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_actor,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_odm,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_oem,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_product,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_public,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_signature,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system,
+ false /* rewrite */));
+ ASSERT_RESULT(MappingExists(
+ res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
+ R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
+ };
+
+ CheckEntries(PolicyFlags::SIGNATURE);
+ CheckEntries(PolicyFlags::PRODUCT_PARTITION);
+ CheckEntries(PolicyFlags::SYSTEM_PARTITION);
+ CheckEntries(PolicyFlags::VENDOR_PARTITION);
+ CheckEntries(PolicyFlags::ODM_PARTITION);
+ CheckEntries(PolicyFlags::OEM_PARTITION);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h
new file mode 100644
index 0000000..6bc924e
--- /dev/null
+++ b/cmds/idmap2/tests/TestConstants.h
@@ -0,0 +1,30 @@
+/*
+ * 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_TESTS_TESTCONSTANTS_H
+#define IDMAP2_TESTS_TESTCONSTANTS_H
+
+namespace android::idmap2::TestConstants {
+
+constexpr const auto TARGET_CRC = 0x41c60c8c;
+constexpr const auto TARGET_CRC_STRING = "41c60c8c";
+
+constexpr const auto OVERLAY_CRC = 0xc054fb26;
+constexpr const auto OVERLAY_CRC_STRING = "c054fb26";
+
+} // namespace android::idmap2::TestConstants
+
+#endif // IDMAP2_TESTS_TESTCONSTANTS_H
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index adea329..b599dcb 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -30,7 +30,7 @@
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
- 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -38,8 +38,14 @@
// 0xc: overlay crc
0x78, 0x56, 0x00, 0x00,
- // 0x10: target path "target.apk"
- 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 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,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -56,8 +62,8 @@
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 "overlay.apk"
- 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // 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,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -74,56 +80,77 @@
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,
+ // 0x215: debug string
+ // string length, including terminating null
+ 0x08, 0x00, 0x00, 0x00,
+
+ // string contents "debug\0\0\0" (padded to word alignment)
+ 0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
+
// DATA HEADER
- // 0x210: target package id
- 0x7f, 0x00,
+ // 0x221: target_package_id
+ 0x7f,
- // 0x212: types count
- 0x02, 0x00,
+ // 0x222: overlay_package_id
+ 0x7f,
- // DATA BLOCK
- // 0x214: target type
- 0x02, 0x00,
+ // 0x223: target_entry_count
+ 0x03, 0x00, 0x00, 0x00,
- // 0x216: overlay type
- 0x02, 0x00,
+ // 0x227: overlay_entry_count
+ 0x03, 0x00, 0x00, 0x00,
- // 0x218: entry count
- 0x01, 0x00,
-
- // 0x21a: entry offset
- 0x00, 0x00,
-
- // 0x21c: entries
+ // 0x22b: string_pool_offset
0x00, 0x00, 0x00, 0x00,
- // DATA BLOCK
- // 0x220: target type
- 0x03, 0x00,
-
- // 0x222: overlay type
- 0x03, 0x00,
-
- // 0x224: entry count
- 0x03, 0x00,
-
- // 0x226: entry offset
- 0x03, 0x00,
-
- // 0x228, 0x22c, 0x230: entries
+ // 0x22f: string_pool_byte_length
0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff,
+ // TARGET ENTRIES
+ // 0x233: 0x7f020000
+ 0x00, 0x00, 0x02, 0x7f,
- 0x01, 0x00, 0x00, 0x00};
+ // 0x237: TYPE_REFERENCE
+ 0x01,
-const unsigned int idmap_raw_data_len = 565;
+ // 0x238: 0x7f020000
+ 0x00, 0x00, 0x02, 0x7f,
+
+ // 0x23c: 0x7f030000
+ 0x00, 0x00, 0x03, 0x7f,
+
+ // 0x240: TYPE_REFERENCE
+ 0x01,
+
+ // 0x241: 0x7f030000
+ 0x00, 0x00, 0x03, 0x7f,
+
+ // 0x245: 0x7f030002
+ 0x02, 0x00, 0x03, 0x7f,
+
+ // 0x249: TYPE_REFERENCE
+ 0x01,
+
+ // 0x24a: 0x7f030001
+ 0x01, 0x00, 0x03, 0x7f,
+
+ // OVERLAY ENTRIES
+ // 0x24e: 0x7f020000 -> 0x7f020000
+ 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
+
+ // 0x256: 0x7f030000 -> 0x7f030000
+ 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
+
+ // 0x25e: 0x7f030001 -> 0x7f030002
+ 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f};
+
+const unsigned int idmap_raw_data_len = 0x266;
std::string GetTestDataPath();
class Idmap2Tests : public testing::Test {
protected:
- virtual void SetUp() {
+ void SetUp() override {
#ifdef __ANDROID__
tmp_dir_path_ = "/data/local/tmp/idmap2-tests-XXXXXX";
#else
@@ -136,7 +163,7 @@
idmap_path_ = tmp_dir_path_ + "/a.idmap";
}
- virtual void TearDown() {
+ void TearDown() override {
EXPECT_EQ(rmdir(tmp_dir_path_.c_str()), 0)
<< "Failed to remove temporary directory " << tmp_dir_path_ << ": " << strerror(errno);
}
diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp
new file mode 100644
index 0000000..1a7eaca
--- /dev/null
+++ b/cmds/idmap2/tests/XmlParserTests.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio> // fclose
+#include <memory>
+#include <string>
+
+#include "TestHelpers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "idmap2/XmlParser.h"
+#include "idmap2/ZipFile.h"
+
+namespace android::idmap2 {
+
+Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) {
+ auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
+ if (zip == nullptr) {
+ return Error("Failed to open zip file");
+ }
+
+ auto data = zip->Uncompress(test_file);
+ if (data == nullptr) {
+ return Error("Failed to open xml file");
+ }
+
+ return XmlParser::Create(data->buf, data->size, /* copy_data */ true);
+}
+
+TEST(XmlParserTests, Create) {
+ auto xml = CreateTestParser("AndroidManifest.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ fclose(stderr); // silence expected warnings from libandroidfw
+ const char* not_xml = "foo";
+ auto fail = XmlParser::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
+ ASSERT_FALSE(fail);
+}
+
+TEST(XmlParserTests, NextChild) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ auto root_iter = (*xml)->tree_iterator();
+ ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(root_iter->name(), "a");
+
+ auto a_iter = root_iter.begin();
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(a_iter->name(), "b");
+
+ auto c_iter = a_iter.begin();
+ ASSERT_EQ(c_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(c_iter->name(), "c");
+
+ ++c_iter;
+ ASSERT_EQ(c_iter->event(), XmlParser::Event::END_TAG);
+ ASSERT_EQ(c_iter, a_iter.end());
+
+ ++a_iter;
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG);
+ ASSERT_EQ(a_iter->name(), "d");
+
+ // Skip the <e> tag.
+ ++a_iter;
+ ASSERT_EQ(a_iter->event(), XmlParser::Event::END_TAG);
+ ASSERT_EQ(a_iter, root_iter.end());
+}
+
+TEST(XmlParserTests, AttributeValues) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter = (*xml)->tree_iterator();
+
+ // Start at the <b> tag.
+ auto a_iter = root_iter.begin();
+ auto attribute_str = a_iter->GetAttributeStringValue("type_string");
+ ASSERT_TRUE(attribute_str);
+ ASSERT_EQ(*attribute_str, "fortytwo");
+
+ auto attribute_value = a_iter->GetAttributeValue("type_int_dec");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 42);
+
+ attribute_value = a_iter->GetAttributeValue("type_int_hex");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 42);
+
+ attribute_value = a_iter->GetAttributeValue("type_int_boolean");
+ ASSERT_TRUE(attribute_value);
+ ASSERT_EQ(attribute_value->data, 0xffffffff);
+}
+
+TEST(XmlParserTests, IteratorEquality) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter_1 = (*xml)->tree_iterator();
+ auto root_iter_2 = (*xml)->tree_iterator();
+ ASSERT_EQ(root_iter_1, root_iter_2);
+ ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+ // Start at the <b> tag.
+ auto a_iter_1 = root_iter_1.begin();
+ auto a_iter_2 = root_iter_2.begin();
+ ASSERT_NE(a_iter_1, root_iter_1.end());
+ ASSERT_NE(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+ // Move to the <d> tag.
+ ++a_iter_1;
+ ++a_iter_2;
+ ASSERT_NE(a_iter_1, root_iter_1.end());
+ ASSERT_NE(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+
+ // Move to the end of the <a> tag.
+ ++a_iter_1;
+ ++a_iter_2;
+ ASSERT_EQ(a_iter_1, root_iter_1.end());
+ ASSERT_EQ(a_iter_2, root_iter_2.end());
+ ASSERT_EQ(a_iter_1, a_iter_2);
+ ASSERT_EQ(*a_iter_1, *a_iter_2);
+}
+
+TEST(XmlParserTests, Backtracking) {
+ auto xml = CreateTestParser("res/xml/test.xml");
+ ASSERT_TRUE(xml) << xml.GetErrorMessage();
+
+ // Start at the <a> tag.
+ auto root_iter_1 = (*xml)->tree_iterator();
+
+ // Start at the <b> tag.
+ auto a_iter_1 = root_iter_1.begin();
+
+ // Start a second iterator at the <a> tag.
+ auto root_iter_2 = root_iter_1;
+ ASSERT_EQ(root_iter_1, root_iter_2);
+ ASSERT_EQ(*root_iter_1, *root_iter_2);
+
+ // Move the first iterator to the end of the <a> tag.
+ auto root_iter_end_1 = root_iter_1.end();
+ ++root_iter_1;
+ ASSERT_NE(root_iter_1, root_iter_2);
+ ASSERT_NE(*root_iter_1, *root_iter_2);
+
+ // Move to the <d> tag.
+ ++a_iter_1;
+ ASSERT_NE(a_iter_1, root_iter_end_1);
+
+ // Move to the end of the <a> tag.
+ ++a_iter_1;
+ ASSERT_EQ(a_iter_1, root_iter_end_1);
+}
+
+} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp
deleted file mode 100644
index df63211..0000000
--- a/cmds/idmap2/tests/XmlTests.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cstdio> // fclose
-
-#include "TestHelpers.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "idmap2/Xml.h"
-#include "idmap2/ZipFile.h"
-
-using ::testing::IsNull;
-using ::testing::NotNull;
-
-namespace android::idmap2 {
-
-TEST(XmlTests, Create) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- auto data = zip->Uncompress("AndroidManifest.xml");
- ASSERT_THAT(data, NotNull());
-
- auto xml = Xml::Create(data->buf, data->size);
- ASSERT_THAT(xml, NotNull());
-
- fclose(stderr); // silence expected warnings from libandroidfw
- const char* not_xml = "foo";
- auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml));
- ASSERT_THAT(fail, IsNull());
-}
-
-TEST(XmlTests, FindTag) {
- auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk");
- ASSERT_THAT(zip, NotNull());
-
- auto data = zip->Uncompress("res/xml/test.xml");
- ASSERT_THAT(data, NotNull());
-
- auto xml = Xml::Create(data->buf, data->size);
- ASSERT_THAT(xml, NotNull());
-
- auto attrs = xml->FindTag("c");
- ASSERT_THAT(attrs, NotNull());
- ASSERT_EQ(attrs->size(), 4U);
- ASSERT_EQ(attrs->at("type_string"), "fortytwo");
- ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42);
- ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42);
- ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U);
-
- auto fail = xml->FindTag("does-not-exist");
- ASSERT_THAT(fail, IsNull());
-}
-
-} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
index 619bb6c..cf3691c 100644
--- a/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
+++ b/cmds/idmap2/tests/data/overlay/AndroidManifest.xml
@@ -16,8 +16,11 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="test.overlay">
+
<application android:hasCode="false"/>
+
<overlay
android:targetPackage="test.target"
- android:targetName="TestResources"/>
+ android:targetName="TestResources"
+ android:resourcesMap="@xml/overlays"/>
</manifest>
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
index 68b9f50..114b099 100755
--- a/cmds/idmap2/tests/data/overlay/build
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-FRAMEWORK_RES_APK=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar
+FRAMEWORK_RES_APK=${ANDROID_PRODUCT_OUT}/system/framework/framework-res.apk
aapt2 compile --dir res -o compiled.flata
@@ -51,4 +51,12 @@
-o overlay-static-2.apk \
compiled.flata
+aapt2 link \
+ --no-resource-removal \
+ --shared-lib \
+ -I "$FRAMEWORK_RES_APK" \
+ --manifest AndroidManifest.xml \
+ -o overlay-shared.apk \
+ compiled.flata
+
rm compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
index 18ee43d..7c25985 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
index 6425190..c75f3e1 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-shared.apk b/cmds/idmap2/tests/data/overlay/overlay-shared.apk
new file mode 100644
index 0000000..93dcc82
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-shared.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
index 642ab90..5b8a6e4 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
index 2ec5602..698a1fd 100644
--- a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk
index 5842da4..1db303f 100644
--- a/cmds/idmap2/tests/data/overlay/overlay.apk
+++ b/cmds/idmap2/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
new file mode 100644
index 0000000..edd33f7
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <item target="string/str1" value="@string/str1"/>
+ <item target="string/str3" value="@string/str3" />
+ <item target="string/str4" value="@string/str4" />
+ <item target="integer/int1" value="@integer/int1" />
+ <item target="integer/not_in_target" value="@integer/not_in_target" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
new file mode 100644
index 0000000..aa7fefa
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_different_package.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <item target="string/str1" value="@android:string/ok"/>
+ <item target="string/str3" value="@string/str3" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
new file mode 100644
index 0000000..e12b823
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_inline.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <item target="string/str1" value="Hello World"/>
+ <item target="integer/int1" value="73" />
+</overlay>
+
diff --git a/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
new file mode 100644
index 0000000..5728e67
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/res/xml/overlays_swap.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<overlay>
+ <item target="string/str1" value="@string/str4"/>
+ <item target="string/str3" value="@string/str1" />
+ <item target="string/str4" value="@string/str3" />
+ <item target="integer/int_not_in_target" value="@integer/int1" />
+</overlay>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
index 9ebfae4..7119d82 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
@@ -25,6 +25,7 @@
<string name="policy_signature">policy_signature</string>
<string name="policy_odm">policy_odm</string>
<string name="policy_oem">policy_oem</string>
+ <string name="policy_actor">policy_actor</string>
<!-- Requests to overlay a resource that is not declared as overlayable. -->
<string name="not_overlayable">not_overlayable</string>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
index 1456e74..bd99098 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
index 8389f56..ad4cd48 100644
--- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml
+++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
@@ -41,6 +41,10 @@
<item type="string" name="policy_oem" />
</policy>
+ <policy type="actor">
+ <item type="string" name="policy_actor" />
+ </policy>
+
<!-- Resources publicly overlayable -->
<policy type="public">
<item type="string" name="policy_public" />
@@ -63,4 +67,4 @@
<item type="string" name="other" />
</policy>
</overlayable>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
index a892c98..5230e25 100644
--- a/cmds/idmap2/tests/data/target/res/values/values.xml
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -36,6 +36,7 @@
<string name="policy_signature">policy_signature</string>
<string name="policy_system">policy_system</string>
<string name="policy_system_vendor">policy_system_vendor</string>
+ <string name="policy_actor">policy_actor</string>
<string name="other">other</string>
</resources>
diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml
index 0fe21c6..56a3f7f 100644
--- a/cmds/idmap2/tests/data/target/res/xml/test.xml
+++ b/cmds/idmap2/tests/data/target/res/xml/test.xml
@@ -14,12 +14,15 @@
limitations under the License.
-->
<a>
- <b>
- <c
- type_string="fortytwo"
- type_int_dec="42"
- type_int_hex="0x2a"
- type_int_boolean="true"
- />
+ <b type_string="fortytwo"
+ type_int_dec="42"
+ type_int_hex="0x2a"
+ type_int_boolean="true">
+
+ <c />
</b>
-</a>
+
+ <d>
+ <e />
+ </d>
+</a>
\ No newline at end of file
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index 033305a..58504a7 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 9bcd6dc..c80e5eb 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh
new file mode 100755
index 0000000..b4ebab0
--- /dev/null
+++ b/cmds/idmap2/valgrind.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+function _log()
+{
+ echo -e "$*" >&2
+}
+
+function _eval()
+{
+ local label="$1"
+ local cmd="$2"
+ local red="\e[31m"
+ local green="\e[32m"
+ local reset="\e[0m"
+ local output
+
+ _log "${green}[ RUN ]${reset} ${label}"
+ output="$(eval "$cmd" 2>&1)"
+ if [[ $? -eq 0 ]]; then
+ _log "${green}[ OK ]${reset} ${label}"
+ return 0
+ else
+ echo "${output}"
+ _log "${red}[ FAILED ]${reset} ${label}"
+ errors=$((errors + 1))
+ return 1
+ fi
+}
+
+errors=0
+script="$(readlink -f "$BASH_SOURCE")"
+prefix="$(dirname "$script")"
+target_path="${prefix}/tests/data/target/target.apk"
+overlay_path="${prefix}/tests/data/overlay/overlay.apk"
+idmap_path="/tmp/a.idmap"
+valgrind="valgrind --error-exitcode=1 -q --track-origins=yes --leak-check=full"
+
+_eval "idmap2 create" "$valgrind idmap2 create --policy public --target-apk-path $target_path --overlay-apk-path $overlay_path --idmap-path $idmap_path"
+_eval "idmap2 dump" "$valgrind idmap2 dump --idmap-path $idmap_path"
+_eval "idmap2 lookup" "$valgrind idmap2 lookup --idmap-path $idmap_path --config '' --resid test.target:string/str1"
+_eval "idmap2 scan" "$valgrind idmap2 scan --input-directory ${prefix}/tests/data/overlay --recursive --target-package-name test.target --target-apk-path $target_path --output-directory /tmp --override-policy public"
+_eval "idmap2 verify" "$valgrind idmap2 verify --idmap-path $idmap_path"
+_eval "idmap2_tests" "$valgrind $ANDROID_HOST_OUT/nativetest64/idmap2_tests/idmap2_tests"
+exit $errors
diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp
index 9e9dac1..94855aa 100644
--- a/cmds/incident/Android.bp
+++ b/cmds/incident/Android.bp
@@ -26,7 +26,7 @@
"libcutils",
"liblog",
"libutils",
- "libincident",
+ "libincidentpriv",
],
static_libs: [
diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp
index d6c6c39..6e0bd06 100644
--- a/cmds/incident/main.cpp
+++ b/cmds/incident/main.cpp
@@ -231,6 +231,7 @@
fprintf(out, " -l list available sections\n");
fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\n");
fprintf(out, " -r REASON human readable description of why the report is taken.\n");
+ fprintf(out, " -z gzip the incident report, i.e. pipe the output through gzip.\n");
fprintf(out, "\n");
fprintf(out, "and one of these destinations:\n");
fprintf(out, " -b (default) print the report to stdout (in proto format)\n");
@@ -255,7 +256,7 @@
// Parse the args
int opt;
- while ((opt = getopt(argc, argv, "bhdlp:r:s:u")) != -1) {
+ while ((opt = getopt(argc, argv, "bhdlp:r:s:uz")) != -1) {
switch (opt) {
case 'h':
usage(stdout);
@@ -302,6 +303,9 @@
destination = DEST_BROADCAST;
receiverArg = optarg;
break;
+ case 'z':
+ args.setGzip(true);
+ break;
default:
usage(stderr);
return 1;
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index d7b6d69..f07743e 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -1,3 +1,28 @@
+// Copyright (C) 2017 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.
+
+java_binary {
+ name: "incident-helper-cmd",
+ wrapper: "incident_helper_cmd",
+ srcs: [
+ "java/**/*.java",
+ ],
+ proto: {
+ plugin: "javastream",
+ },
+}
+
cc_defaults {
name: "incident_helper_defaults",
@@ -19,7 +44,7 @@
"src/ih_util.cpp",
],
- generated_headers: ["gen-platform-proto-constants"],
+ generated_headers: ["framework-cppstream-protos"],
shared_libs: [
"libbase",
diff --git a/cmds/incident_helper/incident_helper_cmd b/cmds/incident_helper/incident_helper_cmd
new file mode 100644
index 0000000..d45f7df
--- /dev/null
+++ b/cmds/incident_helper/incident_helper_cmd
@@ -0,0 +1,6 @@
+#!/system/bin/sh
+# Script to start "incident_helper_cmd" on the device
+#
+base=/system
+export CLASSPATH=$base/framework/incident-helper-cmd.jar
+exec app_process $base/bin com.android.commands.incident.IncidentHelper "$@"
diff --git a/cmds/incident_helper/java/com/android/commands/incident/ExecutionException.java b/cmds/incident_helper/java/com/android/commands/incident/ExecutionException.java
new file mode 100644
index 0000000..d97b17e
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/ExecutionException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package com.android.commands.incident;
+
+/**
+ * Thrown when there is an error executing a section.
+ */
+public class ExecutionException extends Exception {
+ /**
+ * Constructs a ExecutionException.
+ *
+ * @param msg the message
+ */
+ public ExecutionException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs a ExecutionException from another exception.
+ *
+ * @param e the exception
+ */
+ public ExecutionException(Exception e) {
+ super(e);
+ }
+}
diff --git a/cmds/incident_helper/java/com/android/commands/incident/IncidentHelper.java b/cmds/incident_helper/java/com/android/commands/incident/IncidentHelper.java
new file mode 100644
index 0000000..e5874e0
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/IncidentHelper.java
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+package com.android.commands.incident;
+
+import android.util.Log;
+
+import com.android.commands.incident.sections.PersistLogSection;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Helper command runner for incidentd to run customized command to gather data for a non-standard
+ * section.
+ */
+public class IncidentHelper {
+ private static final String TAG = "IncidentHelper";
+ private static boolean sLog = false;
+ private final List<String> mArgs;
+ private ListIterator<String> mArgsIterator;
+
+ private IncidentHelper(String[] args) {
+ mArgs = Collections.unmodifiableList(Arrays.asList(args));
+ mArgsIterator = mArgs.listIterator();
+ }
+
+ private static void showUsage(PrintStream out) {
+ out.println("This command is not designed to be run manually.");
+ out.println("Usage:");
+ out.println(" run [sectionName]");
+ }
+
+ private void run(String[] args) throws ExecutionException {
+ Section section = null;
+ List<String> sectionArgs = new ArrayList<>();
+ while (mArgsIterator.hasNext()) {
+ String arg = mArgsIterator.next();
+ if ("-l".equals(arg)) {
+ sLog = true;
+ Log.i(TAG, "Args: [" + String.join(",", args) + "]");
+ } else if ("run".equals(arg)) {
+ section = getSection(nextArgRequired());
+ mArgsIterator.forEachRemaining(sectionArgs::add);
+ break;
+ } else {
+ log(Log.WARN, TAG, "Error: Unknown argument: " + arg);
+ return;
+ }
+ }
+ section.run(System.in, System.out, sectionArgs);
+ }
+
+ private static Section getSection(String name) throws IllegalArgumentException {
+ if ("persisted_logs".equals(name)) {
+ return new PersistLogSection();
+ }
+ throw new IllegalArgumentException("Section not found: " + name);
+ }
+
+ private String nextArgRequired() {
+ if (!mArgsIterator.hasNext()) {
+ throw new IllegalArgumentException(
+ "Arg required after \"" + mArgs.get(mArgsIterator.previousIndex()) + "\"");
+ }
+ return mArgsIterator.next();
+ }
+
+ /**
+ * Print the given message to stderr, also log it if asked to (set by -l cmd arg).
+ */
+ public static void log(int priority, String tag, String msg) {
+ System.err.println(tag + ": " + msg);
+ if (sLog) {
+ Log.println(priority, tag, msg);
+ }
+ }
+
+ /**
+ * Command-line entry point.
+ *
+ * @param args The command-line arguments
+ */
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ showUsage(System.err);
+ System.exit(0);
+ }
+ IncidentHelper incidentHelper = new IncidentHelper(args);
+ try {
+ incidentHelper.run(args);
+ } catch (IllegalArgumentException e) {
+ showUsage(System.err);
+ System.err.println();
+ e.printStackTrace(System.err);
+ if (sLog) {
+ Log.e(TAG, "Error: ", e);
+ }
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
+ if (sLog) {
+ Log.e(TAG, "Error: ", e);
+ }
+ System.exit(1);
+ }
+ }
+}
diff --git a/cmds/incident_helper/java/com/android/commands/incident/Section.java b/cmds/incident_helper/java/com/android/commands/incident/Section.java
new file mode 100644
index 0000000..1c8c657
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/Section.java
@@ -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.
+ */
+
+package com.android.commands.incident;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+/** Section interface used by {@link IncidentHelper}. */
+public interface Section {
+ /**
+ * Writes protobuf wire format to out, optionally reads data from in, with supplied args.
+ */
+ void run(InputStream in, OutputStream out, List<String> args) throws ExecutionException;
+}
diff --git a/cmds/incident_helper/java/com/android/commands/incident/sections/PersistLogSection.java b/cmds/incident_helper/java/com/android/commands/incident/sections/PersistLogSection.java
new file mode 100644
index 0000000..f9d2e79
--- /dev/null
+++ b/cmds/incident_helper/java/com/android/commands/incident/sections/PersistLogSection.java
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+package com.android.commands.incident.sections;
+
+import android.util.Log;
+import android.util.PersistedLogProto;
+import android.util.TextLogEntry;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.commands.incident.ExecutionException;
+import com.android.commands.incident.IncidentHelper;
+import com.android.commands.incident.Section;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+/** PersistLogSection reads persisted logs and parses them into a PersistedLogProto. */
+public class PersistLogSection implements Section {
+ private static final String TAG = "IH_PersistLog";
+ private static final String LOG_DIR = "/data/misc/logd/";
+ // Persist log files are named logcat, logcat.001, logcat.002, logcat.003, ...
+ private static final Pattern LOG_FILE_RE = Pattern.compile("logcat(\\.\\d+)?");
+ private static final Pattern BUFFER_BEGIN_RE =
+ Pattern.compile("--------- (?:beginning of|switch to) (.*)");
+ private static final Map<String, Long> SECTION_NAME_TO_ID = new HashMap<>();
+ private static final Map<Character, Integer> LOG_PRIORITY_MAP = new HashMap<>();
+ private static final String DEFAULT_BUFFER = "main";
+
+ static {
+ SECTION_NAME_TO_ID.put("main", PersistedLogProto.MAIN_LOGS);
+ SECTION_NAME_TO_ID.put("radio", PersistedLogProto.RADIO_LOGS);
+ SECTION_NAME_TO_ID.put("events", PersistedLogProto.EVENTS_LOGS);
+ SECTION_NAME_TO_ID.put("system", PersistedLogProto.SYSTEM_LOGS);
+ SECTION_NAME_TO_ID.put("crash", PersistedLogProto.CRASH_LOGS);
+ SECTION_NAME_TO_ID.put("kernel", PersistedLogProto.KERNEL_LOGS);
+ }
+
+ static {
+ LOG_PRIORITY_MAP.put('V', TextLogEntry.LOG_VERBOSE);
+ LOG_PRIORITY_MAP.put('D', TextLogEntry.LOG_DEBUG);
+ LOG_PRIORITY_MAP.put('I', TextLogEntry.LOG_INFO);
+ LOG_PRIORITY_MAP.put('W', TextLogEntry.LOG_WARN);
+ LOG_PRIORITY_MAP.put('E', TextLogEntry.LOG_ERROR);
+ LOG_PRIORITY_MAP.put('F', TextLogEntry.LOG_FATAL);
+ LOG_PRIORITY_MAP.put('S', TextLogEntry.LOG_SILENT);
+ }
+
+ /**
+ * Caches dates at 00:00:00 to epoch second elapsed conversion. There are only a few different
+ * dates in persisted logs in one device, and constructing DateTime object is relatively
+ * expensive.
+ */
+ private Map<Integer, Long> mEpochTimeCache = new HashMap<>();
+ private ProtoOutputStream mProto;
+ private long mCurrFieldId;
+ private long mMaxBytes = Long.MAX_VALUE;
+
+ @Override
+ public void run(InputStream in, OutputStream out, List<String> args) throws ExecutionException {
+ parseArgs(args);
+ Path logDirPath = Paths.get(LOG_DIR);
+ if (!Files.exists(logDirPath)) {
+ IncidentHelper.log(Log.WARN, TAG, "Skip dump. " + logDirPath + " does not exist.");
+ return;
+ }
+ if (!Files.isReadable(logDirPath)) {
+ IncidentHelper.log(Log.WARN, TAG, "Skip dump. " + logDirPath + " is not readable.");
+ return;
+ }
+ mProto = new ProtoOutputStream(out);
+ setCurrentSection(DEFAULT_BUFFER);
+ final Matcher logFileRe = LOG_FILE_RE.matcher("");
+ // Need to process older log files first and write logs to proto in chronological order
+ // But we want to process only the latest ones if there is a size limit
+ try (Stream<File> stream = Files.list(logDirPath).map(Path::toFile)
+ .filter(f -> !f.isDirectory() && match(logFileRe, f.getName()) != null)
+ .sorted(Comparator.comparingLong(File::lastModified).reversed())) {
+ Iterator<File> iter = stream.iterator();
+ List<File> filesToProcess = new ArrayList<>();
+ long sumBytes = 0;
+ while (iter.hasNext()) {
+ File file = iter.next();
+ sumBytes += file.length();
+ if (sumBytes > mMaxBytes) {
+ break;
+ }
+ filesToProcess.add(file);
+ }
+ IncidentHelper.log(Log.INFO, TAG, "Limit # log files to " + filesToProcess.size());
+ filesToProcess.stream()
+ .sorted(Comparator.comparingLong(File::lastModified))
+ .forEachOrdered(this::processFile);
+ } catch (IOException e) {
+ throw new ExecutionException(e);
+ } finally {
+ mProto.flush();
+ }
+ IncidentHelper.log(Log.DEBUG, TAG, "Bytes written: " + mProto.getBytes().length);
+ }
+
+ private void parseArgs(List<String> args) {
+ Iterator<String> iter = args.iterator();
+ while (iter.hasNext()) {
+ String arg = iter.next();
+ if ("--limit".equals(arg) && iter.hasNext()) {
+ String sizeStr = iter.next().toLowerCase();
+ if (sizeStr.endsWith("mb")) {
+ mMaxBytes = Long.parseLong(sizeStr.replace("mb", "")) * 1024 * 1024;
+ } else if (sizeStr.endsWith("kb")) {
+ mMaxBytes = Long.parseLong(sizeStr.replace("kb", "")) * 1024;
+ } else {
+ mMaxBytes = Long.parseLong(sizeStr);
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown argument: " + arg);
+ }
+ }
+ }
+
+ private void processFile(File file) {
+ final Matcher bufferBeginRe = BUFFER_BEGIN_RE.matcher("");
+ try (BufferedReader reader = Files.newBufferedReader(file.toPath(),
+ StandardCharsets.UTF_8)) {
+ String line;
+ Matcher m;
+ while ((line = reader.readLine()) != null) {
+ if ((m = match(bufferBeginRe, line)) != null) {
+ setCurrentSection(m.group(1));
+ continue;
+ }
+ parseLine(line);
+ }
+ } catch (IOException e) {
+ // Non-fatal error. We can skip and still process other files.
+ IncidentHelper.log(Log.WARN, TAG, "Error reading \"" + file + "\": " + e.getMessage());
+ }
+ IncidentHelper.log(Log.DEBUG, TAG, "Finished reading " + file);
+ }
+
+ private void setCurrentSection(String sectionName) {
+ Long sectionId = SECTION_NAME_TO_ID.get(sectionName);
+ if (sectionId == null) {
+ IncidentHelper.log(Log.WARN, TAG, "Section does not exist: " + sectionName);
+ sectionId = SECTION_NAME_TO_ID.get(DEFAULT_BUFFER);
+ }
+ mCurrFieldId = sectionId;
+ }
+
+ /**
+ * Parse a log line in the following format:
+ * 01-01 15:01:47.723501 2738 2895 I Exp_TAG: example log line
+ *
+ * It does not use RegExp for performance reasons. Using this RegExp "(\\d{2})-(\\d{2})\\s
+ * (\\d{2}):(\\d{2}):(\\d{2}).(\\d{6})\\s+(\\d+)\\s+(\\d+)\\s+(.)\\s+(.*?):\\s(.*)" is twice as
+ * slow as the current approach.
+ */
+ private void parseLine(String line) {
+ long token = mProto.start(mCurrFieldId);
+ try {
+ mProto.write(TextLogEntry.SEC, getEpochSec(line));
+ // Nanosec is 15th to 20th digits of "10-01 02:57:27.710652" times 1000
+ mProto.write(TextLogEntry.NANOSEC, parseInt(line, 15, 21) * 1000L);
+
+ int start = nextNonBlank(line, 21);
+ int end = line.indexOf(' ', start + 1);
+ mProto.write(TextLogEntry.PID, parseInt(line, start, end));
+
+ start = nextNonBlank(line, end);
+ end = line.indexOf(' ', start + 1);
+ mProto.write(TextLogEntry.TID, parseInt(line, start, end));
+
+ start = nextNonBlank(line, end);
+ char priority = line.charAt(start);
+ mProto.write(TextLogEntry.PRIORITY,
+ LOG_PRIORITY_MAP.getOrDefault(priority, TextLogEntry.LOG_DEFAULT));
+
+ start = nextNonBlank(line, start + 1);
+ end = line.indexOf(": ", start);
+ mProto.write(TextLogEntry.TAG, line.substring(start, end).trim());
+ mProto.write(TextLogEntry.LOG, line.substring(Math.min(end + 2, line.length())));
+ } catch (RuntimeException e) {
+ // Error reporting is likely piped to /dev/null. Inserting it into the proto to make
+ // it more useful.
+ mProto.write(TextLogEntry.SEC, System.currentTimeMillis() / 1000);
+ mProto.write(TextLogEntry.PRIORITY, TextLogEntry.LOG_ERROR);
+ mProto.write(TextLogEntry.TAG, TAG);
+ mProto.write(TextLogEntry.LOG,
+ "Error parsing \"" + line + "\"" + ": " + e.getMessage());
+ }
+ mProto.end(token);
+ }
+
+ // ============== Below are util methods to parse log lines ==============
+
+ private static int nextNonBlank(String line, int start) {
+ for (int i = start; i < line.length(); i++) {
+ if (line.charAt(i) != ' ') {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Gets the epoch second from the line string. Line starts with a fixed-length timestamp like
+ * "10-01 02:57:27.710652"
+ */
+ private long getEpochSec(String line) {
+ int month = getDigit(line, 0) * 10 + getDigit(line, 1);
+ int day = getDigit(line, 3) * 10 + getDigit(line, 4);
+
+ int mmdd = month * 100 + day;
+ long epochSecBase = mEpochTimeCache.computeIfAbsent(mmdd, (key) -> {
+ final GregorianCalendar calendar = new GregorianCalendar();
+ calendar.set(Calendar.MONTH, (month + 12 - 1) % 12);
+ calendar.set(Calendar.DAY_OF_MONTH, day);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ // Date in log entries can never be in the future. If it happens, it means we are off
+ // by one year.
+ if (calendar.getTimeInMillis() > System.currentTimeMillis()) {
+ calendar.roll(Calendar.YEAR, /*amount=*/-1);
+ }
+ return calendar.getTimeInMillis() / 1000;
+ });
+
+ int hh = getDigit(line, 6) * 10 + getDigit(line, 7);
+ int mm = getDigit(line, 9) * 10 + getDigit(line, 10);
+ int ss = getDigit(line, 12) * 10 + getDigit(line, 13);
+ return epochSecBase + hh * 3600 + mm * 60 + ss;
+ }
+
+ private static int parseInt(String line, /*inclusive*/ int start, /*exclusive*/ int end) {
+ int num = 0;
+ for (int i = start; i < end; i++) {
+ num = num * 10 + getDigit(line, i);
+ }
+ return num;
+ }
+
+ private static int getDigit(String str, int pos) {
+ int digit = str.charAt(pos) - '0';
+ if (digit < 0 || digit > 9) {
+ throw new NumberFormatException("'" + str.charAt(pos) + "' is not a digit.");
+ }
+ return digit;
+ }
+
+ private static Matcher match(Matcher matcher, String text) {
+ matcher.reset(text);
+ return matcher.matches() ? matcher : null;
+ }
+}
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index 77a56e5..557fe9f 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -237,33 +237,38 @@
Reader::Reader(const int fd)
{
mFile = fdopen(fd, "r");
+ mBuffer = new char[1024];
mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
}
Reader::~Reader()
{
if (mFile != nullptr) fclose(mFile);
+ delete[] mBuffer;
}
bool Reader::readLine(std::string* line) {
if (mFile == nullptr) return false;
- char* buf = nullptr;
size_t len = 0;
- ssize_t read = getline(&buf, &len, mFile);
+ ssize_t read = getline(&mBuffer, &len, mFile);
if (read != -1) {
- std::string s(buf);
+ std::string s(mBuffer);
line->assign(trim(s, DEFAULT_NEWLINE));
- } else if (errno == EINVAL) {
- mStatus = "Bad Argument";
+ return true;
}
- free(buf);
- return read != -1;
+ if (!feof(mFile)) {
+ mStatus = "Error reading file. Ferror: " + std::to_string(ferror(mFile));
+ }
+ return false;
}
bool Reader::ok(std::string* error) {
+ if (mStatus.empty()) {
+ return true;
+ }
error->assign(mStatus);
- return mStatus.empty();
+ return false;
}
// ==============================================================================
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 09dc8e6..5812c60 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -117,6 +117,7 @@
private:
FILE* mFile;
+ char* mBuffer;
std::string mStatus;
};
diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp
index 25e0328..c47526a 100644
--- a/cmds/incidentd/Android.bp
+++ b/cmds/incidentd/Android.bp
@@ -43,7 +43,7 @@
],
local_include_dirs: ["src"],
- generated_headers: ["gen-platform-proto-constants"],
+ generated_headers: ["framework-cppstream-protos"],
proto: {
type: "lite",
@@ -54,7 +54,7 @@
"libbinder",
"libdebuggerd_client",
"libdumputils",
- "libincident",
+ "libincidentpriv",
"liblog",
"libprotoutil",
"libservices",
@@ -98,7 +98,7 @@
],
local_include_dirs: ["src"],
- generated_headers: ["gen-platform-proto-constants"],
+ generated_headers: ["framework-cppstream-protos"],
srcs: [
"tests/**/*.cpp",
@@ -128,7 +128,7 @@
"libbinder",
"libdebuggerd_client",
"libdumputils",
- "libincident",
+ "libincidentpriv",
"liblog",
"libprotobuf-cpp-full",
"libprotoutil",
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index d295b84..78c322e 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -17,6 +17,7 @@
#include "Log.h"
#include "FdBuffer.h"
+#include "incidentd_util.h"
#include <log/log.h>
#include <utils/SystemClock.h>
@@ -31,17 +32,24 @@
namespace incidentd {
const ssize_t BUFFER_SIZE = 16 * 1024; // 16 KB
-const ssize_t MAX_BUFFER_COUNT = 6144; // 96 MB max
+const ssize_t MAX_BUFFER_SIZE = 96 * 1024 * 1024; // 96 MB
-FdBuffer::FdBuffer()
- :mBuffer(new EncodedBuffer(BUFFER_SIZE)),
+FdBuffer::FdBuffer(): FdBuffer(get_buffer_from_pool(), /* isBufferPooled= */ true) {
+}
+
+FdBuffer::FdBuffer(sp<EncodedBuffer> buffer, bool isBufferPooled)
+ :mBuffer(buffer),
mStartTime(-1),
mFinishTime(-1),
mTimedOut(false),
- mTruncated(false) {
+ mTruncated(false),
+ mIsBufferPooled(isBufferPooled) {
}
FdBuffer::~FdBuffer() {
+ if (mIsBufferPooled) {
+ return_buffer_to_pool(mBuffer);
+ }
}
status_t FdBuffer::read(int fd, int64_t timeout) {
@@ -51,7 +59,7 @@
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
while (true) {
- if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ if (mBuffer->size() >= MAX_BUFFER_SIZE) {
mTruncated = true;
VLOG("Truncating data");
break;
@@ -106,7 +114,7 @@
mStartTime = uptimeMillis();
while (true) {
- if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ if (mBuffer->size() >= MAX_BUFFER_SIZE) {
// Don't let it get too big.
mTruncated = true;
VLOG("Truncating data");
@@ -156,7 +164,7 @@
// This is the buffer used to store processed data
while (true) {
- if (mBuffer->size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
+ if (mBuffer->size() >= MAX_BUFFER_SIZE) {
VLOG("Truncating data");
mTruncated = true;
break;
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index a349360..9b2794d 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -35,6 +35,7 @@
class FdBuffer {
public:
FdBuffer();
+ FdBuffer(sp<EncodedBuffer> buffer, bool isBufferPooled = false);
~FdBuffer();
/**
@@ -114,6 +115,7 @@
int64_t mFinishTime;
bool mTimedOut;
bool mTruncated;
+ bool mIsBufferPooled;
};
} // namespace incidentd
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index cfd77c2..dc16125 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -123,14 +123,17 @@
// ================================================================================
ReportHandler::ReportHandler(const sp<WorkDirectory>& workDirectory,
- const sp<Broadcaster>& broadcaster, const sp<Looper>& handlerLooper,
- const sp<Throttler>& throttler)
+ const sp<Broadcaster>& broadcaster,
+ const sp<Looper>& handlerLooper,
+ const sp<Throttler>& throttler,
+ const vector<BringYourOwnSection*>& registeredSections)
:mLock(),
mWorkDirectory(workDirectory),
mBroadcaster(broadcaster),
mHandlerLooper(handlerLooper),
mBacklogDelay(DEFAULT_DELAY_NS),
mThrottler(throttler),
+ mRegisteredSections(registeredSections),
mBatch(new ReportBatch()) {
}
@@ -149,6 +152,7 @@
}
void ReportHandler::schedulePersistedReport(const IncidentReportArgs& args) {
+ unique_lock<mutex> lock(mLock);
mBatch->addPersistedReport(args);
mHandlerLooper->removeMessages(this, WHAT_TAKE_REPORT);
mHandlerLooper->sendMessage(this, Message(WHAT_TAKE_REPORT));
@@ -156,6 +160,7 @@
void ReportHandler::scheduleStreamingReport(const IncidentReportArgs& args,
const sp<IIncidentReportStatusListener>& listener, int streamFd) {
+ unique_lock<mutex> lock(mLock);
mBatch->addStreamingReport(args, listener, streamFd);
mHandlerLooper->removeMessages(this, WHAT_TAKE_REPORT);
mHandlerLooper->sendMessage(this, Message(WHAT_TAKE_REPORT));
@@ -185,7 +190,7 @@
return;
}
- sp<Reporter> reporter = new Reporter(mWorkDirectory, batch);
+ sp<Reporter> reporter = new Reporter(mWorkDirectory, batch, mRegisteredSections);
// Take the report, which might take a while. More requests might queue
// up while we're doing this, and we'll handle them in their next batch.
@@ -237,7 +242,7 @@
mWorkDirectory = new WorkDirectory();
mBroadcaster = new Broadcaster(mWorkDirectory);
mHandler = new ReportHandler(mWorkDirectory, mBroadcaster, handlerLooper,
- mThrottler);
+ mThrottler, mRegisteredSections);
mBroadcaster->setHandler(mHandler);
}
@@ -327,6 +332,11 @@
incidentArgs.addSection(id);
}
}
+ for (const Section* section : mRegisteredSections) {
+ if (!section_requires_specific_mention(section->id)) {
+ incidentArgs.addSection(section->id);
+ }
+ }
// The ReportRequest takes ownership of the fd, so we need to dup it.
int fd = dup(stream.get());
@@ -339,6 +349,46 @@
return Status::ok();
}
+Status IncidentService::registerSection(const int id, const String16& name16,
+ const sp<IIncidentDumpCallback>& callback) {
+ const String8 name = String8(name16);
+ const uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ ALOGI("Uid %d registers section %d '%s'", callingUid, id, name.c_str());
+ if (callback == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+ for (int i = 0; i < mRegisteredSections.size(); i++) {
+ if (mRegisteredSections.at(i)->id == id) {
+ if (mRegisteredSections.at(i)->uid != callingUid) {
+ ALOGW("Error registering section %d: calling uid does not match", id);
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+ mRegisteredSections.at(i) = new BringYourOwnSection(id, name.c_str(), callingUid, callback);
+ return Status::ok();
+ }
+ }
+ mRegisteredSections.push_back(new BringYourOwnSection(id, name.c_str(), callingUid, callback));
+ return Status::ok();
+}
+
+Status IncidentService::unregisterSection(const int id) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ ALOGI("Uid %d unregisters section %d", callingUid, id);
+
+ for (auto it = mRegisteredSections.begin(); it != mRegisteredSections.end(); it++) {
+ if ((*it)->id == id) {
+ if ((*it)->uid != callingUid) {
+ ALOGW("Error unregistering section %d: calling uid does not match", id);
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+ mRegisteredSections.erase(it);
+ return Status::ok();
+ }
+ }
+ ALOGW("Section %d not found", id);
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+}
+
Status IncidentService::systemRunning() {
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index b2c7f23..49fc566 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -40,12 +40,16 @@
using namespace android::binder;
using namespace android::os;
+class BringYourOwnSection;
+
// ================================================================================
class ReportHandler : public MessageHandler {
public:
ReportHandler(const sp<WorkDirectory>& workDirectory,
- const sp<Broadcaster>& broadcaster, const sp<Looper>& handlerLooper,
- const sp<Throttler>& throttler);
+ const sp<Broadcaster>& broadcaster,
+ const sp<Looper>& handlerLooper,
+ const sp<Throttler>& throttler,
+ const vector<BringYourOwnSection*>& registeredSections);
virtual ~ReportHandler();
virtual void handleMessage(const Message& message);
@@ -79,6 +83,8 @@
nsecs_t mBacklogDelay;
sp<Throttler> mThrottler;
+ const vector<BringYourOwnSection*>& mRegisteredSections;
+
sp<ReportBatch> mBatch;
/**
@@ -126,6 +132,11 @@
virtual Status reportIncidentToDumpstate(unique_fd stream,
const sp<IIncidentReportStatusListener>& listener);
+ virtual Status registerSection(int id, const String16& name,
+ const sp<IIncidentDumpCallback>& callback);
+
+ virtual Status unregisterSection(int id);
+
virtual Status systemRunning();
virtual Status getIncidentReportList(const String16& pkg, const String16& cls,
@@ -149,6 +160,7 @@
sp<Broadcaster> mBroadcaster;
sp<ReportHandler> mHandler;
sp<Throttler> mThrottler;
+ vector<BringYourOwnSection*> mRegisteredSections;
/**
* Commands print out help.
diff --git a/cmds/incidentd/src/PrivacyFilter.cpp b/cmds/incidentd/src/PrivacyFilter.cpp
index d00ecdd..0d427d1 100644
--- a/cmds/incidentd/src/PrivacyFilter.cpp
+++ b/cmds/incidentd/src/PrivacyFilter.cpp
@@ -19,9 +19,6 @@
#include "incidentd_util.h"
#include "PrivacyFilter.h"
#include "proto_util.h"
-
-#include "incidentd_util.h"
-#include "proto_util.h"
#include "Section.h"
#include <android-base/file.h>
@@ -129,6 +126,8 @@
FieldStripper(const Privacy* restrictions, const sp<ProtoReader>& data,
uint8_t bufferLevel);
+ ~FieldStripper();
+
/**
* Take the data that we have, and filter it down so that no fields
* are more sensitive than the given privacy policy.
@@ -167,6 +166,7 @@
*/
uint8_t mCurrentLevel;
+ sp<EncodedBuffer> mEncodedBuffer;
};
FieldStripper::FieldStripper(const Privacy* restrictions, const sp<ProtoReader>& data,
@@ -174,19 +174,25 @@
:mRestrictions(restrictions),
mData(data),
mSize(data->size()),
- mCurrentLevel(bufferLevel) {
+ mCurrentLevel(bufferLevel),
+ mEncodedBuffer(get_buffer_from_pool()) {
if (mSize < 0) {
ALOGW("FieldStripper constructed with a ProtoReader that doesn't support size."
" Data will be missing.");
}
}
+FieldStripper::~FieldStripper() {
+ return_buffer_to_pool(mEncodedBuffer);
+}
+
status_t FieldStripper::strip(const uint8_t privacyPolicy) {
// If the current strip level is less (fewer fields retained) than what's already in the
// buffer, then we can skip it.
if (mCurrentLevel < privacyPolicy) {
PrivacySpec spec(privacyPolicy);
- ProtoOutputStream proto;
+ mEncodedBuffer->clear();
+ ProtoOutputStream proto(mEncodedBuffer);
// Optimization when no strip happens.
if (mRestrictions == NULL || spec.RequireAll()) {
@@ -267,7 +273,7 @@
// Order the writes by privacy filter, with increasing levels of filtration,k
// so we can do the filter once, and then write many times.
sort(mOutputs.begin(), mOutputs.end(),
- [](const sp<FilterFd>& a, const sp<FilterFd>& b) -> bool {
+ [](const sp<FilterFd>& a, const sp<FilterFd>& b) -> bool {
return a->getPrivacyPolicy() < b->getPrivacyPolicy();
});
@@ -370,7 +376,7 @@
write_field_or_skip(NULL, reader, fieldTag, true);
}
}
-
+ clear_buffer_pool();
err = reader->getError();
if (err != NO_ERROR) {
ALOGW("filter_and_write_report reader had an error: %s", strerror(-err));
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 02b6bbe..86a78f09 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -35,10 +35,12 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <time.h>
+#include <wait.h>
namespace android {
namespace os {
@@ -51,6 +53,8 @@
* frameworks/base/core/proto/android/os/incident.proto
*/
const int FIELD_ID_METADATA = 2;
+// Args for exec gzip
+static const char* GZIP[] = {"/system/bin/gzip", NULL};
IncidentMetadata_Destination privacy_policy_to_dest(uint8_t privacyPolicy) {
switch (privacyPolicy) {
@@ -142,7 +146,8 @@
mListener(listener),
mFd(fd),
mIsStreaming(fd >= 0),
- mStatus(NO_ERROR) {
+ mStatus(OK),
+ mZipPid(-1) {
}
ReportRequest::~ReportRequest() {
@@ -153,7 +158,14 @@
}
bool ReportRequest::ok() {
- return mFd >= 0 && mStatus == NO_ERROR;
+ if (mStatus != OK) {
+ return false;
+ }
+ if (!args.gzip()) {
+ return mFd >= 0;
+ }
+ // Send a blank signal to check if mZipPid is alive
+ return mZipPid > 0 && kill(mZipPid, 0) == 0;
}
bool ReportRequest::containsSection(int sectionId) const {
@@ -161,10 +173,45 @@
}
void ReportRequest::closeFd() {
- if (mIsStreaming && mFd >= 0) {
+ if (!mIsStreaming) {
+ return;
+ }
+ if (mFd >= 0) {
close(mFd);
mFd = -1;
}
+ if (mZipPid > 0) {
+ mZipPipe.close();
+ // Gzip may take some time.
+ status_t err = wait_child(mZipPid, /* timeout_ms= */ 10 * 1000);
+ if (err != 0) {
+ ALOGW("[ReportRequest] abnormal child process: %s", strerror(-err));
+ }
+ }
+}
+
+int ReportRequest::getFd() {
+ return mZipPid > 0 ? mZipPipe.writeFd().get() : mFd;
+}
+
+status_t ReportRequest::initGzipIfNecessary() {
+ if (!mIsStreaming || !args.gzip()) {
+ return OK;
+ }
+ if (!mZipPipe.init()) {
+ ALOGE("[ReportRequest] Failed to setup pipe for gzip");
+ mStatus = -errno;
+ return mStatus;
+ }
+ int status = 0;
+ pid_t pid = fork_execute_cmd((char* const*)GZIP, mZipPipe.readFd().release(), mFd, &status);
+ if (pid < 0 || status != 0) {
+ mStatus = status;
+ return mStatus;
+ }
+ mZipPid = pid;
+ mFd = -1;
+ return OK;
}
// ================================================================================
@@ -364,7 +411,6 @@
mSectionBufferSuccess = false;
mHadError = false;
mSectionErrors.clear();
-
}
void ReportWriter::setSectionStats(const FdBuffer& buffer) {
@@ -470,10 +516,13 @@
// ================================================================================
-Reporter::Reporter(const sp<WorkDirectory>& workDirectory, const sp<ReportBatch>& batch)
+Reporter::Reporter(const sp<WorkDirectory>& workDirectory,
+ const sp<ReportBatch>& batch,
+ const vector<BringYourOwnSection*>& registeredSections)
:mWorkDirectory(workDirectory),
mWriter(batch),
- mBatch(batch) {
+ mBatch(batch),
+ mRegisteredSections(registeredSections) {
}
Reporter::~Reporter() {
@@ -560,6 +609,13 @@
reportId = (spec.tv_sec) * 1000 + spec.tv_nsec;
}
+ mBatch->forEachStreamingRequest([](const sp<ReportRequest>& request) {
+ status_t err = request->initGzipIfNecessary();
+ if (err != 0) {
+ ALOGW("Error forking gzip: %s", strerror(err));
+ }
+ });
+
// Write the incident report headers - each request gets its own headers. It's different
// from the other top-level fields in IncidentReport that are the sections where the rest
// is all shared data (although with their own individual privacy filtering).
@@ -580,50 +636,15 @@
// For each of the report fields, see if we need it, and if so, execute the command
// and report to those that care that we're doing it.
for (const Section** section = SECTION_LIST; *section; section++) {
- const int sectionId = (*section)->id;
-
- // If nobody wants this section, skip it.
- if (!mBatch->containsSection(sectionId)) {
- continue;
- }
-
- ALOGD("Start incident report section %d '%s'", sectionId, (*section)->name.string());
- IncidentMetadata::SectionStats* sectionMetadata = metadata.add_sections();
-
- // Notify listener of starting
- mBatch->forEachListener(sectionId, [sectionId](const auto& listener) {
- listener->onReportSectionStatus(
- sectionId, IIncidentReportStatusListener::STATUS_STARTING);
- });
-
- // Go get the data and write it into the file descriptors.
- mWriter.startSection(sectionId);
- err = (*section)->Execute(&mWriter);
- mWriter.endSection(sectionMetadata);
-
- // Sections returning errors are fatal. Most errors should not be fatal.
- if (err != NO_ERROR) {
- mWriter.error((*section), err, "Section failed. Stopping report.");
+ if (execute_section(*section, &metadata, reportByteSize) != NO_ERROR) {
goto DONE;
}
+ }
- // The returned max data size is used for throttling too many incident reports.
- (*reportByteSize) += sectionMetadata->report_size_bytes();
-
- // For any requests that failed during this section, remove them now. We do this
- // before calling back about section finished, so listeners do not erroniously get the
- // impression that the section succeeded. But we do it here instead of inside
- // writeSection so that the callback is done from a known context and not from the
- // bowels of a section, where changing the batch could cause odd errors.
- cancel_and_remove_failed_requests();
-
- // Notify listener of finishing
- mBatch->forEachListener(sectionId, [sectionId](const auto& listener) {
- listener->onReportSectionStatus(
- sectionId, IIncidentReportStatusListener::STATUS_FINISHED);
- });
-
- ALOGD("Finish incident report section %d '%s'", sectionId, (*section)->name.string());
+ for (const Section* section : mRegisteredSections) {
+ if (execute_section(section, &metadata, reportByteSize) != NO_ERROR) {
+ goto DONE;
+ }
}
DONE:
@@ -677,10 +698,59 @@
listener->onReportFailed();
});
}
-
+ clear_buffer_pool();
ALOGI("Done taking incident report err=%s", strerror(-err));
}
+status_t Reporter::execute_section(const Section* section, IncidentMetadata* metadata,
+ size_t* reportByteSize) {
+ const int sectionId = section->id;
+
+ // If nobody wants this section, skip it.
+ if (!mBatch->containsSection(sectionId)) {
+ return NO_ERROR;
+ }
+
+ ALOGD("Start incident report section %d '%s'", sectionId, section->name.string());
+ IncidentMetadata::SectionStats* sectionMetadata = metadata->add_sections();
+
+ // Notify listener of starting
+ mBatch->forEachListener(sectionId, [sectionId](const auto& listener) {
+ listener->onReportSectionStatus(
+ sectionId, IIncidentReportStatusListener::STATUS_STARTING);
+ });
+
+ // Go get the data and write it into the file descriptors.
+ mWriter.startSection(sectionId);
+ status_t err = section->Execute(&mWriter);
+ mWriter.endSection(sectionMetadata);
+
+ // Sections returning errors are fatal. Most errors should not be fatal.
+ if (err != NO_ERROR) {
+ mWriter.error(section, err, "Section failed. Stopping report.");
+ return err;
+ }
+
+ // The returned max data size is used for throttling too many incident reports.
+ (*reportByteSize) += sectionMetadata->report_size_bytes();
+
+ // For any requests that failed during this section, remove them now. We do this
+ // before calling back about section finished, so listeners do not erroniously get the
+ // impression that the section succeeded. But we do it here instead of inside
+ // writeSection so that the callback is done from a known context and not from the
+ // bowels of a section, where changing the batch could cause odd errors.
+ cancel_and_remove_failed_requests();
+
+ // Notify listener of finishing
+ mBatch->forEachListener(sectionId, [sectionId](const auto& listener) {
+ listener->onReportSectionStatus(
+ sectionId, IIncidentReportStatusListener::STATUS_FINISHED);
+ });
+
+ ALOGD("Finish incident report section %d '%s'", sectionId, section->name.string());
+ return NO_ERROR;
+}
+
void Reporter::cancel_and_remove_failed_requests() {
// Handle a failure in the persisted file
if (mPersistedFile != nullptr) {
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index fb3961a..bd47a23 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -15,12 +15,14 @@
*/
#pragma once
+#include "incidentd_util.h"
#include "FdBuffer.h"
#include "WorkDirectory.h"
#include "frameworks/base/core/proto/android/os/metadata.pb.h"
#include <android/content/ComponentName.h>
#include <android/os/IIncidentReportStatusListener.h>
+#include <android/os/IIncidentDumpCallback.h>
#include <android/os/IncidentReportArgs.h>
#include <android/util/protobuf.h>
@@ -39,6 +41,7 @@
using namespace android::content;
using namespace android::os;
+class BringYourOwnSection;
class Section;
// ================================================================================
@@ -61,10 +64,12 @@
sp<IIncidentReportStatusListener> getListener() { return mListener; }
- int getFd() { return mFd; }
+ int getFd();
int setPersistedFd(int fd);
+ status_t initGzipIfNecessary();
+
void closeFd();
private:
@@ -72,6 +77,8 @@
int mFd;
bool mIsStreaming;
status_t mStatus;
+ pid_t mZipPid;
+ Fpipe mZipPipe;
};
// ================================================================================
@@ -122,7 +129,7 @@
void forEachStreamingRequest(const function<void (const sp<ReportRequest>&)>& func);
/**
- * Call func(request) for each file descriptor that has
+ * Call func(request) for each file descriptor.
*/
void forEachFd(int sectionId, const function<void (const sp<ReportRequest>&)>& func);
@@ -251,7 +258,9 @@
// ================================================================================
class Reporter : public virtual RefBase {
public:
- Reporter(const sp<WorkDirectory>& workDirectory, const sp<ReportBatch>& batch);
+ Reporter(const sp<WorkDirectory>& workDirectory,
+ const sp<ReportBatch>& batch,
+ const vector<BringYourOwnSection*>& registeredSections);
virtual ~Reporter();
@@ -263,6 +272,10 @@
ReportWriter mWriter;
sp<ReportBatch> mBatch;
sp<ReportFile> mPersistedFile;
+ const vector<BringYourOwnSection*>& mRegisteredSections;
+
+ status_t execute_section(const Section* section, IncidentMetadata* metadata,
+ size_t* reportByteSize);
void cancel_and_remove_failed_requests();
};
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 6cbfd47..e56ed39 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -20,9 +20,9 @@
#include <dirent.h>
#include <errno.h>
-
#include <mutex>
#include <set>
+#include <thread>
#include <android-base/file.h>
#include <android-base/properties.h>
@@ -35,12 +35,14 @@
#include <log/log_event_list.h>
#include <log/logprint.h>
#include <private/android_logger.h>
+#include <sys/mman.h>
#include "FdBuffer.h"
#include "Privacy.h"
#include "frameworks/base/core/proto/android/os/backtrace.proto.h"
#include "frameworks/base/core/proto/android/os/data.proto.h"
#include "frameworks/base/core/proto/android/util/log.proto.h"
+#include "frameworks/base/core/proto/android/util/textdump.proto.h"
#include "incidentd_util.h"
namespace android {
@@ -104,7 +106,6 @@
return NO_ERROR;
}
- FdBuffer buffer;
Fpipe p2cPipe;
Fpipe c2pPipe;
// initiate pipes to pass data to/from incident_helper
@@ -120,6 +121,7 @@
}
// parent process
+ FdBuffer buffer;
status_t readStatus = buffer.readProcessedDataInStream(fd.get(), std::move(p2cPipe.writeFd()),
std::move(c2pPipe.readFd()),
this->timeoutMs, mIsSysfs);
@@ -134,7 +136,7 @@
status_t ihStatus = wait_child(pid);
if (ihStatus != NO_ERROR) {
ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-ihStatus));
- return ihStatus;
+ return OK; // Not a fatal error.
}
return writer->writeSection(buffer);
@@ -233,7 +235,7 @@
Fpipe pipe;
// Lock protects these fields
- mutex lock;
+ std::mutex lock;
bool workerDone;
status_t workerError;
@@ -260,78 +262,42 @@
}
}
-static void* worker_thread_func(void* cookie) {
- // Don't crash the service if we write to a closed pipe (which can happen if
- // dumping times out).
- signal(SIGPIPE, sigpipe_handler);
-
- WorkerThreadData* data = (WorkerThreadData*)cookie;
- status_t err = data->section->BlockingCall(data->pipe.writeFd().get());
-
- {
- unique_lock<mutex> lock(data->lock);
- data->workerDone = true;
- data->workerError = err;
- }
-
- data->pipe.writeFd().reset();
- data->decStrong(data->section);
- // data might be gone now. don't use it after this point in this thread.
- return NULL;
-}
-
status_t WorkerThreadSection::Execute(ReportWriter* writer) const {
- status_t err = NO_ERROR;
- pthread_t thread;
- pthread_attr_t attr;
- bool workerDone = false;
- FdBuffer buffer;
-
- // Data shared between this thread and the worker thread.
+ // Create shared data and pipe. Don't put data on the stack since this thread may exit early.
sp<WorkerThreadData> data = new WorkerThreadData(this);
-
- // Create the pipe
if (!data->pipe.init()) {
return -errno;
}
-
- // Create the thread
- err = pthread_attr_init(&attr);
- if (err != 0) {
- return -err;
- }
- // TODO: Do we need to tweak thread priority?
- err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- if (err != 0) {
- pthread_attr_destroy(&attr);
- return -err;
- }
-
- // The worker thread needs a reference and we can't let the count go to zero
- // if that thread is slow to start.
data->incStrong(this);
-
- err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
- pthread_attr_destroy(&attr);
- if (err != 0) {
+ std::thread([data, this]() {
+ // Don't crash the service if writing to a closed pipe (may happen if dumping times out)
+ signal(SIGPIPE, sigpipe_handler);
+ status_t err = data->section->BlockingCall(data->pipe.writeFd());
+ {
+ std::scoped_lock<std::mutex> lock(data->lock);
+ data->workerDone = true;
+ data->workerError = err;
+ // unique_fd is not thread safe. If we don't lock it, reset() may pause half way while
+ // the other thread executes to the end, calling ~Fpipe, which is a race condition.
+ data->pipe.writeFd().reset();
+ }
data->decStrong(this);
- return -err;
- }
+ }).detach();
// Loop reading until either the timeout or the worker side is done (i.e. eof).
+ status_t err = NO_ERROR;
+ bool workerDone = false;
+ FdBuffer buffer;
err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
if (err != NO_ERROR) {
ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
}
- // Done with the read fd. The worker thread closes the write one so
- // we never race and get here first.
- data->pipe.readFd().reset();
-
// If the worker side is finished, then return its error (which may overwrite
// our possible error -- but it's more interesting anyway). If not, then we timed out.
{
- unique_lock<mutex> lock(data->lock);
+ std::scoped_lock<std::mutex> lock(data->lock);
+ data->pipe.close();
if (data->workerError != NO_ERROR) {
err = data->workerError;
ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
@@ -390,7 +356,6 @@
CommandSection::~CommandSection() { free(mCommand); }
status_t CommandSection::Execute(ReportWriter* writer) const {
- FdBuffer buffer;
Fpipe cmdPipe;
Fpipe ihPipe;
@@ -411,6 +376,7 @@
}
cmdPipe.writeFd().reset();
+ FdBuffer buffer;
status_t readStatus = buffer.read(ihPipe.readFd().get(), this->timeoutMs);
writer->setSectionStats(buffer);
if (readStatus != NO_ERROR || buffer.timedOut()) {
@@ -457,7 +423,7 @@
DumpsysSection::~DumpsysSection() {}
-status_t DumpsysSection::BlockingCall(int pipeWriteFd) const {
+status_t DumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const {
// checkService won't wait for the service to show up like getService will.
sp<IBinder> service = defaultServiceManager()->checkService(mService);
@@ -466,19 +432,120 @@
return NAME_NOT_FOUND;
}
- service->dump(pipeWriteFd, mArgs);
+ service->dump(pipeWriteFd.get(), mArgs);
return NO_ERROR;
}
// ================================================================================
+TextDumpsysSection::TextDumpsysSection(int id, const char* service, ...)
+ :Section(id), mService(service) {
+ name = "dumpsys ";
+ name += service;
+
+ va_list args;
+ va_start(args, service);
+ while (true) {
+ const char* arg = va_arg(args, const char*);
+ if (arg == NULL) {
+ break;
+ }
+ mArgs.add(String16(arg));
+ name += " ";
+ name += arg;
+ }
+ va_end(args);
+}
+
+TextDumpsysSection::~TextDumpsysSection() {}
+
+status_t TextDumpsysSection::Execute(ReportWriter* writer) const {
+ // checkService won't wait for the service to show up like getService will.
+ sp<IBinder> service = defaultServiceManager()->checkService(mService);
+ if (service == NULL) {
+ ALOGW("TextDumpsysSection: Can't lookup service: %s", String8(mService).string());
+ return NAME_NOT_FOUND;
+ }
+
+ // Create pipe
+ Fpipe dumpPipe;
+ if (!dumpPipe.init()) {
+ ALOGW("[%s] failed to setup pipe", this->name.string());
+ return -errno;
+ }
+
+ // Run dumping thread
+ const uint64_t start = Nanotime();
+ 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(write_fd.get(), this->mArgs);
+ if (err != OK) {
+ ALOGW("[%s] dump thread failed. Error: %s", this->name.string(), strerror(-err));
+ }
+ write_fd.reset();
+ });
+
+ // Collect dump content
+ FdBuffer buffer;
+ ProtoOutputStream proto;
+ proto.write(TextDumpProto::COMMAND, std::string(name.string()));
+ proto.write(TextDumpProto::DUMP_DURATION_NS, int64_t(Nanotime() - start));
+ buffer.write(proto.data());
+
+ sp<EncodedBuffer> internalBuffer = buffer.data();
+ internalBuffer->writeHeader((uint32_t)TextDumpProto::CONTENT, WIRE_TYPE_LENGTH_DELIMITED);
+ size_t editPos = internalBuffer->wp()->pos();
+ internalBuffer->wp()->move(8); // reserve 8 bytes for the varint of the data size
+ size_t dataBeginPos = internalBuffer->wp()->pos();
+
+ status_t readStatus = buffer.read(dumpPipe.readFd(), this->timeoutMs);
+ dumpPipe.readFd().reset();
+ writer->setSectionStats(buffer);
+ if (readStatus != OK || buffer.timedOut()) {
+ ALOGW("[%s] failed to read from dumpsys: %s, timedout: %s", this->name.string(),
+ strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+ worker.detach();
+ return readStatus;
+ }
+ worker.join(); // wait for worker to finish
+
+ // Revisit the actual size from dumpsys and edit the internal buffer accordingly.
+ size_t dumpSize = buffer.size() - dataBeginPos;
+ internalBuffer->wp()->rewind()->move(editPos);
+ internalBuffer->writeRawVarint32(dumpSize);
+ internalBuffer->copy(dataBeginPos, dumpSize);
+
+ return writer->writeSection(buffer);
+}
+
+// ================================================================================
// initialization only once in Section.cpp.
map<log_id_t, log_time> LogSection::gLastLogsRetrieved;
-LogSection::LogSection(int id, log_id_t logID) : WorkerThreadSection(id), mLogID(logID) {
- name = "logcat ";
- name += android_log_id_to_name(logID);
- switch (logID) {
+LogSection::LogSection(int id, const char* logID, ...) : WorkerThreadSection(id), mLogMode(logModeBase) {
+ name = "logcat -b ";
+ name += logID;
+
+ va_list args;
+ va_start(args, logID);
+ mLogID = android_name_to_log_id(logID);
+ while(true) {
+ const char* arg = va_arg(args, const char*);
+ if (arg == NULL) {
+ break;
+ }
+ if (!strcmp(arg, "-L")) {
+ // Read from last logcat buffer
+ mLogMode = mLogMode | ANDROID_LOG_PSTORE;
+ }
+ name += " ";
+ name += arg;
+ }
+ va_end(args);
+
+ switch (mLogID) {
case LOG_ID_EVENTS:
case LOG_ID_STATS:
case LOG_ID_SECURITY:
@@ -507,42 +574,51 @@
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}
-status_t LogSection::BlockingCall(int pipeWriteFd) const {
+status_t LogSection::BlockingCall(unique_fd& pipeWriteFd) const {
+ // heap profile shows that liblog malloc & free significant amount of memory in this process.
+ // Hence forking a new process to prevent memory fragmentation.
+ pid_t pid = fork();
+ if (pid < 0) {
+ ALOGW("[%s] failed to fork", this->name.string());
+ return errno;
+ }
+ if (pid > 0) {
+ return wait_child(pid, this->timeoutMs);
+ }
// Open log buffer and getting logs since last retrieved time if any.
unique_ptr<logger_list, void (*)(logger_list*)> loggers(
gLastLogsRetrieved.find(mLogID) == gLastLogsRetrieved.end()
- ? android_logger_list_alloc(ANDROID_LOG_NONBLOCK, 0, 0)
- : android_logger_list_alloc_time(ANDROID_LOG_NONBLOCK,
- gLastLogsRetrieved[mLogID], 0),
+ ? android_logger_list_alloc(mLogMode, 0, 0)
+ : android_logger_list_alloc_time(mLogMode, gLastLogsRetrieved[mLogID], 0),
android_logger_list_free);
if (android_logger_open(loggers.get(), mLogID) == NULL) {
ALOGE("[%s] Can't get logger.", this->name.string());
- return -1;
+ _exit(EXIT_FAILURE);
}
log_msg msg;
log_time lastTimestamp(0);
ProtoOutputStream proto;
+ status_t err = OK;
while (true) { // keeps reading until logd buffer is fully read.
- status_t err = android_logger_list_read(loggers.get(), &msg);
- // err = 0 - no content, unexpected connection drop or EOF.
- // err = +ive number - size of retrieved data from logger
- // err = -ive number, OS supplied error _except_ for -EAGAIN
- // err = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end of data.
- if (err <= 0) {
- if (err != -EAGAIN) {
+ status_t status = android_logger_list_read(loggers.get(), &msg);
+ // status = 0 - no content, unexpected connection drop or EOF.
+ // status = +ive number - size of retrieved data from logger
+ // status = -ive number, OS supplied error _except_ for -EAGAIN
+ // status = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end.
+ if (status <= 0) {
+ if (status != -EAGAIN) {
ALOGW("[%s] fails to read a log_msg.\n", this->name.string());
+ err = -status;
}
- // dump previous logs and don't consider this error a failure.
break;
}
if (mBinary) {
// remove the first uint32 which is tag's index in event log tags
android_log_context context = create_android_log_parser(msg.msg() + sizeof(uint32_t),
msg.len() - sizeof(uint32_t));
- ;
android_log_list_element elem;
lastTimestamp.tv_sec = msg.entry.sec;
@@ -602,9 +678,10 @@
}
} else {
AndroidLogEntry entry;
- err = android_log_processLogBuffer(&msg.entry, &entry);
- if (err != NO_ERROR) {
+ status = android_log_processLogBuffer(&msg.entry, &entry);
+ if (status != OK) {
ALOGW("[%s] fails to process to an entry.\n", this->name.string());
+ err = status;
break;
}
lastTimestamp.tv_sec = entry.tv_sec;
@@ -623,17 +700,24 @@
trimTail(entry.message, entry.messageLen));
proto.end(token);
}
+ if (!proto.flush(pipeWriteFd.get())) {
+ if (errno == EPIPE) {
+ ALOGW("[%s] wrote to a broken pipe\n", this->name.string());
+ }
+ err = errno;
+ break;
+ }
+ proto.clear();
}
gLastLogsRetrieved[mLogID] = lastTimestamp;
- if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
- ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
- return EPIPE;
- }
- return NO_ERROR;
+ _exit(err);
}
// ================================================================================
+const int LINK_NAME_LEN = 64;
+const int EXE_NAME_LEN = 1024;
+
TombstoneSection::TombstoneSection(int id, const char* type, const int64_t timeoutMs)
: WorkerThreadSection(id, timeoutMs), mType(type) {
name = "tombstone ";
@@ -642,7 +726,7 @@
TombstoneSection::~TombstoneSection() {}
-status_t TombstoneSection::BlockingCall(int pipeWriteFd) const {
+status_t TombstoneSection::BlockingCall(unique_fd& pipeWriteFd) const {
std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
if (proc.get() == nullptr) {
ALOGE("opendir /proc failed: %s\n", strerror(errno));
@@ -651,25 +735,37 @@
const std::set<int> hal_pids = get_interesting_hal_pids();
- ProtoOutputStream proto;
+ auto pooledBuffer = get_buffer_from_pool();
+ ProtoOutputStream proto(pooledBuffer);
+ // dumpBufferSize should be a multiple of page size (4 KB) to reduce memory fragmentation
+ size_t dumpBufferSize = 64 * 1024; // 64 KB is enough for most tombstone dump
+ char* dumpBuffer = (char*)mmap(NULL, dumpBufferSize, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
struct dirent* d;
+ char link_name[LINK_NAME_LEN];
+ char exe_name[EXE_NAME_LEN];
status_t err = NO_ERROR;
while ((d = readdir(proc.get()))) {
int pid = atoi(d->d_name);
if (pid <= 0) {
continue;
}
-
- const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
- std::string exe;
- if (!android::base::Readlink(link_name, &exe)) {
- ALOGE("Section %s: Can't read '%s': %s\n", name.string(),
- link_name.c_str(), strerror(errno));
+ snprintf(link_name, LINK_NAME_LEN, "/proc/%d/exe", pid);
+ struct stat fileStat;
+ if (stat(link_name, &fileStat) != OK) {
continue;
}
+ ssize_t exe_name_len = readlink(link_name, exe_name, EXE_NAME_LEN);
+ if (exe_name_len < 0 || exe_name_len >= EXE_NAME_LEN) {
+ ALOGE("[%s] Can't read '%s': %s", name.string(), link_name, strerror(errno));
+ continue;
+ }
+ // readlink(2) does not put a null terminator at the end
+ exe_name[exe_name_len] = '\0';
bool is_java_process;
- if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") {
+ if (strncmp(exe_name, "/system/bin/app_process32", LINK_NAME_LEN) == 0 ||
+ strncmp(exe_name, "/system/bin/app_process64", LINK_NAME_LEN) == 0) {
if (mType != "java") continue;
// Don't bother dumping backtraces for the zygote.
if (IsZygote(pid)) {
@@ -678,7 +774,7 @@
}
is_java_process = true;
- } else if (should_dump_native_traces(exe.c_str())) {
+ } else if (should_dump_native_traces(exe_name)) {
if (mType != "native") continue;
is_java_process = false;
} else if (hal_pids.find(pid) != hal_pids.end()) {
@@ -734,32 +830,58 @@
ALOGE("[%s] child had an issue: %s\n", this->name.string(), strerror(-cStatus));
}
- auto dump = std::make_unique<char[]>(buffer.size());
+ // Resize dump buffer
+ if (dumpBufferSize < buffer.size()) {
+ munmap(dumpBuffer, dumpBufferSize);
+ while(dumpBufferSize < buffer.size()) dumpBufferSize = dumpBufferSize << 1;
+ dumpBuffer = (char*)mmap(NULL, dumpBufferSize, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ }
sp<ProtoReader> reader = buffer.data()->read();
int i = 0;
while (reader->hasNext()) {
- dump[i] = reader->next();
+ dumpBuffer[i] = reader->next();
i++;
}
uint64_t token = proto.start(android::os::BackTraceProto::TRACES);
proto.write(android::os::BackTraceProto::Stack::PID, pid);
- proto.write(android::os::BackTraceProto::Stack::DUMP, dump.get(), i);
+ proto.write(android::os::BackTraceProto::Stack::DUMP, dumpBuffer, i);
proto.write(android::os::BackTraceProto::Stack::DUMP_DURATION_NS,
static_cast<long long>(Nanotime() - start));
proto.end(token);
dumpPipe.readFd().reset();
- }
-
- if (!proto.flush(pipeWriteFd) && errno == EPIPE) {
- ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
- if (err != NO_ERROR) {
- return EPIPE;
+ if (!proto.flush(pipeWriteFd.get())) {
+ if (errno == EPIPE) {
+ ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+ }
+ err = errno;
+ break;
}
+ proto.clear();
}
-
+ munmap(dumpBuffer, dumpBufferSize);
+ return_buffer_to_pool(pooledBuffer);
return err;
}
+// ================================================================================
+BringYourOwnSection::BringYourOwnSection(int id, const char* customName, const uid_t callingUid,
+ const sp<IIncidentDumpCallback>& callback)
+ : WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS), uid(callingUid), mCallback(callback) {
+ name = "registered ";
+ name += customName;
+}
+
+BringYourOwnSection::~BringYourOwnSection() {}
+
+status_t BringYourOwnSection::BlockingCall(unique_fd& pipeWriteFd) const {
+ android::os::ParcelFileDescriptor pfd(std::move(pipeWriteFd));
+ if(mCallback != nullptr) {
+ mCallback->onDumpSection(pfd);
+ }
+ return NO_ERROR;
+}
+
} // namespace incidentd
} // namespace os
} // namespace android
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index f8649f8..698cc04 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -23,6 +23,7 @@
#include <stdarg.h>
#include <map>
+#include <android/os/IIncidentDumpCallback.h>
#include <log/log_read.h>
#include <utils/String16.h>
#include <utils/String8.h>
@@ -90,7 +91,7 @@
virtual status_t Execute(ReportWriter* writer) const;
- virtual status_t BlockingCall(int pipeWriteFd) const = 0;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const = 0;
};
/**
@@ -111,14 +112,30 @@
};
/**
- * Section that calls dumpsys on a system service.
+ * Section that calls protobuf dumpsys on a system service, usually
+ * "dumpsys [service_name] --proto".
*/
class DumpsysSection : public WorkerThreadSection {
public:
DumpsysSection(int id, const char* service, ...);
virtual ~DumpsysSection();
- virtual status_t BlockingCall(int pipeWriteFd) const;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
+
+private:
+ String16 mService;
+ Vector<String16> mArgs;
+};
+
+/**
+ * Section that calls text dumpsys on a system service, usually "dumpsys [service_name]".
+ */
+class TextDumpsysSection : public Section {
+public:
+ TextDumpsysSection(int id, const char* service, ...);
+ virtual ~TextDumpsysSection();
+
+ virtual status_t Execute(ReportWriter* writer) const;
private:
String16 mService;
@@ -133,7 +150,7 @@
SystemPropertyDumpsysSection(int id, const char* service, ...);
virtual ~SystemPropertyDumpsysSection();
- virtual status_t BlockingCall(int pipeWriteFd) const;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
private:
String16 mService;
@@ -147,15 +164,19 @@
// global last log retrieved timestamp for each log_id_t.
static map<log_id_t, log_time> gLastLogsRetrieved;
+ // log mode: non blocking.
+ const static int logModeBase = ANDROID_LOG_NONBLOCK;
+
public:
- LogSection(int id, log_id_t logID);
+ LogSection(int id, const char* logID, ...);
virtual ~LogSection();
- virtual status_t BlockingCall(int pipeWriteFd) const;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
private:
log_id_t mLogID;
bool mBinary;
+ int mLogMode;
};
/**
@@ -166,12 +187,29 @@
TombstoneSection(int id, const char* type, int64_t timeoutMs = 120000 /* 2 minutes */);
virtual ~TombstoneSection();
- virtual status_t BlockingCall(int pipeWriteFd) const;
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
private:
std::string mType;
};
+/**
+ * Section that gets data from a registered dump callback.
+ */
+class BringYourOwnSection : public WorkerThreadSection {
+public:
+ const uid_t uid;
+
+ BringYourOwnSection(int id, const char* customName, const uid_t callingUid,
+ const sp<IIncidentDumpCallback>& callback);
+ virtual ~BringYourOwnSection();
+
+ virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
+
+private:
+ const sp<IIncidentDumpCallback> mCallback;
+};
+
/**
* These sections will not be generated when doing an 'all' report, either
diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp
index 9963533..1944d6e 100644
--- a/cmds/incidentd/src/WorkDirectory.cpp
+++ b/cmds/incidentd/src/WorkDirectory.cpp
@@ -16,10 +16,10 @@
#include "Log.h"
-#include "WorkDirectory.h"
-
+#include "incidentd_util.h"
#include "proto_util.h"
#include "PrivacyFilter.h"
+#include "WorkDirectory.h"
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <private/android_filesystem_config.h>
@@ -68,6 +68,9 @@
/** metadata field id in IncidentProto */
const int FIELD_ID_INCIDENT_METADATA = 2;
+// Args for exec gzip
+static const char* GZIP[] = {"/system/bin/gzip", NULL};
+
/**
* Read a protobuf from disk into the message.
*/
@@ -292,6 +295,7 @@
report->set_cls(args.receiverCls());
report->set_privacy_policy(args.getPrivacyPolicy());
report->set_all_sections(args.all());
+ report->set_gzip(args.gzip());
for (int section: args.sections()) {
report->add_section(section);
}
@@ -417,6 +421,24 @@
return BAD_VALUE;
}
+ pid_t zipPid = 0;
+ if (args.gzip()) {
+ Fpipe zipPipe;
+ if (!zipPipe.init()) {
+ ALOGE("[ReportFile] Failed to setup pipe for gzip");
+ close(writeFd);
+ return -errno;
+ }
+ int status = 0;
+ zipPid = fork_execute_cmd((char* const*)GZIP, zipPipe.readFd().release(), writeFd, &status);
+ close(writeFd);
+ if (zipPid < 0 || status != 0) {
+ ALOGE("[ReportFile] Failed to fork and exec gzip");
+ return status;
+ }
+ writeFd = zipPipe.writeFd().release();
+ }
+
status_t err;
for (const auto& report : mEnvelope.report()) {
@@ -437,6 +459,13 @@
}
close(writeFd);
+ if (zipPid > 0) {
+ status_t err = wait_child(zipPid, /* timeout_ms= */ 10 * 1000);
+ if (err != 0) {
+ ALOGE("[ReportFile] abnormal child process: %s", strerror(-err));
+ }
+ return err;
+ }
return NO_ERROR;
}
@@ -621,7 +650,7 @@
map<string,WorkDirectoryEntry> files;
get_directory_contents_locked(&files, 0);
-
+
for (map<string,WorkDirectoryEntry>::iterator it = files.begin();
it != files.end(); it++) {
sp<ReportFile> reportFile = new ReportFile(this, it->second.timestampNs,
@@ -815,6 +844,7 @@
out->setAll(report.all_sections());
out->setReceiverPkg(report.pkg());
out->setReceiverCls(report.cls());
+ out->setGzip(report.gzip());
const int sectionCount = report.section_size();
for (int i = 0; i < sectionCount; i++) {
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index dfaf893..150ab99 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -18,6 +18,8 @@
#include "incidentd_util.h"
+#include <android/util/EncodedBuffer.h>
+#include <fcntl.h>
#include <sys/prctl.h>
#include <wait.h>
@@ -27,8 +29,6 @@
namespace os {
namespace incidentd {
-using namespace android::base;
-
const Privacy* get_privacy_of_section(int id) {
int l = 0;
int r = PRIVACY_POLICY_COUNT - 1;
@@ -47,6 +47,30 @@
return NULL;
}
+std::vector<sp<EncodedBuffer>> gBufferPool;
+std::mutex gBufferPoolLock;
+
+sp<EncodedBuffer> get_buffer_from_pool() {
+ std::scoped_lock<std::mutex> lock(gBufferPoolLock);
+ if (gBufferPool.size() == 0) {
+ return new EncodedBuffer();
+ }
+ sp<EncodedBuffer> buffer = gBufferPool.back();
+ gBufferPool.pop_back();
+ return buffer;
+}
+
+void return_buffer_to_pool(sp<EncodedBuffer> buffer) {
+ buffer->clear();
+ std::scoped_lock<std::mutex> lock(gBufferPoolLock);
+ gBufferPool.push_back(buffer);
+}
+
+void clear_buffer_pool() {
+ std::scoped_lock<std::mutex> lock(gBufferPoolLock);
+ gBufferPool.clear();
+}
+
// ================================================================================
Fpipe::Fpipe() : mRead(), mWrite() {}
@@ -64,28 +88,52 @@
unique_fd& Fpipe::writeFd() { return mWrite; }
-pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output) {
- // fork used in multithreaded environment, avoid adding unnecessary code in child process
- pid_t pid = fork();
+pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output, int* status) {
+ int in = -1;
+ if (input != nullptr) {
+ in = input->readFd().release();
+ // Auto close write end of the input pipe on exec to prevent leaking fd in child process
+ fcntl(input->writeFd().get(), F_SETFD, FD_CLOEXEC);
+ }
+ int out = output->writeFd().release();
+ // Auto close read end of the output pipe on exec
+ fcntl(output->readFd().get(), F_SETFD, FD_CLOEXEC);
+ return fork_execute_cmd(argv, in, out, status);
+}
+
+pid_t fork_execute_cmd(char* const argv[], int in, int out, int* status) {
+ int dummy_status = 0;
+ if (status == nullptr) {
+ status = &dummy_status;
+ }
+ *status = 0;
+ pid_t pid = vfork();
+ if (pid < 0) {
+ *status = -errno;
+ return -1;
+ }
if (pid == 0) {
- if (input != NULL && (TEMP_FAILURE_RETRY(dup2(input->readFd().get(), STDIN_FILENO)) < 0 ||
- !input->close())) {
+ // In child
+ if (in >= 0 && (TEMP_FAILURE_RETRY(dup2(in, STDIN_FILENO)) < 0 || close(in))) {
ALOGW("Failed to dup2 stdin.");
_exit(EXIT_FAILURE);
}
- if (TEMP_FAILURE_RETRY(dup2(output->writeFd().get(), STDOUT_FILENO)) < 0 ||
- !output->close()) {
+ if (TEMP_FAILURE_RETRY(dup2(out, STDOUT_FILENO)) < 0 || close(out)) {
ALOGW("Failed to dup2 stdout.");
_exit(EXIT_FAILURE);
}
- /* make sure the child dies when incidentd dies */
+ // Make sure the child dies when incidentd dies
prctl(PR_SET_PDEATHSIG, SIGKILL);
execvp(argv[0], argv);
_exit(errno); // always exits with failure if any
}
- // close the fds used in child process.
- if (input != NULL) input->readFd().reset();
- output->writeFd().reset();
+ // In parent
+ if ((in >= 0 && close(in) < 0) || close(out) < 0) {
+ ALOGW("Failed to close pd. Killing child process");
+ *status = -errno;
+ kill_child(pid);
+ return -1;
+ }
return pid;
}
@@ -120,9 +168,6 @@
}
// ================================================================================
-const int WAIT_MAX = 5;
-const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
-
static status_t statusCode(int status) {
if (WIFSIGNALED(status)) {
VLOG("return by signal: %s", strerror(WTERMSIG(status)));
@@ -134,25 +179,64 @@
return NO_ERROR;
}
+static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) {
+ sigset_t child_mask, old_mask;
+ sigemptyset(&child_mask);
+ sigaddset(&child_mask, SIGCHLD);
+
+ if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
+ ALOGW("sigprocmask failed: %s", strerror(errno));
+ return false;
+ }
+
+ timespec ts;
+ ts.tv_sec = timeout_ms / 1000;
+ ts.tv_nsec = (timeout_ms % 1000) * 1000000;
+ int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts));
+ int saved_errno = errno;
+
+ // Set the signals back the way they were.
+ if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) {
+ ALOGW("sigprocmask failed: %s", strerror(errno));
+ if (ret == 0) {
+ return false;
+ }
+ }
+ if (ret == -1) {
+ errno = saved_errno;
+ if (errno == EAGAIN) {
+ errno = ETIMEDOUT;
+ } else {
+ ALOGW("sigtimedwait failed: %s", strerror(errno));
+ }
+ return false;
+ }
+
+ pid_t child_pid = waitpid(pid, status, WNOHANG);
+ if (child_pid == pid) {
+ return true;
+ }
+ if (child_pid == -1) {
+ ALOGW("waitpid failed: %s", strerror(errno));
+ } else {
+ ALOGW("Waiting for pid %d, got pid %d instead", pid, child_pid);
+ }
+ return false;
+}
+
status_t kill_child(pid_t pid) {
int status;
- VLOG("try to kill child process %d", pid);
kill(pid, SIGKILL);
if (waitpid(pid, &status, 0) == -1) return -1;
return statusCode(status);
}
-status_t wait_child(pid_t pid) {
+status_t wait_child(pid_t pid, int timeout_ms) {
int status;
- bool died = false;
- // wait for child to report status up to 1 seconds
- for (int loop = 0; !died && loop < WAIT_MAX; loop++) {
- if (waitpid(pid, &status, WNOHANG) == pid) died = true;
- // sleep for 0.2 second
- nanosleep(&WAIT_INTERVAL_NS, NULL);
+ if (waitpid_with_timeout(pid, timeout_ms, &status)) {
+ return statusCode(status);
}
- if (!died) return kill_child(pid);
- return statusCode(status);
+ return kill_child(pid);
}
} // namespace incidentd
diff --git a/cmds/incidentd/src/incidentd_util.h b/cmds/incidentd/src/incidentd_util.h
index cc30768..8499889 100644
--- a/cmds/incidentd/src/incidentd_util.h
+++ b/cmds/incidentd/src/incidentd_util.h
@@ -19,18 +19,21 @@
#define INCIDENTD_UTIL_H
#include <stdarg.h>
-#include <unistd.h>
-
-#include <android-base/unique_fd.h>
#include <utils/Errors.h>
#include "Privacy.h"
namespace android {
+
+namespace util {
+class EncodedBuffer;
+}
+
namespace os {
namespace incidentd {
-using namespace android::base;
+using android::base::unique_fd;
+using android::util::EncodedBuffer;
/**
* Looks up Privacy of a section in the auto-gen PRIVACY_POLICY_LIST;
@@ -38,6 +41,25 @@
const Privacy* get_privacy_of_section(int id);
/**
+ * Get an EncodedBuffer from an internal pool, or create and return a new one if the pool is empty.
+ * The EncodedBuffer should be returned after use.
+ * Thread safe.
+ */
+sp<EncodedBuffer> get_buffer_from_pool();
+
+/**
+ * Return the EncodedBuffer back to the pool for reuse.
+ * Thread safe.
+ */
+void return_buffer_to_pool(sp<EncodedBuffer> buffer);
+
+/**
+ * Clear the buffer pool to free memory, after taking an incident report.
+ * Thread safe.
+ */
+void clear_buffer_pool();
+
+/**
* This class wraps android::base::Pipe.
*/
class Fpipe {
@@ -56,11 +78,24 @@
};
/**
- * Forks and exec a command with two pipes, one connects stdin for input,
- * one connects stdout for output. It returns the pid of the child.
- * Input pipe can be NULL to indicate child process doesn't read stdin.
+ * Forks and exec a command with two pipes and returns the pid of the child, or -1 when it fails.
+ *
+ * input connects stdin for input. output connects stdout for output. input can be nullptr to
+ * indicate that child process doesn't read stdin. This function will close in and out fds upon
+ * success. If status is not NULL, the status information will be stored in the int to which it
+ * points.
*/
-pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output);
+pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output, int* status = nullptr);
+
+/**
+ * Forks and exec a command that reads from in fd and writes to out fd and returns the pid of the
+ * child, or -1 when it fails.
+ *
+ * in can be -1 to indicate that child process doesn't read stdin. This function will close in and
+ * out fds upon success. If status is not NULL, the status information will be stored in the int
+ * to which it points.
+ */
+pid_t fork_execute_cmd(char* const argv[], int in, int out, int* status = nullptr);
/**
* Grabs varargs from stack and stores them in heap with NULL-terminated array.
@@ -76,7 +111,7 @@
* Methods to wait or kill child process, return exit status code.
*/
status_t kill_child(pid_t pid);
-status_t wait_child(pid_t pid);
+status_t wait_child(pid_t pid, int timeout_ms = 1000);
status_t start_detached_thread(const function<void ()>& func);
diff --git a/cmds/incidentd/src/report_file.proto b/cmds/incidentd/src/report_file.proto
index 7563da2..85fd2da 100644
--- a/cmds/incidentd/src/report_file.proto
+++ b/cmds/incidentd/src/report_file.proto
@@ -65,6 +65,11 @@
* the given client.
*/
optional bool share_approved = 8;
+
+ /**
+ * Whether the report is gzipped.
+ */
+ optional bool gzip = 9;
}
/**
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index a077745..08216d9 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -430,6 +430,6 @@
+ " (Default: touchscreen)");
out.println(" press (Default: trackball)");
out.println(" roll <dx> <dy> (Default: trackball)");
- out.println(" event <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
+ out.println(" motionevent <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
}
}
diff --git a/cmds/locksettings/TEST_MAPPING b/cmds/locksettings/TEST_MAPPING
new file mode 100644
index 0000000..56f5cc0
--- /dev/null
+++ b/cmds/locksettings/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit-devicepolicy": [
+ {
+ "name": "CtsDevicePolicyManagerTestCases",
+ "options": [
+ {
+ "include-annotation": "com.android.cts.devicepolicy.annotations.LockSettingsTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/cmds/media/Android.bp b/cmds/media/Android.bp
deleted file mode 100644
index 7879c53..0000000
--- a/cmds/media/Android.bp
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2013 The Android Open Source Project
-//
-
-java_binary {
- name: "media",
- wrapper: "media",
- srcs: ["**/*.java"],
-}
diff --git a/cmds/media/MODULE_LICENSE_APACHE2 b/cmds/media/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/cmds/media/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/cmds/media/NOTICE b/cmds/media/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/cmds/media/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2005-2008, 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.
-
- 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.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
diff --git a/cmds/media/media b/cmds/media/media
deleted file mode 100755
index 00c3915..0000000
--- a/cmds/media/media
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/system/bin/sh
-export CLASSPATH=/system/framework/media.jar
-exec app_process /system/bin com.android.commands.media.Media "$@"
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
deleted file mode 100644
index 1e915f8..0000000
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
-**
-** Copyright 2013, 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.
-*/
-
-package com.android.commands.media;
-
-import android.app.ActivityThread;
-import android.content.Context;
-import android.media.MediaMetadata;
-import android.media.session.ISessionManager;
-import android.media.session.MediaController;
-import android.media.session.MediaController.PlaybackInfo;
-import android.media.session.MediaSession.QueueItem;
-import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
-import android.os.Bundle;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.util.AndroidException;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-
-import com.android.internal.os.BaseCommand;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.util.List;
-
-public class Media extends BaseCommand {
- // This doesn't belongs to any package. Setting the package name to empty string.
- private static final String PACKAGE_NAME = "";
- private static ActivityThread sThread;
- private static MediaSessionManager sMediaSessionManager;
- private ISessionManager mSessionService;
-
- /**
- * Command-line entry point.
- *
- * @param args The command-line arguments
- */
- public static void main(String[] args) {
- (new Media()).run(args);
- }
-
- @Override
- public void onShowUsage(PrintStream out) {
- out.println(
- "usage: media [subcommand] [options]\n" +
- " media dispatch KEY\n" +
- " media list-sessions\n" +
- " media monitor <tag>\n" +
- " media volume [options]\n" +
- "\n" +
- "media dispatch: dispatch a media key to the system.\n" +
- " KEY may be: play, pause, play-pause, mute, headsethook,\n" +
- " stop, next, previous, rewind, record, fast-forword.\n" +
- "media list-sessions: print a list of the current sessions.\n" +
- "media monitor: monitor updates to the specified session.\n" +
- " Use the tag from list-sessions.\n" +
- "media volume: " + VolumeCtrl.USAGE
- );
- }
-
- @Override
- public void onRun() throws Exception {
- if (sThread == null) {
- Looper.prepareMainLooper();
- sThread = ActivityThread.systemMain();
- Context context = sThread.getSystemContext();
- sMediaSessionManager =
- (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
- }
- mSessionService = ISessionManager.Stub.asInterface(ServiceManager.checkService(
- Context.MEDIA_SESSION_SERVICE));
- if (mSessionService == null) {
- System.err.println(NO_SYSTEM_ERROR_CODE);
- throw new AndroidException(
- "Can't connect to media session service; is the system running?");
- }
-
- String op = nextArgRequired();
-
- if (op.equals("dispatch")) {
- runDispatch();
- } else if (op.equals("list-sessions")) {
- runListSessions();
- } else if (op.equals("monitor")) {
- runMonitor();
- } else if (op.equals("volume")) {
- runVolume();
- } else {
- showError("Error: unknown command '" + op + "'");
- return;
- }
- }
-
- private void sendMediaKey(KeyEvent event) {
- try {
- mSessionService.dispatchMediaKeyEvent(PACKAGE_NAME, false, event, false);
- } catch (RemoteException e) {
- }
- }
-
- private void runMonitor() throws Exception {
- String id = nextArgRequired();
- if (id == null) {
- showError("Error: must include a session id");
- return;
- }
-
- boolean success = false;
- try {
- List<MediaController> controllers = sMediaSessionManager.getActiveSessions(null);
- for (MediaController controller : controllers) {
- try {
- if (controller != null && id.equals(controller.getTag())) {
- ControllerMonitor monitor = new ControllerMonitor(controller);
- monitor.run();
- success = true;
- break;
- }
- } catch (RemoteException e) {
- // ignore
- }
- }
- } catch (Exception e) {
- System.out.println("***Error monitoring session*** " + e.getMessage());
- }
- if (!success) {
- System.out.println("No session found with id " + id);
- }
- }
-
- private void runDispatch() throws Exception {
- String cmd = nextArgRequired();
- int keycode;
- if ("play".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MEDIA_PLAY;
- } else if ("pause".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MEDIA_PAUSE;
- } else if ("play-pause".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE;
- } else if ("mute".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MUTE;
- } else if ("headsethook".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_HEADSETHOOK;
- } else if ("stop".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MEDIA_STOP;
- } else if ("next".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MEDIA_NEXT;
- } else if ("previous".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
- } else if ("rewind".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MEDIA_REWIND;
- } else if ("record".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MEDIA_RECORD;
- } else if ("fast-forward".equals(cmd)) {
- keycode = KeyEvent.KEYCODE_MEDIA_FAST_FORWARD;
- } else {
- showError("Error: unknown dispatch code '" + cmd + "'");
- return;
- }
- final long now = SystemClock.uptimeMillis();
- sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
- sendMediaKey(new KeyEvent(now, now, KeyEvent.ACTION_UP, keycode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
- }
-
- class ControllerCallback extends MediaController.Callback {
- @Override
- public void onSessionDestroyed() {
- System.out.println("onSessionDestroyed. Enter q to quit.");
- }
-
- @Override
- public void onSessionEvent(String event, Bundle extras) {
- System.out.println("onSessionEvent event=" + event + ", extras=" + extras);
- }
-
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- System.out.println("onPlaybackStateChanged " + state);
- }
-
- @Override
- public void onMetadataChanged(MediaMetadata metadata) {
- String mmString = metadata == null ? null : "title=" + metadata
- .getDescription();
- System.out.println("onMetadataChanged " + mmString);
- }
-
- @Override
- public void onQueueChanged(List<QueueItem> queue) {
- System.out.println("onQueueChanged, "
- + (queue == null ? "null queue" : " size=" + queue.size()));
- }
-
- @Override
- public void onQueueTitleChanged(CharSequence title) {
- System.out.println("onQueueTitleChange " + title);
- }
-
- @Override
- public void onExtrasChanged(Bundle extras) {
- System.out.println("onExtrasChanged " + extras);
- }
-
- @Override
- public void onAudioInfoChanged(PlaybackInfo info) {
- System.out.println("onAudioInfoChanged " + info);
- }
- }
-
- private class ControllerMonitor {
- private final MediaController mController;
- private final ControllerCallback mControllerCallback;
-
- ControllerMonitor(MediaController controller) {
- mController = controller;
- mControllerCallback = new ControllerCallback();
- }
-
- void printUsageMessage() {
- try {
- System.out.println("V2Monitoring session " + mController.getTag()
- + "... available commands: play, pause, next, previous");
- } catch (RuntimeException e) {
- System.out.println("Error trying to monitor session!");
- }
- System.out.println("(q)uit: finish monitoring");
- }
-
- void run() throws RemoteException {
- printUsageMessage();
- HandlerThread cbThread = new HandlerThread("MediaCb") {
- @Override
- protected void onLooperPrepared() {
- try {
- mController.registerCallback(mControllerCallback);
- } catch (RuntimeException e) {
- System.out.println("Error registering monitor callback");
- }
- }
- };
- cbThread.start();
-
- try {
- InputStreamReader converter = new InputStreamReader(System.in);
- BufferedReader in = new BufferedReader(converter);
- String line;
-
- while ((line = in.readLine()) != null) {
- boolean addNewline = true;
- if (line.length() <= 0) {
- addNewline = false;
- } else if ("q".equals(line) || "quit".equals(line)) {
- break;
- } else if ("play".equals(line)) {
- dispatchKeyCode(KeyEvent.KEYCODE_MEDIA_PLAY);
- } else if ("pause".equals(line)) {
- dispatchKeyCode(KeyEvent.KEYCODE_MEDIA_PAUSE);
- } else if ("next".equals(line)) {
- dispatchKeyCode(KeyEvent.KEYCODE_MEDIA_NEXT);
- } else if ("previous".equals(line)) {
- dispatchKeyCode(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
- } else {
- System.out.println("Invalid command: " + line);
- }
-
- synchronized (this) {
- if (addNewline) {
- System.out.println("");
- }
- printUsageMessage();
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- cbThread.getLooper().quit();
- try {
- mController.unregisterCallback(mControllerCallback);
- } catch (Exception e) {
- // ignoring
- }
- }
- }
-
- private void dispatchKeyCode(int keyCode) {
- final long now = SystemClock.uptimeMillis();
- KeyEvent down = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD);
- KeyEvent up = new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD);
- try {
- mController.dispatchMediaButtonEvent(down);
- mController.dispatchMediaButtonEvent(up);
- } catch (RuntimeException e) {
- System.out.println("Failed to dispatch " + keyCode);
- }
- }
- }
-
- private void runListSessions() {
- System.out.println("Sessions:");
- try {
- List<MediaController> controllers = sMediaSessionManager.getActiveSessions(null);
- for (MediaController controller : controllers) {
- if (controller != null) {
- try {
- System.out.println(" tag=" + controller.getTag()
- + ", package=" + controller.getPackageName());
- } catch (RuntimeException e) {
- // ignore
- }
- }
- }
- } catch (Exception e) {
- System.out.println("***Error listing sessions***");
- }
- }
-
- //=================================
- // "volume" command for stream volume control
- private void runVolume() throws Exception {
- VolumeCtrl.run(this);
- }
-}
diff --git a/cmds/media/src/com/android/commands/media/VolumeCtrl.java b/cmds/media/src/com/android/commands/media/VolumeCtrl.java
deleted file mode 100755
index 1629c6f..0000000
--- a/cmds/media/src/com/android/commands/media/VolumeCtrl.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
-**
-** Copyright 2016, 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.
-*/
-
-package com.android.commands.media;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.AudioSystem;
-import android.media.IAudioService;
-import android.os.ServiceManager;
-import android.util.AndroidException;
-
-import com.android.internal.os.BaseCommand;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.lang.ArrayIndexOutOfBoundsException;
-
-/**
- * Command line tool to exercise AudioService.setStreamVolume()
- * and AudioService.adjustStreamVolume()
- */
-public class VolumeCtrl {
-
- private final static String TAG = "VolumeCtrl";
-
- // --stream affects --set, --adj or --get options.
- // --show affects --set and --adj options.
- // --get can be used with --set, --adj or by itself.
- public final static String USAGE = new String(
- "the options are as follows: \n" +
- "\t\t--stream STREAM selects the stream to control, see AudioManager.STREAM_*\n" +
- "\t\t controls AudioManager.STREAM_MUSIC if no stream is specified\n"+
- "\t\t--set INDEX sets the volume index value\n" +
- "\t\t--adj DIRECTION adjusts the volume, use raise|same|lower for the direction\n" +
- "\t\t--get outputs the current volume\n" +
- "\t\t--show shows the UI during the volume change\n" +
- "\texamples:\n" +
- "\t\tadb shell media volume --show --stream 3 --set 11\n" +
- "\t\tadb shell media volume --stream 0 --adj lower\n" +
- "\t\tadb shell media volume --stream 3 --get\n"
- );
-
- private final static int VOLUME_CONTROL_MODE_SET = 1;
- private final static int VOLUME_CONTROL_MODE_ADJUST = 2;
-
- private final static String ADJUST_LOWER = "lower";
- private final static String ADJUST_SAME = "same";
- private final static String ADJUST_RAISE = "raise";
-
- public static void run(BaseCommand cmd) throws Exception {
- //----------------------------------------
- // Default parameters
- int stream = AudioManager.STREAM_MUSIC;
- int volIndex = 5;
- int mode = 0;
- int adjDir = AudioManager.ADJUST_RAISE;
- boolean showUi = false;
- boolean doGet = false;
-
- //----------------------------------------
- // read options
- String option;
- String adjustment = null;
- while ((option = cmd.nextOption()) != null) {
- switch (option) {
- case "--show":
- showUi = true;
- break;
- case "--get":
- doGet = true;
- log(LOG_V, "will get volume");
- break;
- case "--stream":
- stream = Integer.decode(cmd.nextArgRequired()).intValue();
- log(LOG_V, "will control stream=" + stream + " (" + streamName(stream) + ")");
- break;
- case "--set":
- volIndex = Integer.decode(cmd.nextArgRequired()).intValue();
- mode = VOLUME_CONTROL_MODE_SET;
- log(LOG_V, "will set volume to index=" + volIndex);
- break;
- case "--adj":
- mode = VOLUME_CONTROL_MODE_ADJUST;
- adjustment = cmd.nextArgRequired();
- log(LOG_V, "will adjust volume");
- break;
- default:
- throw new IllegalArgumentException("Unknown argument " + option);
- }
- }
-
- //------------------------------
- // Read options: validation
- if (mode == VOLUME_CONTROL_MODE_ADJUST) {
- if (adjustment == null) {
- cmd.showError("Error: no valid volume adjustment (null)");
- return;
- }
- switch (adjustment) {
- case ADJUST_RAISE: adjDir = AudioManager.ADJUST_RAISE; break;
- case ADJUST_SAME: adjDir = AudioManager.ADJUST_SAME; break;
- case ADJUST_LOWER: adjDir = AudioManager.ADJUST_LOWER; break;
- default:
- cmd.showError("Error: no valid volume adjustment, was " + adjustment
- + ", expected " + ADJUST_LOWER + "|" + ADJUST_SAME + "|"
- + ADJUST_RAISE);
- return;
- }
- }
-
- //----------------------------------------
- // Test initialization
- log(LOG_V, "Connecting to AudioService");
- IAudioService audioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
- Context.AUDIO_SERVICE));
- if (audioService == null) {
- System.err.println(BaseCommand.NO_SYSTEM_ERROR_CODE);
- throw new AndroidException(
- "Can't connect to audio service; is the system running?");
- }
-
- if (mode == VOLUME_CONTROL_MODE_SET) {
- if ((volIndex > audioService.getStreamMaxVolume(stream))
- || (volIndex < audioService.getStreamMinVolume(stream))) {
- cmd.showError(String.format("Error: invalid volume index %d for stream %d "
- + "(should be in [%d..%d])", volIndex, stream,
- audioService.getStreamMinVolume(stream),
- audioService.getStreamMaxVolume(stream)));
- return;
- }
- }
-
- //----------------------------------------
- // Non-interactive test
- final int flag = showUi? AudioManager.FLAG_SHOW_UI : 0;
- final String pack = cmd.getClass().getPackage().getName();
- if (mode == VOLUME_CONTROL_MODE_SET) {
- audioService.setStreamVolume(stream, volIndex, flag, pack/*callingPackage*/);
- } else if (mode == VOLUME_CONTROL_MODE_ADJUST) {
- audioService.adjustStreamVolume(stream, adjDir, flag, pack);
- }
- if (doGet) {
- log(LOG_V, "volume is " + audioService.getStreamVolume(stream) +
- " in range [" + audioService.getStreamMinVolume(stream) +
- ".." + audioService.getStreamMaxVolume(stream) + "]");
- }
- }
-
- //--------------------------------------------
- // Utilities
-
- static final String LOG_V = "[v]";
- static final String LOG_W = "[w]";
- static final String LOG_OK = "[ok]";
-
- static void log(String code, String msg) {
- System.out.println(code + " " + msg);
- }
-
- static String streamName(int stream) {
- try {
- return AudioSystem.STREAM_NAMES[stream];
- } catch (ArrayIndexOutOfBoundsException e) {
- return "invalid stream";
- }
- }
-
-}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 4410f1c..bb32dd2 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -105,7 +105,7 @@
char *cmd[] = {
(char*) "am",
(char*) "broadcast",
- (char*) "am",
+ (char*) "-a",
(char*) "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
(char*) "-d",
&filePath[0],
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index e494ea3..0617eb6 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -44,19 +44,9 @@
cc_defaults {
name: "statsd_defaults",
- aidl: {
- include_dirs: ["frameworks/base/core/java"],
- },
srcs: [
- ":statsd_aidl",
- ":ICarStatsService.aidl",
"src/active_config_list.proto",
- "src/statsd_config.proto",
- "src/uid_data.proto",
- "src/FieldValue.cpp",
- "src/hash.cpp",
- "src/stats_log_util.cpp",
"src/anomaly/AlarmMonitor.cpp",
"src/anomaly/AlarmTracker.cpp",
"src/anomaly/AnomalyTracker.cpp",
@@ -64,52 +54,57 @@
"src/anomaly/subscriber_util.cpp",
"src/condition/CombinationConditionTracker.cpp",
"src/condition/condition_util.cpp",
- "src/condition/SimpleConditionTracker.cpp",
"src/condition/ConditionWizard.cpp",
- "src/condition/StateTracker.cpp",
+ "src/condition/SimpleConditionTracker.cpp",
"src/config/ConfigKey.cpp",
"src/config/ConfigListener.cpp",
"src/config/ConfigManager.cpp",
- "src/external/CarStatsPuller.cpp",
- "src/external/GpuStatsPuller.cpp",
+ "src/experiment_ids.proto",
"src/external/Perfetto.cpp",
- "src/external/StatsPuller.cpp",
- "src/external/StatsCallbackPuller.cpp",
- "src/external/StatsCompanionServicePuller.cpp",
- "src/external/SubsystemSleepStatePuller.cpp",
- "src/external/PowerStatsPuller.cpp",
- "src/external/ResourceHealthManagerPuller.cpp",
- "src/external/TrainInfoPuller.cpp",
- "src/external/StatsPullerManager.cpp",
+ "src/external/PullResultReceiver.cpp",
"src/external/puller_util.cpp",
+ "src/external/StatsCallbackPuller.cpp",
+ "src/external/StatsPuller.cpp",
+ "src/external/StatsPullerManager.cpp",
+ "src/external/TrainInfoPuller.cpp",
+ "src/FieldValue.cpp",
+ "src/guardrail/StatsdStats.cpp",
+ "src/hash.cpp",
+ "src/HashableDimensionKey.cpp",
"src/logd/LogEvent.cpp",
"src/logd/LogEventQueue.cpp",
"src/matchers/CombinationLogMatchingTracker.cpp",
"src/matchers/EventMatcherWizard.cpp",
"src/matchers/matcher_util.cpp",
"src/matchers/SimpleLogMatchingTracker.cpp",
- "src/metrics/MetricProducer.cpp",
- "src/metrics/EventMetricProducer.cpp",
+ "src/metadata_util.cpp",
"src/metrics/CountMetricProducer.cpp",
- "src/metrics/DurationMetricProducer.cpp",
- "src/metrics/duration_helper/OringDurationTracker.cpp",
"src/metrics/duration_helper/MaxDurationTracker.cpp",
- "src/metrics/ValueMetricProducer.cpp",
+ "src/metrics/duration_helper/OringDurationTracker.cpp",
+ "src/metrics/DurationMetricProducer.cpp",
+ "src/metrics/EventMetricProducer.cpp",
"src/metrics/GaugeMetricProducer.cpp",
- "src/metrics/MetricsManager.cpp",
+ "src/metrics/MetricProducer.cpp",
"src/metrics/metrics_manager_util.cpp",
+ "src/metrics/MetricsManager.cpp",
+ "src/metrics/ValueMetricProducer.cpp",
"src/packages/UidMap.cpp",
- "src/storage/StorageManager.cpp",
+ "src/shell/shell_config.proto",
+ "src/shell/ShellSubscriber.cpp",
+ "src/socket/StatsSocketListener.cpp",
+ "src/state/StateManager.cpp",
+ "src/state/StateTracker.cpp",
+ "src/stats_log_util.cpp",
+ "src/statscompanion_util.cpp",
+ "src/statsd_config.proto",
+ "src/statsd_metadata.proto",
"src/StatsLogProcessor.cpp",
"src/StatsService.cpp",
- "src/statscompanion_util.cpp",
+ "src/storage/StorageManager.cpp",
"src/subscriber/IncidentdReporter.cpp",
"src/subscriber/SubscriberReporter.cpp",
- "src/HashableDimensionKey.cpp",
- "src/guardrail/StatsdStats.cpp",
- "src/socket/StatsSocketListener.cpp",
- "src/shell/ShellSubscriber.cpp",
- "src/shell/shell_config.proto",
+ "src/uid_data.proto",
+ "src/utils/MultiConditionTrigger.cpp",
],
local_include_dirs: [
@@ -117,79 +112,82 @@
],
static_libs: [
- "libhealthhalutils",
- ],
-
- shared_libs: [
"libbase",
- "libbinder",
- "libgraphicsenv",
+ "libcutils",
+ "libgtest_prod",
+ "libprotoutil",
+ "libstatslog_statsd",
+ "libsysutils",
+ "libutils",
+ "statsd-aidl-ndk_platform",
+ ],
+ shared_libs: [
+ "libbinder_ndk",
"libincident",
"liblog",
- "libutils",
- "libservices",
- "libprotoutil",
- "libstatslog",
- "libhardware",
- "libhardware_legacy",
- "libhidlbase",
- "android.frameworks.stats@1.0",
- "android.hardware.health@2.0",
- "android.hardware.power@1.0",
- "android.hardware.power@1.1",
- "android.hardware.power.stats@1.0",
- "libpackagelistparser",
- "libstatsmetadata",
- "libsysutils",
- "libcutils",
- ],
-}
-
-// ================
-// 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",
+ name: "statslog_statsd.h",
tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --atomsInfoCpp $(genDir)/atoms_info.cpp",
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsd.h --module statsd --namespace android,os,statsd,util",
out: [
- "atoms_info.cpp",
+ "statslog_statsd.h",
],
}
-cc_library_shared {
- name: "libstatsmetadata",
- host_supported: true,
- generated_sources: [
- "atoms_info.cpp",
+genrule {
+ name: "statslog_statsd.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsd.cpp --module statsd --namespace android,os,statsd,util --importHeader statslog_statsd.h",
+ out: [
+ "statslog_statsd.cpp",
],
- generated_headers: [
- "atoms_info.h",
+}
+
+genrule {
+ name: "statslog_statsdtest.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsdtest.h --module statsdtest --namespace android,os,statsd,util",
+ out: [
+ "statslog_statsdtest.h",
],
- cflags: [
- "-Wall",
- "-Werror",
+}
+
+genrule {
+ name: "statslog_statsdtest.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsdtest.cpp --module statsdtest --namespace android,os,statsd,util --importHeader statslog_statsdtest.h",
+ out: [
+ "statslog_statsdtest.cpp",
],
- export_generated_headers: [
- "atoms_info.h",
+}
+
+cc_library_static {
+ name: "libstatslog_statsdtest",
+ generated_sources: ["statslog_statsdtest.cpp"],
+ generated_headers: ["statslog_statsdtest.h"],
+ export_generated_headers: ["statslog_statsdtest.h"],
+ shared_libs: [
+ "libstatssocket",
+ ]
+}
+
+cc_library_static {
+ name: "libstatslog_statsd",
+ generated_sources: ["statslog_statsd.cpp"],
+ generated_headers: ["statslog_statsd.h"],
+ export_generated_headers: ["statslog_statsd.h"],
+ apex_available: [
+ "com.android.os.statsd",
+ "test_com.android.os.statsd",
],
shared_libs: [
- "libcutils",
- "libstatslog",
- ],
+ "libstatssocket",
+ ]
}
-
// =========
// statsd
// =========
@@ -226,11 +224,18 @@
proto: {
type: "lite",
+ static: true,
},
+ stl: "libc++_static",
- shared_libs: ["libgtest_prod"],
+ shared_libs: [
+ "libstatssocket",
+ ],
- init_rc: ["statsd.rc"],
+ apex_available: [
+ "com.android.os.statsd",
+ "test_com.android.os.statsd",
+ ],
}
// ==============
@@ -240,7 +245,20 @@
cc_test {
name: "statsd_test",
defaults: ["statsd_defaults"],
- test_suites: ["device-tests"],
+ test_suites: ["device-tests", "mts"],
+ test_config: "statsd_test.xml",
+
+ //TODO(b/153588990): Remove when the build system properly separates
+ //32bit and 64bit architectures.
+ compile_multilib: "both",
+ multilib: {
+ lib64: {
+ suffix: "64",
+ },
+ lib32: {
+ suffix: "32",
+ },
+ },
cflags: [
"-Wall",
@@ -251,6 +269,8 @@
"-Wno-unused-parameter",
],
+ require_root: true,
+
srcs: [
// atom_field_options.proto needs field_options.proto, but that is
// not included in libprotobuf-cpp-lite, so compile it here.
@@ -258,62 +278,65 @@
"src/atom_field_options.proto",
"src/atoms.proto",
- "src/stats_log.proto",
"src/shell/shell_data.proto",
+ "src/stats_log.proto",
"tests/AlarmMonitor_test.cpp",
"tests/anomaly/AlarmTracker_test.cpp",
"tests/anomaly/AnomalyTracker_test.cpp",
+ "tests/condition/CombinationConditionTracker_test.cpp",
+ "tests/condition/ConditionTimer_test.cpp",
+ "tests/condition/SimpleConditionTracker_test.cpp",
"tests/ConfigManager_test.cpp",
+ "tests/e2e/Alarm_e2e_test.cpp",
+ "tests/e2e/Anomaly_count_e2e_test.cpp",
+ "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
+ "tests/e2e/Attribution_e2e_test.cpp",
+ "tests/e2e/ConfigTtl_e2e_test.cpp",
+ "tests/e2e/CountMetric_e2e_test.cpp",
+ "tests/e2e/DurationMetric_e2e_test.cpp",
+ "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
+ "tests/e2e/GaugeMetric_e2e_push_test.cpp",
+ "tests/e2e/MetricActivation_e2e_test.cpp",
+ "tests/e2e/MetricConditionLink_e2e_test.cpp",
+ "tests/e2e/PartialBucket_e2e_test.cpp",
+ "tests/e2e/ValueMetric_pull_e2e_test.cpp",
+ "tests/e2e/WakelockDuration_e2e_test.cpp",
"tests/external/puller_util_test.cpp",
- "tests/external/GpuStatsPuller_test.cpp",
- "tests/external/IncidentReportArgs_test.cpp",
+ "tests/external/StatsCallbackPuller_test.cpp",
"tests/external/StatsPuller_test.cpp",
+ "tests/external/StatsPullerManager_test.cpp",
+ "tests/FieldValue_test.cpp",
+ "tests/guardrail/StatsdStats_test.cpp",
+ "tests/HashableDimensionKey_test.cpp",
"tests/indexed_priority_queue_test.cpp",
+ "tests/log_event/LogEventQueue_test.cpp",
"tests/LogEntryMatcher_test.cpp",
"tests/LogEvent_test.cpp",
- "tests/log_event/LogEventQueue_test.cpp",
- "tests/MetricsManager_test.cpp",
- "tests/StatsLogProcessor_test.cpp",
- "tests/StatsService_test.cpp",
- "tests/UidMap_test.cpp",
- "tests/FieldValue_test.cpp",
- "tests/condition/CombinationConditionTracker_test.cpp",
- "tests/condition/SimpleConditionTracker_test.cpp",
- "tests/condition/StateTracker_test.cpp",
- "tests/condition/ConditionTimer_test.cpp",
- "tests/metrics/OringDurationTracker_test.cpp",
- "tests/metrics/MaxDurationTracker_test.cpp",
+ "tests/metadata_util_test.cpp",
"tests/metrics/CountMetricProducer_test.cpp",
"tests/metrics/DurationMetricProducer_test.cpp",
"tests/metrics/EventMetricProducer_test.cpp",
- "tests/metrics/ValueMetricProducer_test.cpp",
"tests/metrics/GaugeMetricProducer_test.cpp",
- "tests/guardrail/StatsdStats_test.cpp",
+ "tests/metrics/MaxDurationTracker_test.cpp",
"tests/metrics/metrics_test_helper.cpp",
- "tests/statsd_test_util.cpp",
- "tests/storage/StorageManager_test.cpp",
- "tests/e2e/WakelockDuration_e2e_test.cpp",
- "tests/e2e/MetricActivation_e2e_test.cpp",
- "tests/e2e/MetricConditionLink_e2e_test.cpp",
- "tests/e2e/Alarm_e2e_test.cpp",
- "tests/e2e/Attribution_e2e_test.cpp",
- "tests/e2e/GaugeMetric_e2e_push_test.cpp",
- "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
- "tests/e2e/ValueMetric_pull_e2e_test.cpp",
- "tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp",
- "tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp",
- "tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp",
- "tests/e2e/Anomaly_count_e2e_test.cpp",
- "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
- "tests/e2e/ConfigTtl_e2e_test.cpp",
- "tests/e2e/PartialBucket_e2e_test.cpp",
- "tests/e2e/DurationMetric_e2e_test.cpp",
+ "tests/metrics/OringDurationTracker_test.cpp",
+ "tests/metrics/ValueMetricProducer_test.cpp",
+ "tests/MetricsManager_test.cpp",
"tests/shell/ShellSubscriber_test.cpp",
+ "tests/state/StateTracker_test.cpp",
+ "tests/statsd_test_util.cpp",
+ "tests/StatsLogProcessor_test.cpp",
+ "tests/StatsService_test.cpp",
+ "tests/storage/StorageManager_test.cpp",
+ "tests/UidMap_test.cpp",
+ "tests/utils/MultiConditionTrigger_test.cpp",
],
static_libs: [
"libgmock",
"libplatformprotos",
+ "libstatslog_statsdtest",
+ "libstatssocket_private",
],
proto: {
@@ -321,8 +344,6 @@
include_dirs: ["external/protobuf/src"],
},
- shared_libs: ["libprotobuf-cpp-lite"],
-
}
//#############################
@@ -338,17 +359,17 @@
// not included in libprotobuf-cpp-lite, so compile it here.
":libprotobuf-internal-protos",
+ "benchmark/duration_metric_benchmark.cpp",
+ "benchmark/filter_value_benchmark.cpp",
+ "benchmark/get_dimensions_for_condition_benchmark.cpp",
+ "benchmark/hello_world_benchmark.cpp",
+ "benchmark/log_event_benchmark.cpp",
+ "benchmark/main.cpp",
+ "benchmark/metric_util.cpp",
+ "benchmark/stats_write_benchmark.cpp",
"src/atom_field_options.proto",
"src/atoms.proto",
"src/stats_log.proto",
- "benchmark/main.cpp",
- "benchmark/hello_world_benchmark.cpp",
- "benchmark/log_event_benchmark.cpp",
- "benchmark/stats_write_benchmark.cpp",
- "benchmark/filter_value_benchmark.cpp",
- "benchmark/get_dimensions_for_condition_benchmark.cpp",
- "benchmark/metric_util.cpp",
- "benchmark/duration_metric_benchmark.cpp",
],
proto: {
@@ -364,17 +385,18 @@
"-Wno-unused-function",
// Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
- "-Wno-varargs"
+ "-Wno-varargs",
],
static_libs: [
"libplatformprotos",
+ "libstatssocket_private",
],
shared_libs: [
"libgtest_prod",
- "libstatslog",
"libprotobuf-cpp-lite",
+ "libstatslog",
],
}
@@ -388,11 +410,11 @@
},
srcs: [
- "src/stats_log.proto",
- "src/statsd_config.proto",
"src/atoms.proto",
"src/shell/shell_config.proto",
"src/shell/shell_data.proto",
+ "src/stats_log.proto",
+ "src/statsd_config.proto",
],
static_libs: [
diff --git a/cmds/statsd/AndroidTest.xml b/cmds/statsd/AndroidTest.xml
deleted file mode 100644
index afe30a2..0000000
--- a/cmds/statsd/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 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="Config for statsd_test">
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="statsd_test->/data/nativetest/statsd_test" />
- </target_preparer>
- <option name="test-suite-tag" value="apct" />
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/nativetest" />
- <option name="module-name" value="statsd_test" />
- </test>
-</configuration>
\ No newline at end of file
diff --git a/cmds/statsd/TEST_MAPPING b/cmds/statsd/TEST_MAPPING
new file mode 100644
index 0000000..8dee073
--- /dev/null
+++ b/cmds/statsd/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit" : [
+ {
+ "name" : "statsd_test"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/cmds/statsd/benchmark/duration_metric_benchmark.cpp b/cmds/statsd/benchmark/duration_metric_benchmark.cpp
index 2631009..2d315d9 100644
--- a/cmds/statsd/benchmark/duration_metric_benchmark.cpp
+++ b/cmds/statsd/benchmark/duration_metric_benchmark.cpp
@@ -137,77 +137,74 @@
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 11));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 40));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 11,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 40, android::view::DISPLAY_STATE_ON));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 102));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 450));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 102,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
+ android::view::DISPLAY_STATE_ON));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 650));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 650,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
+ android::view::DISPLAY_STATE_ON));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + bucketSizeNs + 640));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 650));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 640,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 650,
+ android::view::DISPLAY_STATE_ON));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
+ vector<int> attributionUids1 = {9999};
+ vector<string> attributionTags1 = {""};
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 2, attributionUids1,
+ attributionTags1, "job0"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
+ attributionTags1, "job0"));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids1,
+ attributionTags1, "job2"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids1,
+ attributionTags1, "job2"));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
+ vector<int> attributionUids2 = {8888};
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
+ attributionTags1, "job2"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
+ attributionUids2, attributionTags1, "job2"));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 600,
+ attributionUids2, attributionTags1, "job1"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
+ attributionUids2, attributionTags1, "job1"));
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 10));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
+ vector<int> attributionUids3 = {111, 222, 222};
+ vector<string> attributionTags3 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 10, attributionUids3,
+ attributionTags3, "ReadEmail"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 50, attributionUids3, attributionTags3,
+ "ReadEmail"));
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 200));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 300));
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200, attributionUids3,
+ attributionTags3, "ReadEmail"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids3,
+ attributionTags3, "ReadEmail"));
- events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
- bucketStartTimeNs + 400));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids3,
+ attributionTags3, "ReadDoc"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids3,
+ attributionTags3, "ReadDoc"));
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + 401));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
-
+ vector<int> attributionUids4 = {333, 222, 555};
+ vector<string> attributionTags4 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 401, attributionUids4,
+ attributionTags4, "ReadEmail"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids4,
+ attributionTags4, "ReadEmail"));
sortLogEventsByTimestamp(&events);
while (state.KeepRunning()) {
@@ -230,78 +227,75 @@
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions3 = {
- CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 55));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 120));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 121));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 450));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 55,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 120,
+ android::view::DISPLAY_STATE_ON));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 121,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
+ android::view::DISPLAY_STATE_ON));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 501));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 501,
+ android::view::DISPLAY_STATE_OFF));
+ events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
+ android::view::DISPLAY_STATE_ON));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
+ vector<int> attributionUids1 = {111};
+ vector<string> attributionTags1 = {"App1"};
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 1, attributionUids1,
+ attributionTags1, "job1"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
+ attributionTags1, "job1"));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2",
- bucketStartTimeNs + bucketSizeNs + 850));
+ vector<int> attributionUids2 = {333};
+ vector<string> attributionTags2 = {"App2"};
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids2,
+ attributionTags2, "job2"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids2,
+ attributionTags2, "job2"));
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
+ attributionTags2, "job2"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
+ attributionUids2, attributionTags2, "job2"));
- events.push_back(
- CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs - 2));
- events.push_back(
- CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs + 900));
+ vector<int> attributionUids3 = {444};
+ vector<string> attributionTags3 = {"App3"};
+ events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs - 2,
+ attributionUids3, attributionTags3, "job3"));
+ events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
+ attributionUids3, attributionTags3, "job3"));
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 110));
+ vector<int> attributionUids4 = {111, 222, 222};
+ vector<string> attributionTags4 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids4,
+ attributionTags4, "ReadEmail"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 110, attributionUids4, attributionTags4,
+ "ReadEmail"));
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + 300));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
- events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + 400));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
+ vector<int> attributionUids5 = {333, 222, 555};
+ vector<string> attributionTags5 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 300, attributionUids5,
+ attributionTags5, "ReadEmail"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids5,
+ attributionTags5, "ReadEmail"));
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids5,
+ attributionTags5, "ReadDoc"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids5,
+ attributionTags5, "ReadDoc"));
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 550));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 800));
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs + 700));
+ vector<int> attributionUids6 = {444, 222, 555};
+ vector<string> attributionTags6 = {"App3", "GMSCoreModule1", "GMSCoreModule2"};
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 550, attributionUids6,
+ attributionTags6, "ReadDoc"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 800, attributionUids6, attributionTags6,
+ "ReadDoc"));
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids6,
+ attributionTags6, "ReadDoc"));
+ events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids6,
+ attributionTags6, "ReadDoc"));
sortLogEventsByTimestamp(&events);
while (state.KeepRunning()) {
diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp
index cfe477d..743ccc4 100644
--- a/cmds/statsd/benchmark/filter_value_benchmark.cpp
+++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp
@@ -14,10 +14,13 @@
* limitations under the License.
*/
#include <vector>
-#include "benchmark/benchmark.h"
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
+#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
+#include "metric_util.h"
+#include "stats_event.h"
#include "stats_log_util.h"
namespace android {
@@ -26,17 +29,20 @@
using std::vector;
-static void createLogEventAndMatcher(LogEvent* event, FieldMatcher *field_matcher) {
- AttributionNodeInternal node;
- node.set_uid(100);
- node.set_tag("LOCATION");
+static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matcher) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, 1);
+ AStatsEvent_overwriteTimestamp(statsEvent, 100000);
- std::vector<AttributionNodeInternal> nodes = {node, node};
- event->write(nodes);
- event->write(3.2f);
- event->write("LOCATION");
- event->write((int64_t)990);
- event->init();
+ std::vector<int> attributionUids = {100, 100};
+ std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+
+ AStatsEvent_writeFloat(statsEvent, 3.2f);
+ AStatsEvent_writeString(statsEvent, "LOCATION");
+ AStatsEvent_writeInt64(statsEvent, 990);
+
+ parseStatsEventToLogEvent(statsEvent, event);
field_matcher->set_field(1);
auto child = field_matcher->add_child();
@@ -46,7 +52,7 @@
}
static void BM_FilterValue(benchmark::State& state) {
- LogEvent event(1, 100000);
+ LogEvent event(/*uid=*/0, /*pid=*/0);
FieldMatcher field_matcher;
createLogEventAndMatcher(&event, &field_matcher);
diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
index 2a4403e..7a45565 100644
--- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
+++ b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
@@ -14,10 +14,13 @@
* limitations under the License.
*/
#include <vector>
-#include "benchmark/benchmark.h"
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
+#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
+#include "metric_util.h"
+#include "stats_event.h"
#include "stats_log_util.h"
namespace android {
@@ -27,16 +30,19 @@
using std::vector;
static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) {
- AttributionNodeInternal node;
- node.set_uid(100);
- node.set_tag("LOCATION");
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, 1);
+ AStatsEvent_overwriteTimestamp(statsEvent, 100000);
- std::vector<AttributionNodeInternal> nodes = {node, node};
- event->write(nodes);
- event->write(3.2f);
- event->write("LOCATION");
- event->write((int64_t)990);
- event->init();
+ std::vector<int> attributionUids = {100, 100};
+ std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+
+ AStatsEvent_writeFloat(statsEvent, 3.2f);
+ AStatsEvent_writeString(statsEvent, "LOCATION");
+ AStatsEvent_writeInt64(statsEvent, 990);
+
+ parseStatsEventToLogEvent(statsEvent, event);
link->conditionId = 1;
@@ -54,7 +60,7 @@
static void BM_GetDimensionInCondition(benchmark::State& state) {
Metric2Condition link;
- LogEvent event(1, 100000);
+ LogEvent event(/*uid=*/0, /*pid=*/0);
createLogEventAndLink(&event, &link);
while (state.KeepRunning()) {
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index 2603469..057e00b 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -16,55 +16,31 @@
#include <vector>
#include "benchmark/benchmark.h"
#include "logd/LogEvent.h"
+#include "stats_event.h"
namespace android {
namespace os {
namespace statsd {
-using std::vector;
+static size_t createAndParseStatsEvent(uint8_t* msg) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ AStatsEvent_writeInt32(event, 2);
+ AStatsEvent_writeFloat(event, 2.0);
+ AStatsEvent_build(event);
-/* Special markers for android_log_list_element type */
-static const char EVENT_TYPE_LIST_STOP = '\n'; /* declare end of list */
-static const char EVENT_TYPE_UNKNOWN = '?'; /* protocol error */
-
-static const char EVENT_TYPE_INT = 0;
-static const char EVENT_TYPE_LONG = 1;
-static const char EVENT_TYPE_STRING = 2;
-static const char EVENT_TYPE_LIST = 3;
-static const char EVENT_TYPE_FLOAT = 4;
-
-static const int kLogMsgHeaderSize = 28;
-
-static void write4Bytes(int val, vector<char>* buffer) {
- buffer->push_back(static_cast<char>(val));
- buffer->push_back(static_cast<char>((val >> 8) & 0xFF));
- buffer->push_back(static_cast<char>((val >> 16) & 0xFF));
- buffer->push_back(static_cast<char>((val >> 24) & 0xFF));
-}
-
-static void getSimpleLogMsgData(log_msg* msg) {
- vector<char> buffer;
- // stats_log tag id
- write4Bytes(1937006964, &buffer);
- buffer.push_back(EVENT_TYPE_LIST);
- buffer.push_back(2); // field counts;
- buffer.push_back(EVENT_TYPE_INT);
- write4Bytes(10 /* atom id */, &buffer);
- buffer.push_back(EVENT_TYPE_INT);
- write4Bytes(99 /* a value to log*/, &buffer);
- buffer.push_back(EVENT_TYPE_LIST_STOP);
-
- msg->entry.len = buffer.size();
- msg->entry.hdr_size = kLogMsgHeaderSize;
- msg->entry.sec = time(nullptr);
- std::copy(buffer.begin(), buffer.end(), msg->buf + kLogMsgHeaderSize);
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+ memcpy(msg, buf, size);
+ return size;
}
static void BM_LogEventCreation(benchmark::State& state) {
- log_msg msg;
- getSimpleLogMsgData(&msg);
+ uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD];
+ size_t size = createAndParseStatsEvent(msg);
while (state.KeepRunning()) {
- benchmark::DoNotOptimize(LogEvent(msg));
+ LogEvent event(/*uid=*/ 1000, /*pid=*/ 1001);
+ benchmark::DoNotOptimize(event.parseBuffer(msg, size));
}
}
BENCHMARK(BM_LogEventCreation);
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index cca6d52..89fd3d9 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -14,6 +14,8 @@
#include "metric_util.h"
+#include "stats_event.h"
+
namespace android {
namespace os {
namespace statsd {
@@ -245,118 +247,105 @@
return dimensions;
}
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
- const android::view::DisplayStateEnum state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs);
- event->write(state);
- event->init();
- return event;
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags) {
+ vector<const char*> cTags(attributionTags.size());
+ for (int i = 0; i < cTags.size(); i++) {
+ cTags[i] = attributionTags[i].c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(statsEvent,
+ reinterpret_cast<const uint32_t*>(attributionUids.data()),
+ cTags.data(), attributionUids.size());
}
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
- int level, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
- (event->write(level));
- event->init();
- return event;
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
+ AStatsEvent_build(statsEvent);
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+
+ AStatsEvent_release(statsEvent);
+}
+
+std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
+ uint64_t timestampNs, const android::view::DisplayStateEnum state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ AStatsEvent_writeInt32(statsEvent, state);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
}
std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& jobName,
- const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs);
- event->write(attributions);
- event->write(jobName);
- event->write(state);
- event->init();
- return event;
+ const vector<int>& attributionUids, const vector<string>& attributionTags,
+ const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_writeString(statsEvent, jobName.c_str());
+ AStatsEvent_writeInt32(statsEvent, state);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
}
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
- const std::vector<AttributionNodeInternal>& attributions,
- const string& name, uint64_t timestampNs) {
- return CreateScheduledJobStateChangedEvent(
- attributions, name, ScheduledJobStateChanged::STARTED, timestampNs);
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& jobName) {
+ return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+ ScheduledJobStateChanged::STARTED, timestampNs);
}
// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
- const std::vector<AttributionNodeInternal>& attributions,
- const string& name, uint64_t timestampNs) {
- return CreateScheduledJobStateChangedEvent(
- attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs);
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& jobName) {
+ return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+ ScheduledJobStateChanged::FINISHED, timestampNs);
}
-std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- const WakelockStateChanged::State state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs);
- event->write(attributions);
- event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
- event->write(wakelockName);
- event->write(state);
- event->init();
- return event;
+std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& name,
+ const SyncStateChanged::State state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_writeString(statsEvent, name.c_str());
+ AStatsEvent_writeInt32(statsEvent, state);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
}
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- uint64_t timestampNs) {
- return CreateWakelockStateChangedEvent(
- attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
+std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& name) {
+ return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
+ SyncStateChanged::ON);
}
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- uint64_t timestampNs) {
- return CreateWakelockStateChangedEvent(
- attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
- const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(
- android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
- event->write(uid);
- event->write("pkg_name");
- event->write("class_name");
- event->write(state);
- event->init();
- return event;
-}
-
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
- return CreateActivityForegroundStateChangedEvent(
- uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
- return CreateActivityForegroundStateChangedEvent(
- uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- const SyncStateChanged::State state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
- event->write(attributions);
- event->write(name);
- event->write(state);
- event->init();
- return event;
-}
-
-std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- uint64_t timestampNs) {
- return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- uint64_t timestampNs) {
- return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
+std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& name) {
+ return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
+ SyncStateChanged::OFF);
}
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
@@ -373,13 +362,6 @@
return processor;
}
-AttributionNodeInternal CreateAttribution(const int& uid, const string& tag) {
- AttributionNodeInternal attribution;
- attribution.set_uid(uid);
- attribution.set_tag(tag);
- return attribution;
-}
-
void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
std::sort(events->begin(), events->end(),
[](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h
index 9b28d60..3efaa85 100644
--- a/cmds/statsd/benchmark/metric_util.h
+++ b/cmds/statsd/benchmark/metric_util.h
@@ -18,6 +18,7 @@
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "src/StatsLogProcessor.h"
#include "src/logd/LogEvent.h"
+#include "stats_event.h"
#include "statslog.h"
namespace android {
@@ -92,60 +93,38 @@
FieldMatcher CreateAttributionUidDimensions(const int atomId,
const std::vector<Position>& positions);
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags);
+
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
+
// Create log event for screen state changed.
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
- const android::view::DisplayStateEnum state, uint64_t timestampNs);
-
-// Create log event for screen brightness state changed.
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
- int level, uint64_t timestampNs);
+ uint64_t timestampNs, const android::view::DisplayStateEnum state);
// Create log event when scheduled job starts.
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
- const std::vector<AttributionNodeInternal>& attributions,
- const string& name, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& jobName);
// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
- const std::vector<AttributionNodeInternal>& attributions,
- const string& name, uint64_t timestampNs);
-
-// Create log event for app moving to background.
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
-
-// Create log event for app moving to foreground.
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& jobName);
// Create log event when the app sync starts.
-std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& name);
// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- uint64_t timestampNs);
-
-// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateAppCrashEvent(
- const int uid, uint64_t timestampNs);
-
-// Create log event for acquiring wakelock.
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- uint64_t timestampNs);
-
-// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- uint64_t timestampNs);
-
-// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
- int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs);
-
-// Helper function to create an AttributionNodeInternal proto.
-AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
+std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& name);
// Create a statsd log event processor upon the start time in seconds, config and key.
sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
@@ -158,4 +137,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 320a32a..c9ccfb9 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -18,7 +18,6 @@
#include "Log.h"
#include "FieldValue.h"
#include "HashableDimensionKey.h"
-#include "atoms_info.h"
#include "math.h"
namespace android {
@@ -116,28 +115,13 @@
}
bool isAttributionUidField(const FieldValue& value) {
- int field = value.mField.getField() & 0xff007f;
- if (field == 0x10001 && value.mValue.getType() == INT) {
- return true;
- }
- return false;
+ return isAttributionUidField(value.mField, value.mValue);
}
int32_t getUidIfExists(const FieldValue& value) {
- bool isUid = false;
- // the field is uid field if the field is the uid field in attribution node or marked as
- // is_uid in atoms.proto
- if (isAttributionUidField(value)) {
- isUid = true;
- } else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag());
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto
- isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField &&
- value.mValue.getType() == INT;
- }
- }
-
+ // the field is uid field if the field is the uid field in attribution node
+ // or annotated as such in the atom
+ bool isUid = isAttributionUidField(value) || isUidField(value);
return isUid ? value.mValue.int_value : -1;
}
@@ -149,6 +133,10 @@
return false;
}
+bool isUidField(const FieldValue& fieldValue) {
+ return fieldValue.mAnnotations.isUidField();
+}
+
Value::Value(const Value& from) {
type = from.getType();
switch (type) {
@@ -438,6 +426,25 @@
return eq;
}
+bool subsetDimensions(const std::vector<Matcher>& dimension_a,
+ const std::vector<Matcher>& dimension_b) {
+ if (dimension_a.size() > dimension_b.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < dimension_a.size(); ++i) {
+ bool found = false;
+ for (size_t j = 0; j < dimension_b.size(); ++j) {
+ if (dimension_a[i] == dimension_b[j]) {
+ found = true;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool HasPositionANY(const FieldMatcher& matcher) {
if (matcher.has_position() && matcher.position() == Position::ANY) {
return true;
@@ -464,4 +471,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index 6729e05..fd86e36 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -16,6 +16,7 @@
#pragma once
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "annotations.h"
namespace android {
namespace os {
@@ -26,7 +27,6 @@
struct Field;
struct FieldValue;
-const int32_t kAttributionField = 1;
const int32_t kMaxLogDepth = 2;
const int32_t kLastBitMask = 0x80;
const int32_t kClearLastBitDeco = 0x7f;
@@ -180,6 +180,7 @@
return false;
}
+
bool matches(const Matcher& that) const;
};
@@ -261,6 +262,11 @@
return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000);
}
+inline Matcher getFirstUidMatcher(int32_t atomId) {
+ int32_t pos[] = {1, 1, 1};
+ return Matcher(Field(atomId, pos, 2), 0xff7f7f7f);
+}
+
/**
* A wrapper for a union type to contain multiple types of values.
*
@@ -352,6 +358,56 @@
Value& operator=(const Value& that);
};
+class Annotations {
+public:
+ Annotations() {
+ setNested(true); // Nested = true by default
+ }
+
+ // This enum stores where particular annotations can be found in the
+ // bitmask. Note that these pos do not correspond to annotation ids.
+ enum {
+ NESTED_POS = 0x0,
+ PRIMARY_POS = 0x1,
+ EXCLUSIVE_POS = 0x2,
+ UID_POS = 0x3
+ };
+
+ inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); }
+
+ inline void setPrimaryField(bool primary) { setBitmaskAtPos(PRIMARY_POS, primary); }
+
+ inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); }
+
+ inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); }
+
+ // Default value = false
+ inline bool isNested() const { return getValueFromBitmask(NESTED_POS); }
+
+ // Default value = false
+ inline bool isPrimaryField() const { return getValueFromBitmask(PRIMARY_POS); }
+
+ // Default value = false
+ inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); }
+
+ // Default value = false
+ inline bool isUidField() const { return getValueFromBitmask(UID_POS); }
+
+private:
+ inline void setBitmaskAtPos(int pos, bool value) {
+ mBooleanBitmask &= ~(1 << pos); // clear
+ mBooleanBitmask |= (value << pos); // set
+ }
+
+ inline bool getValueFromBitmask(int pos) const {
+ return (mBooleanBitmask >> pos) & 0x1;
+ }
+
+ // This is a bitmask over all annotations stored in boolean form. Because
+ // there are only 4 booleans, just one byte is required.
+ uint8_t mBooleanBitmask = 0;
+};
+
/**
* Represents a log item, or a dimension item (They are essentially the same).
*/
@@ -379,6 +435,7 @@
Field mField;
Value mValue;
+ Annotations mAnnotations;
};
bool HasPositionANY(const FieldMatcher& matcher);
@@ -392,9 +449,14 @@
void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
bool isAttributionUidField(const Field& field, const Value& value);
+bool isUidField(const FieldValue& fieldValue);
bool equalDimensions(const std::vector<Matcher>& dimension_a,
const std::vector<Matcher>& dimension_b);
+
+// Returns true if dimension_a is a subset of dimension_b.
+bool subsetDimensions(const std::vector<Matcher>& dimension_a,
+ const std::vector<Matcher>& dimension_b);
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 05acef8..eba66e0 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -25,6 +25,100 @@
using std::string;
using std::vector;
+using android::base::StringPrintf;
+
+// 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_LONG_TYPE = 4;
+// const static int STATS_DIMENSIONS_VALUE_BOOL_TYPE = 5; (commented out because
+// unused -- statsd does not correctly support bool types)
+const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
+const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
+
+/**
+ * Recursive helper function that populates a parent StatsDimensionsValueParcel
+ * with children StatsDimensionsValueParcels.
+ *
+ * \param parent parcel that will be populated with children
+ * \param childDepth depth of children FieldValues
+ * \param childPrefix expected FieldValue prefix of children
+ * \param dims vector of FieldValues stored by HashableDimensionKey
+ * \param index position in dims to start reading children from
+ */
+static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel& parent,
+ int childDepth, int childPrefix,
+ const vector<FieldValue>& dims,
+ size_t& index) {
+ if (childDepth > 2) {
+ ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel.");
+ return;
+ }
+
+ while (index < dims.size()) {
+ const FieldValue& dim = dims[index];
+ int fieldDepth = dim.mField.getDepth();
+ int fieldPrefix = dim.mField.getPrefix(childDepth);
+
+ StatsDimensionsValueParcel child;
+ child.field = dim.mField.getPosAtDepth(childDepth);
+
+ if (fieldDepth == childDepth && fieldPrefix == childPrefix) {
+ switch (dim.mValue.getType()) {
+ case INT:
+ child.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE;
+ child.intValue = dim.mValue.int_value;
+ break;
+ case LONG:
+ child.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE;
+ child.longValue = dim.mValue.long_value;
+ break;
+ case FLOAT:
+ child.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE;
+ child.floatValue = dim.mValue.float_value;
+ break;
+ case STRING:
+ child.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
+ child.stringValue = dim.mValue.str_value;
+ break;
+ default:
+ ALOGE("Encountered FieldValue with unsupported value type.");
+ break;
+ }
+ index++;
+ parent.tupleValue.push_back(child);
+ } else if (fieldDepth > childDepth && fieldPrefix == childPrefix) {
+ // This FieldValue is not a child of the current parent, but it is
+ // an indirect descendant. Thus, create a direct child of TUPLE_TYPE
+ // and recurse to parcel the indirect descendants.
+ child.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
+ populateStatsDimensionsValueParcelChildren(child, childDepth + 1,
+ dim.mField.getPrefix(childDepth + 1), dims,
+ index);
+ parent.tupleValue.push_back(child);
+ } else {
+ return;
+ }
+ }
+}
+
+StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const {
+ StatsDimensionsValueParcel root;
+ if (mValues.size() == 0) {
+ return root;
+ }
+
+ root.field = mValues[0].mField.getTag();
+ root.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
+
+ // Children of the root correspond to top-level (depth = 0) FieldValues.
+ int childDepth = 0;
+ int childPrefix = 0;
+ size_t index = 0;
+ populateStatsDimensionsValueParcelChildren(root, childDepth, childPrefix, mValues, index);
+
+ return root;
+}
android::hash_t hashDimension(const HashableDimensionKey& value) {
android::hash_t hash = 0;
@@ -57,6 +151,17 @@
return JenkinsHashWhiten(hash);
}
+bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values,
+ FieldValue* output) {
+ for (const auto& value : values) {
+ if (value.mField.matches(matcherField)) {
+ (*output) = value;
+ return true;
+ }
+ }
+ return false;
+}
+
bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
HashableDimensionKey* output) {
size_t num_matches = 0;
@@ -75,6 +180,23 @@
return num_matches > 0;
}
+bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output) {
+ size_t num_matches = 0;
+ const int32_t simpleFieldMask = 0xff7f0000;
+ const int32_t attributionUidFieldMask = 0xff7f7f7f;
+ for (const auto& value : values) {
+ if (value.mAnnotations.isPrimaryField()) {
+ output->addValue(value);
+ output->mutableValue(num_matches)->mField.setTag(value.mField.getTag());
+ const int32_t mask =
+ isAttributionUidField(value) ? attributionUidFieldMask : simpleFieldMask;
+ output->mutableValue(num_matches)->mField.setField(value.mField.getField() & mask);
+ num_matches++;
+ }
+ }
+ return num_matches > 0;
+}
+
void filterGaugeValues(const std::vector<Matcher>& matcherFields,
const std::vector<FieldValue>& values, std::vector<FieldValue>* output) {
for (const auto& field : matcherFields) {
@@ -94,18 +216,78 @@
size_t count = conditionDimension->getValues().size();
if (count != links.conditionFields.size()) {
- // ALOGE("WTF condition link is bad");
return;
}
for (size_t i = 0; i < count; i++) {
conditionDimension->mutableValue(i)->mField.setField(
- links.conditionFields[i].mMatcher.getField());
+ links.conditionFields[i].mMatcher.getField());
conditionDimension->mutableValue(i)->mField.setTag(
- links.conditionFields[i].mMatcher.getTag());
+ links.conditionFields[i].mMatcher.getTag());
}
}
+void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
+ HashableDimensionKey* statePrimaryKey) {
+ // First, get the dimension from the event using the "what" fields from the
+ // MetricStateLinks.
+ filterValues(link.metricFields, eventValues, statePrimaryKey);
+
+ // Then check that the statePrimaryKey size equals the number of state fields
+ size_t count = statePrimaryKey->getValues().size();
+ if (count != link.stateFields.size()) {
+ return;
+ }
+
+ // For each dimension Value in the statePrimaryKey, set the field and tag
+ // using the state atom fields from MetricStateLinks.
+ for (size_t i = 0; i < count; i++) {
+ statePrimaryKey->mutableValue(i)->mField.setField(link.stateFields[i].mMatcher.getField());
+ statePrimaryKey->mutableValue(i)->mField.setTag(link.stateFields[i].mMatcher.getTag());
+ }
+}
+
+bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
+ const HashableDimensionKey& primaryKey,
+ const vector<Metric2State>& stateLinks, const int32_t stateAtomId) {
+ if (whatKey.getValues().size() < primaryKey.getValues().size()) {
+ ALOGE("Contains linked values false: whatKey is too small");
+ return false;
+ }
+
+ for (const auto& primaryValue : primaryKey.getValues()) {
+ bool found = false;
+ for (const auto& whatValue : whatKey.getValues()) {
+ if (linked(stateLinks, stateAtomId, primaryValue.mField, whatValue.mField) &&
+ primaryValue.mValue == whatValue.mValue) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool linked(const vector<Metric2State>& stateLinks, const int32_t stateAtomId,
+ const Field& stateField, const Field& metricField) {
+ for (auto stateLink : stateLinks) {
+ if (stateLink.stateAtomId != stateAtomId) {
+ continue;
+ }
+
+ for (size_t i = 0; i < stateLink.stateFields.size(); i++) {
+ if (stateLink.stateFields[i].mMatcher == stateField &&
+ stateLink.metricFields[i].mMatcher == metricField) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) {
if (s1.size() != s2.size()) {
return s1.size() < s2.size();
@@ -120,6 +302,10 @@
return false;
}
+bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const {
+ return !((*this) == that);
+}
+
bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
if (mValues.size() != that.getValues().size()) {
return false;
@@ -173,11 +359,11 @@
bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
- mDimensionKeyInCondition == that.getDimensionKeyInCondition();
+ mStateValuesKey == that.getStateValuesKey();
};
string MetricDimensionKey::toString() const {
- return mDimensionKeyInWhat.toString() + mDimensionKeyInCondition.toString();
+ return mDimensionKeyInWhat.toString() + mStateValuesKey.toString();
}
bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
@@ -187,7 +373,7 @@
return false;
}
- return mDimensionKeyInCondition < that.getDimensionKeyInCondition();
+ return mStateValuesKey < that.getStateValuesKey();
}
} // namespace statsd
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 6f4941f..bd01100 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -16,17 +16,18 @@
#pragma once
+#include <aidl/android/os/StatsDimensionsValueParcel.h>
#include <utils/JenkinsHash.h>
#include <vector>
-#include "FieldValue.h"
#include "android-base/stringprintf.h"
+#include "FieldValue.h"
#include "logd/LogEvent.h"
namespace android {
namespace os {
namespace statsd {
-using android::base::StringPrintf;
+using ::aidl::android::os::StatsDimensionsValueParcel;
struct Metric2Condition {
int64_t conditionId;
@@ -34,6 +35,12 @@
std::vector<Matcher> conditionFields;
};
+struct Metric2State {
+ int32_t stateAtomId;
+ std::vector<Matcher> metricFields;
+ std::vector<Matcher> stateFields;
+};
+
class HashableDimensionKey {
public:
explicit HashableDimensionKey(const std::vector<FieldValue>& values) {
@@ -63,8 +70,12 @@
return nullptr;
}
+ StatsDimensionsValueParcel toStatsDimensionsValueParcel() const;
+
std::string toString() const;
+ bool operator!=(const HashableDimensionKey& that) const;
+
bool operator==(const HashableDimensionKey& that) const;
bool operator<(const HashableDimensionKey& that) const;
@@ -76,17 +87,16 @@
};
class MetricDimensionKey {
- public:
+public:
explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat,
- const HashableDimensionKey& dimensionKeyInCondition)
- : mDimensionKeyInWhat(dimensionKeyInWhat),
- mDimensionKeyInCondition(dimensionKeyInCondition) {};
+ const HashableDimensionKey& stateValuesKey)
+ : mDimensionKeyInWhat(dimensionKeyInWhat), mStateValuesKey(stateValuesKey){};
MetricDimensionKey(){};
MetricDimensionKey(const MetricDimensionKey& that)
: mDimensionKeyInWhat(that.getDimensionKeyInWhat()),
- mDimensionKeyInCondition(that.getDimensionKeyInCondition()) {};
+ mStateValuesKey(that.getStateValuesKey()){};
MetricDimensionKey& operator=(const MetricDimensionKey& from) = default;
@@ -96,30 +106,41 @@
return mDimensionKeyInWhat;
}
- inline const HashableDimensionKey& getDimensionKeyInCondition() const {
- return mDimensionKeyInCondition;
+ inline const HashableDimensionKey& getStateValuesKey() const {
+ return mStateValuesKey;
}
- inline void setDimensionKeyInCondition(const HashableDimensionKey& key) {
- mDimensionKeyInCondition = key;
+ inline HashableDimensionKey* getMutableStateValuesKey() {
+ return &mStateValuesKey;
}
- bool hasDimensionKeyInCondition() const {
- return mDimensionKeyInCondition.getValues().size() > 0;
+ inline void setStateValuesKey(const HashableDimensionKey& key) {
+ mStateValuesKey = key;
+ }
+
+ bool hasStateValuesKey() const {
+ return mStateValuesKey.getValues().size() > 0;
}
bool operator==(const MetricDimensionKey& that) const;
bool operator<(const MetricDimensionKey& that) const;
- private:
- HashableDimensionKey mDimensionKeyInWhat;
- HashableDimensionKey mDimensionKeyInCondition;
+private:
+ HashableDimensionKey mDimensionKeyInWhat;
+ HashableDimensionKey mStateValuesKey;
};
android::hash_t hashDimension(const HashableDimensionKey& key);
/**
+ * Returns true if a FieldValue field matches the matcher field.
+ * The value of the FieldValue is output.
+ */
+bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values,
+ FieldValue* output);
+
+/**
* Creating HashableDimensionKeys from FieldValues using matcher.
*
* This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
@@ -133,6 +154,18 @@
HashableDimensionKey* output);
/**
+ * Creating HashableDimensionKeys from State Primary Keys in FieldValues.
+ *
+ * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
+ * in it. This is because: for example, when we create dimension from last uid in attribution chain,
+ * In one event, uid 1000 is at position 5 and it's the last
+ * In another event, uid 1000 is at position 6, and it's the last
+ * these 2 events should be mapped to the same dimension. So we will remove the original position
+ * from the dimension key for the uid field (by applying 0x80 bit mask).
+ */
+bool filterPrimaryKey(const std::vector<FieldValue>& values, HashableDimensionKey* output);
+
+/**
* Filter the values from FieldValues using the matchers.
*
* In contrast to the above function, this function will not do any modification to the original
@@ -145,6 +178,39 @@
const Metric2Condition& links,
HashableDimensionKey* conditionDimension);
+/**
+ * Get dimension values using metric's "what" fields and fill statePrimaryKey's
+ * mField information using "state" fields.
+ */
+void getDimensionForState(const std::vector<FieldValue>& eventValues, const Metric2State& link,
+ HashableDimensionKey* statePrimaryKey);
+
+/**
+ * Returns true if the primaryKey values are a subset of the whatKey values.
+ * The values from the primaryKey come from the state atom, so we need to
+ * check that a link exists between the state atom field and what atom field.
+ *
+ * Example:
+ * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
+ * statePrimaryKey = [Atom: 27, {uid: 1005}]
+ * Returns true IF one of the Metric2State links Atom 10's uid to Atom 27's uid
+ *
+ * Example:
+ * whatKey = [Atom: 10, {uid: 1005, wakelock_name: "compose"}]
+ * statePrimaryKey = [Atom: 59, {uid: 1005, package_name: "system"}]
+ * Returns false
+ */
+bool containsLinkedStateValues(const HashableDimensionKey& whatKey,
+ const HashableDimensionKey& primaryKey,
+ const std::vector<Metric2State>& stateLinks,
+ const int32_t stateAtomId);
+
+/**
+ * Returns true if there is a Metric2State link that links the stateField and
+ * the metricField (they are equal fields from different atoms).
+ */
+bool linked(const std::vector<Metric2State>& stateLinks, const int32_t stateAtomId,
+ const Field& stateField, const Field& metricField);
} // namespace statsd
} // namespace os
} // namespace android
@@ -165,8 +231,8 @@
struct hash<MetricDimensionKey> {
std::size_t operator()(const MetricDimensionKey& key) const {
android::hash_t hash = hashDimension(key.getDimensionKeyInWhat());
- hash = android::JenkinsHashMix(hash, hashDimension(key.getDimensionKeyInCondition()));
+ hash = android::JenkinsHashMix(hash, hashDimension(key.getStateValuesKey()));
return android::JenkinsHashWhiten(hash);
}
};
-} // namespace std
\ No newline at end of file
+} // namespace std
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a730a0d..e7b32c5 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -20,16 +20,20 @@
#include "StatsLogProcessor.h"
#include <android-base/file.h>
+#include <cutils/multiuser.h>
#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
+#include <frameworks/base/cmds/statsd/src/experiment_ids.pb.h>
#include "android-base/stringprintf.h"
-#include "atoms_info.h"
#include "external/StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
#include "metrics/CountMetricProducer.h"
+#include "StatsService.h"
+#include "state/StateManager.h"
#include "stats_log_util.h"
#include "stats_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
#include "storage/StorageManager.h"
using namespace android;
@@ -67,9 +71,14 @@
// for ActiveConfigList
const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1;
+// for permissions checks
+constexpr const char* kPermissionDump = "android.permission.DUMP";
+constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
+
#define NS_PER_HOUR 3600 * NS_PER_SEC
#define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric"
+#define STATS_METADATA_DIR "/data/misc/stats-metadata"
// Cool down period for writing data to disk to avoid overwriting files.
#define WRITE_DATA_COOL_DOWN_SEC 5
@@ -92,6 +101,7 @@
mLargestTimestampSeen(0),
mLastTimestampSeen(0) {
mPullerManager->ForceClearPullerCache();
+ StateManager::getInstance().updateLogSources(uidMap);
}
StatsLogProcessor::~StatsLogProcessor() {
@@ -128,38 +138,22 @@
}
}
-void updateUid(Value* value, int hostUid) {
- int uid = value->int_value;
- if (uid != hostUid) {
- value->setInt(hostUid);
- }
-}
-
void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const {
- if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(event->GetTagId()) !=
- android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
- for (auto& value : *(event->getMutableValues())) {
- if (value.mField.getPosAtDepth(0) > kAttributionField) {
- break;
- }
- if (isAttributionUidField(value)) {
- const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value);
- updateUid(&value.mValue, hostUid);
+ if (std::pair<int, int> indexRange; event->hasAttributionChain(&indexRange)) {
+ vector<FieldValue>* const fieldValues = event->getMutableValues();
+ for (int i = indexRange.first; i <= indexRange.second; i++) {
+ FieldValue& fieldValue = fieldValues->at(i);
+ if (isAttributionUidField(fieldValue)) {
+ const int hostUid = mUidMap->getHostUidOrSelf(fieldValue.mValue.int_value);
+ fieldValue.mValue.setInt(hostUid);
}
}
} else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(event->GetTagId());
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto,
- // starting from 1
- if (uidField > 0 && (int)event->getValues().size() >= uidField &&
- (event->getValues())[uidField - 1].mValue.getType() == INT) {
- Value& value = (*event->getMutableValues())[uidField - 1].mValue;
- const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
- updateUid(&value, hostUid);
- } else {
- ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
- }
+ int uidFieldIndex = event->getUidFieldIndex();
+ if (uidFieldIndex != -1) {
+ Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
+ const int hostUid = mUidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
}
}
}
@@ -180,6 +174,200 @@
}
}
+void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) {
+ pid_t pid = event->GetPid();
+ uid_t uid = event->GetUid();
+ if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
+ !checkPermissionForIds(kPermissionUsage, pid, uid)) {
+ return;
+ }
+ // The Get* functions don't modify the status on success, they only write in
+ // failure statuses, so we can use one status variable for all calls then
+ // check if it is no longer NO_ERROR.
+ status_t err = NO_ERROR;
+ InstallTrainInfo trainInfo;
+ trainInfo.trainName = string(event->GetString(1 /*train name field id*/, &err));
+ trainInfo.trainVersionCode = event->GetLong(2 /*train version field id*/, &err);
+ trainInfo.requiresStaging = event->GetBool(3 /*requires staging field id*/, &err);
+ trainInfo.rollbackEnabled = event->GetBool(4 /*rollback enabled field id*/, &err);
+ trainInfo.requiresLowLatencyMonitor =
+ event->GetBool(5 /*requires low latency monitor field id*/, &err);
+ trainInfo.status = int32_t(event->GetLong(6 /*state field id*/, &err));
+ std::vector<uint8_t> trainExperimentIdBytes =
+ event->GetStorage(7 /*experiment ids field id*/, &err);
+ bool is_rollback = event->GetBool(10 /*is rollback field id*/, &err);
+
+ if (err != NO_ERROR) {
+ ALOGE("Failed to parse fields in binary push state changed log event");
+ return;
+ }
+ ExperimentIds trainExperimentIds;
+ if (!trainExperimentIds.ParseFromArray(trainExperimentIdBytes.data(),
+ trainExperimentIdBytes.size())) {
+ ALOGE("Failed to parse experimentids in binary push state changed.");
+ return;
+ }
+ trainInfo.experimentIds = {trainExperimentIds.experiment_id().begin(),
+ trainExperimentIds.experiment_id().end()};
+
+ // Update the train info on disk and get any data the logevent is missing.
+ getAndUpdateTrainInfoOnDisk(is_rollback, &trainInfo);
+
+ std::vector<uint8_t> trainExperimentIdProto;
+ writeExperimentIdsToProto(trainInfo.experimentIds, &trainExperimentIdProto);
+ int32_t userId = multiuser_get_user_id(uid);
+
+ event->updateValue(2 /*train version field id*/, trainInfo.trainVersionCode, LONG);
+ event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE);
+ event->updateValue(8 /*user id field id*/, userId, INT);
+
+ // If this event is a rollback event, then the following bits in the event
+ // are invalid and we will need to update them with the values we pulled
+ // from disk.
+ if (is_rollback) {
+ int bit = trainInfo.requiresStaging ? 1 : 0;
+ event->updateValue(3 /*requires staging field id*/, bit, INT);
+ bit = trainInfo.rollbackEnabled ? 1 : 0;
+ event->updateValue(4 /*rollback enabled field id*/, bit, INT);
+ bit = trainInfo.requiresLowLatencyMonitor ? 1 : 0;
+ event->updateValue(5 /*requires low latency monitor field id*/, bit, INT);
+ }
+}
+
+void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback,
+ InstallTrainInfo* trainInfo) {
+ // If the train name is empty, we don't know which train to attribute the
+ // event to, so return early.
+ if (trainInfo->trainName.empty()) {
+ return;
+ }
+ bool readTrainInfoSuccess = false;
+ InstallTrainInfo trainInfoOnDisk;
+ readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo->trainName, trainInfoOnDisk);
+
+ bool resetExperimentIds = false;
+ if (readTrainInfoSuccess) {
+ // Keep the old train version if we received an empty version.
+ if (trainInfo->trainVersionCode == -1) {
+ trainInfo->trainVersionCode = trainInfoOnDisk.trainVersionCode;
+ } else if (trainInfo->trainVersionCode != trainInfoOnDisk.trainVersionCode) {
+ // Reset experiment ids if we receive a new non-empty train version.
+ resetExperimentIds = true;
+ }
+
+ // Reset if we received a different experiment id.
+ if (!trainInfo->experimentIds.empty() &&
+ (trainInfoOnDisk.experimentIds.empty() ||
+ trainInfo->experimentIds.at(0) != trainInfoOnDisk.experimentIds[0])) {
+ resetExperimentIds = true;
+ }
+ }
+
+ // Find the right experiment IDs
+ if ((!resetExperimentIds || is_rollback) && readTrainInfoSuccess) {
+ trainInfo->experimentIds = trainInfoOnDisk.experimentIds;
+ }
+
+ if (!trainInfo->experimentIds.empty()) {
+ int64_t firstId = trainInfo->experimentIds.at(0);
+ auto& ids = trainInfo->experimentIds;
+ switch (trainInfo->status) {
+ case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
+ if (find(ids.begin(), ids.end(), firstId + 1) == ids.end()) {
+ ids.push_back(firstId + 1);
+ }
+ break;
+ case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
+ if (find(ids.begin(), ids.end(), firstId + 2) == ids.end()) {
+ ids.push_back(firstId + 2);
+ }
+ break;
+ case android::os::statsd::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
+ if (find(ids.begin(), ids.end(), firstId + 3) == ids.end()) {
+ ids.push_back(firstId + 3);
+ }
+ break;
+ }
+ }
+
+ // If this event is a rollback event, the following fields are invalid and
+ // need to be replaced by the fields stored to disk.
+ if (is_rollback) {
+ trainInfo->requiresStaging = trainInfoOnDisk.requiresStaging;
+ trainInfo->rollbackEnabled = trainInfoOnDisk.rollbackEnabled;
+ trainInfo->requiresLowLatencyMonitor = trainInfoOnDisk.requiresLowLatencyMonitor;
+ }
+
+ StorageManager::writeTrainInfo(*trainInfo);
+}
+
+void StatsLogProcessor::onWatchdogRollbackOccurredLocked(LogEvent* event) {
+ pid_t pid = event->GetPid();
+ uid_t uid = event->GetUid();
+ if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
+ !checkPermissionForIds(kPermissionUsage, pid, uid)) {
+ return;
+ }
+ // The Get* functions don't modify the status on success, they only write in
+ // failure statuses, so we can use one status variable for all calls then
+ // check if it is no longer NO_ERROR.
+ status_t err = NO_ERROR;
+ int32_t rollbackType = int32_t(event->GetInt(1 /*rollback type field id*/, &err));
+ string packageName = string(event->GetString(2 /*package name field id*/, &err));
+
+ if (err != NO_ERROR) {
+ ALOGE("Failed to parse fields in watchdog rollback occurred log event");
+ return;
+ }
+
+ vector<int64_t> experimentIds =
+ processWatchdogRollbackOccurred(rollbackType, packageName);
+ vector<uint8_t> experimentIdProto;
+ writeExperimentIdsToProto(experimentIds, &experimentIdProto);
+
+ event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STORAGE);
+}
+
+vector<int64_t> StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
+ const string& packageNameIn) {
+ // If the package name is empty, we can't attribute it to any train, so
+ // return early.
+ if (packageNameIn.empty()) {
+ return vector<int64_t>();
+ }
+ bool readTrainInfoSuccess = false;
+ InstallTrainInfo trainInfoOnDisk;
+ // We use the package name of the event as the train name.
+ readTrainInfoSuccess = StorageManager::readTrainInfo(packageNameIn, trainInfoOnDisk);
+
+ if (!readTrainInfoSuccess) {
+ return vector<int64_t>();
+ }
+
+ if (trainInfoOnDisk.experimentIds.empty()) {
+ return vector<int64_t>();
+ }
+
+ int64_t firstId = trainInfoOnDisk.experimentIds[0];
+ auto& ids = trainInfoOnDisk.experimentIds;
+ switch (rollbackTypeIn) {
+ case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+ if (find(ids.begin(), ids.end(), firstId + 4) == ids.end()) {
+ ids.push_back(firstId + 4);
+ }
+ StorageManager::writeTrainInfo(trainInfoOnDisk);
+ break;
+ case android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+ if (find(ids.begin(), ids.end(), firstId + 5) == ids.end()) {
+ ids.push_back(firstId + 5);
+ }
+ StorageManager::writeTrainInfo(trainInfoOnDisk);
+ break;
+ }
+
+ return trainInfoOnDisk.experimentIds;
+}
+
void StatsLogProcessor::resetConfigs() {
std::lock_guard<std::mutex> lock(mMetricsMutex);
resetConfigsLocked(getElapsedRealtimeNs());
@@ -194,26 +382,51 @@
}
void StatsLogProcessor::OnLogEvent(LogEvent* event) {
+ OnLogEvent(event, getElapsedRealtimeNs());
+}
+
+void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
+ // Tell StatsdStats about new event
+ const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs();
+ int atomId = event->GetTagId();
+ StatsdStats::getInstance().noteAtomLogged(atomId, eventElapsedTimeNs / NS_PER_SEC);
+ if (!event->isValid()) {
+ StatsdStats::getInstance().noteAtomError(atomId);
+ return;
+ }
+
+ // Hard-coded logic to update train info on disk and fill in any information
+ // this log event may be missing.
+ if (atomId == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) {
+ onBinaryPushStateChangedEventLocked(event);
+ }
+
+ // Hard-coded logic to update experiment ids on disk for certain rollback
+ // types and fill the rollback atom with experiment ids
+ if (atomId == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) {
+ onWatchdogRollbackOccurredLocked(event);
+ }
+
#ifdef VERY_VERBOSE_PRINTING
if (mPrintAllLogs) {
ALOGI("%s", event->ToString().c_str());
}
#endif
- const int64_t currentTimestampNs = event->GetElapsedTimestampNs();
-
- resetIfConfigTtlExpiredLocked(currentTimestampNs);
-
- StatsdStats::getInstance().noteAtomLogged(
- event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
+ resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
// Hard-coded logic to update the isolated uid's in the uid-map.
// The field numbers need to be currently updated by hand with atoms.proto
- if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) {
+ 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);
+
if (mMetricsManagers.empty()) {
return;
}
@@ -224,12 +437,6 @@
mLastPullerCacheClearTimeSec = curTimeSec;
}
-
- if (event->GetTagId() != android::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.
@@ -256,15 +463,16 @@
uidsWithActiveConfigsChanged.insert(uid);
StatsdStats::getInstance().noteActiveStatusChanged(pair.first, isCurActive);
}
- flushIfNecessaryLocked(event->GetElapsedTimestampNs(), pair.first, *(pair.second));
+ flushIfNecessaryLocked(pair.first, *(pair.second));
}
+ // Don't use the event timestamp for the guardrail.
for (int uid : uidsWithActiveConfigsChanged) {
// Send broadcast so that receivers can pull data.
auto lastBroadcastTime = mLastActivationBroadcastTimes.find(uid);
if (lastBroadcastTime != mLastActivationBroadcastTimes.end()) {
- if (currentTimestampNs - lastBroadcastTime->second <
- StatsdStats::kMinActivationBroadcastPeriodNs) {
+ if (elapsedRealtimeNs - lastBroadcastTime->second <
+ StatsdStats::kMinActivationBroadcastPeriodNs) {
StatsdStats::getInstance().noteActivationBroadcastGuardrailHit(uid);
VLOG("StatsD would've sent an activation broadcast but the rate limit stopped us.");
return;
@@ -274,13 +482,13 @@
if (activeConfigs != activeConfigsPerUid.end()) {
if (mSendActivationBroadcast(uid, activeConfigs->second)) {
VLOG("StatsD sent activation notice for uid %d", uid);
- mLastActivationBroadcastTimes[uid] = currentTimestampNs;
+ mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs;
}
} else {
std::vector<int64_t> emptyActiveConfigs;
if (mSendActivationBroadcast(uid, emptyActiveConfigs)) {
VLOG("StatsD sent EMPTY activation notice for uid %d", uid);
- mLastActivationBroadcastTimes[uid] = currentTimestampNs;
+ mLastActivationBroadcastTimes[uid] = elapsedRealtimeNs;
}
}
}
@@ -314,13 +522,16 @@
new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
if (newMetricsManager->isConfigValid()) {
+ newMetricsManager->init();
mUidMap->OnConfigUpdated(key);
newMetricsManager->refreshTtl(timestampNs);
mMetricsManagers[key] = newMetricsManager;
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);
}
}
@@ -537,22 +748,23 @@
}
}
-void StatsLogProcessor::flushIfNecessaryLocked(
- int64_t timestampNs, const ConfigKey& key, MetricsManager& metricsManager) {
+void StatsLogProcessor::flushIfNecessaryLocked(const ConfigKey& key,
+ MetricsManager& metricsManager) {
+ int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
auto lastCheckTime = mLastByteSizeTimes.find(key);
if (lastCheckTime != mLastByteSizeTimes.end()) {
- if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
+ if (elapsedRealtimeNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
return;
}
}
// We suspect that the byteSize() computation is expensive, so we set a rate limit.
size_t totalBytes = metricsManager.byteSize();
- mLastByteSizeTimes[key] = timestampNs;
+ mLastByteSizeTimes[key] = elapsedRealtimeNs;
bool requestDump = false;
- if (totalBytes >
- StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data.
- metricsManager.dropData(timestampNs);
+ if (totalBytes > StatsdStats::kMaxMetricsBytesPerConfig) {
+ // Too late. We need to start clearing data.
+ metricsManager.dropData(elapsedRealtimeNs);
StatsdStats::getInstance().noteDataDropped(key, totalBytes);
VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
} else if ((totalBytes > StatsdStats::kBytesPerConfigTriggerGetData) ||
@@ -567,7 +779,8 @@
// Send broadcast so that receivers can pull data.
auto lastBroadcastTime = mLastBroadcastTimes.find(key);
if (lastBroadcastTime != mLastBroadcastTimes.end()) {
- if (timestampNs - lastBroadcastTime->second < StatsdStats::kMinBroadcastPeriodNs) {
+ if (elapsedRealtimeNs - lastBroadcastTime->second <
+ StatsdStats::kMinBroadcastPeriodNs) {
VLOG("StatsD would've sent a broadcast but the rate limit stopped us.");
return;
}
@@ -575,7 +788,7 @@
if (mSendBroadcast(key)) {
mOnDiskDataConfigs.erase(key);
VLOG("StatsD triggered data fetch for %s", key.ToString().c_str());
- mLastBroadcastTimes[key] = timestampNs;
+ mLastBroadcastTimes[key] = elapsedRealtimeNs;
StatsdStats::getInstance().noteBroadcastSent(key);
}
}
@@ -627,6 +840,110 @@
proto.flush(fd.get());
}
+void StatsLogProcessor::SaveMetadataToDisk(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ // Do not write to disk if we already have in the last few seconds.
+ if (static_cast<unsigned long long> (systemElapsedTimeNs) <
+ mLastMetadataWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) {
+ ALOGI("Statsd skipping writing metadata to disk. Already wrote data in last %d seconds",
+ WRITE_DATA_COOL_DOWN_SEC);
+ return;
+ }
+ mLastMetadataWriteNs = systemElapsedTimeNs;
+
+ metadata::StatsMetadataList metadataList;
+ WriteMetadataToProtoLocked(
+ currentWallClockTimeNs, systemElapsedTimeNs, &metadataList);
+
+ string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR);
+ StorageManager::deleteFile(file_name.c_str());
+
+ if (metadataList.stats_metadata_size() == 0) {
+ // Skip the write if we have nothing to write.
+ return;
+ }
+
+ std::string data;
+ metadataList.SerializeToString(&data);
+ StorageManager::writeFile(file_name.c_str(), data.c_str(), data.size());
+}
+
+void StatsLogProcessor::WriteMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadataList* metadataList) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ WriteMetadataToProtoLocked(currentWallClockTimeNs, systemElapsedTimeNs, metadataList);
+}
+
+void StatsLogProcessor::WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadataList* metadataList) {
+ for (const auto& pair : mMetricsManagers) {
+ const sp<MetricsManager>& metricsManager = pair.second;
+ metadata::StatsMetadata* statsMetadata = metadataList->add_stats_metadata();
+ bool metadataWritten = metricsManager->writeMetadataToProto(currentWallClockTimeNs,
+ systemElapsedTimeNs, statsMetadata);
+ if (!metadataWritten) {
+ metadataList->mutable_stats_metadata()->RemoveLast();
+ }
+ }
+}
+
+void StatsLogProcessor::LoadMetadataFromDisk(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR);
+ int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC);
+ if (-1 == fd) {
+ VLOG("Attempt to read %s but failed", file_name.c_str());
+ StorageManager::deleteFile(file_name.c_str());
+ return;
+ }
+ string content;
+ if (!android::base::ReadFdToString(fd, &content)) {
+ ALOGE("Attempt to read %s but failed", file_name.c_str());
+ close(fd);
+ StorageManager::deleteFile(file_name.c_str());
+ return;
+ }
+
+ close(fd);
+
+ metadata::StatsMetadataList statsMetadataList;
+ if (!statsMetadataList.ParseFromString(content)) {
+ ALOGE("Attempt to read %s but failed; failed to metadata", file_name.c_str());
+ StorageManager::deleteFile(file_name.c_str());
+ return;
+ }
+ SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs);
+ StorageManager::deleteFile(file_name.c_str());
+}
+
+void StatsLogProcessor::SetMetadataState(const metadata::StatsMetadataList& statsMetadataList,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs);
+}
+
+void StatsLogProcessor::SetMetadataStateLocked(
+ const metadata::StatsMetadataList& statsMetadataList,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ for (const metadata::StatsMetadata& metadata : statsMetadataList.stats_metadata()) {
+ ConfigKey key(metadata.config_key().uid(), metadata.config_key().config_id());
+ auto it = mMetricsManagers.find(key);
+ if (it == mMetricsManagers.end()) {
+ ALOGE("No config found for configKey %s", key.ToString().c_str());
+ continue;
+ }
+ VLOG("Setting metadata %s", key.ToString().c_str());
+ it->second->loadMetadata(metadata, currentWallClockTimeNs, systemElapsedTimeNs);
+ }
+ VLOG("Successfully loaded %d metadata.", statsMetadataList.stats_metadata_size());
+}
+
void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
@@ -736,8 +1053,9 @@
void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk,
const int uid, const int64_t version) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- ALOGW("Received app upgrade");
- for (auto it : mMetricsManagers) {
+ VLOG("Received app upgrade");
+ StateManager::getInstance().notifyAppChanged(apk, mUidMap);
+ for (const auto& it : mMetricsManagers) {
it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version);
}
}
@@ -745,20 +1063,30 @@
void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
const int uid) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- ALOGW("Received app removed");
- for (auto it : mMetricsManagers) {
+ VLOG("Received app removed");
+ StateManager::getInstance().notifyAppChanged(apk, mUidMap);
+ for (const auto& it : mMetricsManagers) {
it.second->notifyAppRemoved(eventTimeNs, apk, uid);
}
}
void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- ALOGW("Received uid map");
- for (auto it : mMetricsManagers) {
+ VLOG("Received uid map");
+ StateManager::getInstance().updateLogSources(mUidMap);
+ for (const auto& it : mMetricsManagers) {
it.second->onUidMapReceived(eventTimeNs);
}
}
+void StatsLogProcessor::onStatsdInitCompleted(const int64_t& elapsedTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ VLOG("Received boot completed signal");
+ for (const auto& it : mMetricsManagers) {
+ it.second->onStatsdInitCompleted(elapsedTimeNs);
+ }
+}
+
void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
mOnDiskDataConfigs.insert(key);
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 0d2b33e..23f2584 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -18,11 +18,13 @@
#include <gtest/gtest_prod.h>
#include "config/ConfigListener.h"
+#include "logd/LogEvent.h"
#include "metrics/MetricsManager.h"
#include "packages/UidMap.h"
#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h"
#include <stdio.h>
#include <unordered_map>
@@ -88,6 +90,23 @@
/* Load configs containing metrics with active activations from disk. */
void LoadActiveConfigsFromDisk();
+ /* Persist metadata for configs and metrics to disk. */
+ void SaveMetadataToDisk(int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs);
+
+ /* Writes the statsd metadata for all configs and metrics to StatsMetadataList. */
+ void WriteMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadataList* metadataList);
+
+ /* Load stats metadata for configs and metrics from disk. */
+ void LoadMetadataFromDisk(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
+
+ /* Sets the metadata for all configs and metrics */
+ void SetMetadataState(const metadata::StatsMetadataList& statsMetadataList,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
+
/* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
@@ -101,6 +120,11 @@
/* Notify all MetricsManagers of uid map snapshots received */
void onUidMapReceived(const int64_t& eventTimeNs) override;
+ /* Notify all metrics managers of boot completed
+ * This will force a bucket split when the boot is finished.
+ */
+ void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
+
// Reset all configs.
void resetConfigs();
@@ -157,6 +181,8 @@
sp<AlarmMonitor> mPeriodicAlarmMonitor;
+ void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs);
+
void resetIfConfigTtlExpiredLocked(const int64_t timestampNs);
void OnConfigUpdatedLocked(
@@ -170,8 +196,17 @@
void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList,
int64_t currentTimeNs);
+ void SetMetadataStateLocked(const metadata::StatsMetadataList& statsMetadataList,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
+
+ void WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadataList* metadataList);
+
void WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
+
void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
const DumpReportReason dumpReportReason,
const DumpLatency dumpLatency);
@@ -186,8 +221,7 @@
/* Check if we should send a broadcast if approaching memory limits and if we're over, we
* actually delete the data. */
- void flushIfNecessaryLocked(int64_t timestampNs, const ConfigKey& key,
- MetricsManager& metricsManager);
+ void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager);
// Maps the isolated uid in the log event to host uid if the log event contains uid fields.
void mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const;
@@ -195,6 +229,22 @@
// Handler over the isolated uid change event.
void onIsolatedUidChangedEventLocked(const LogEvent& event);
+ // Handler over the binary push state changed event.
+ void onBinaryPushStateChangedEventLocked(LogEvent* event);
+
+ // Handler over the watchdog rollback occurred event.
+ void onWatchdogRollbackOccurredLocked(LogEvent* event);
+
+ // Updates train info on disk based on binary push state changed info and
+ // write disk info into parameters.
+ void getAndUpdateTrainInfoOnDisk(bool is_rollback, InstallTrainInfo* trainInfoIn);
+
+ // Gets experiment ids on disk for associated train and updates them
+ // depending on rollback type. Then writes them back to disk and returns
+ // them.
+ std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
+ const string& packageName);
+
// Reset all configs.
void resetConfigsLocked(const int64_t timestampNs);
// Reset the specified configs.
@@ -223,6 +273,9 @@
// Last time we wrote active metrics to disk.
int64_t mLastActiveMetricsWriteNs = 0;
+ //Last time we wrote metadata to disk.
+ int64_t mLastMetadataWriteNs = 0;
+
#ifdef VERY_VERBOSE_PRINTING
bool mPrintAllLogs = false;
#endif
@@ -231,6 +284,7 @@
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);
@@ -254,25 +308,12 @@
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
@@ -285,12 +326,31 @@
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+ FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
+ FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
+ FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
+
+ FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index d21c10c..3226482 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -28,14 +28,11 @@
#include <android-base/file.h>
#include <android-base/strings.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionController.h>
#include <cutils/multiuser.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <frameworks/base/cmds/statsd/src/uid_data.pb.h>
#include <private/android_filesystem_config.h>
-#include <statslog.h>
+#include <statslog_statsd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/system_properties.h>
@@ -48,79 +45,44 @@
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_MESSAGE;
+using Status = ::ndk::ScopedAStatus;
+
namespace android {
namespace os {
namespace statsd {
constexpr const char* kPermissionDump = "android.permission.DUMP";
-constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
-constexpr const char* kOpUsage = "android:get_usage_stats";
+constexpr const char* kPermissionRegisterPullAtom = "android.permission.REGISTER_STATS_PULL_ATOM";
#define STATS_SERVICE_DIR "/data/misc/stats-service"
// for StatsDataDumpProto
const int FIELD_ID_REPORTS_LIST = 1;
-static binder::Status ok() {
- return binder::Status::ok();
-}
-
-static binder::Status exception(uint32_t code, const std::string& msg) {
+static Status exception(int32_t code, const std::string& msg) {
ALOGE("%s (%d)", msg.c_str(), code);
- return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+ return Status::fromExceptionCodeWithMessage(code, msg.c_str());
}
-binder::Status checkUid(uid_t expectedUid) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
+static bool checkPermission(const char* permission) {
+ pid_t pid = AIBinder_getCallingPid();
+ uid_t uid = AIBinder_getCallingUid();
+ return checkPermissionForIds(permission, pid, uid);
+}
+
+Status checkUid(uid_t expectedUid) {
+ uid_t uid = AIBinder_getCallingUid();
if (uid == expectedUid || uid == AID_ROOT) {
- return ok();
+ return Status::ok();
} else {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
- }
-}
-
-binder::Status checkDumpAndUsageStats(const String16& packageName) {
- pid_t pid = IPCThreadState::self()->getCallingPid();
- uid_t uid = IPCThreadState::self()->getCallingUid();
-
- // Root, system, and shell always have access
- if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
- return ok();
- }
-
- // Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionDump));
- }
- if (!checkCallingPermission(String16(kPermissionUsage))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionUsage));
- }
-
- // Caller must also have usage stats op granted
- PermissionController pc;
- switch (pc.noteOp(String16(kOpUsage), uid, packageName)) {
- case PermissionController::MODE_ALLOWED:
- case PermissionController::MODE_DEFAULT:
- return ok();
- default:
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks app-op %s", uid, pid, kOpUsage));
+ return exception(EX_SECURITY,
+ StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
}
}
#define ENFORCE_UID(uid) { \
- binder::Status status = checkUid((uid)); \
- if (!status.isOk()) { \
- return status; \
- } \
-}
-
-#define ENFORCE_DUMP_AND_USAGE_STATS(packageName) { \
- binder::Status status = checkDumpAndUsageStats(packageName); \
+ Status status = checkUid((uid)); \
if (!status.isOk()) { \
return status; \
} \
@@ -129,13 +91,13 @@
StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue)
: mAnomalyAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
- [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+ [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
if (sc != nullptr) {
sc->setAnomalyAlarm(timeMillis);
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
}
},
- [](const sp<IStatsCompanionService>& sc) {
+ [](const shared_ptr<IStatsCompanionService>& sc) {
if (sc != nullptr) {
sc->cancelAnomalyAlarm();
StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
@@ -143,19 +105,23 @@
})),
mPeriodicAlarmMonitor(new AlarmMonitor(
MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
- [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+ [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
if (sc != nullptr) {
sc->setAlarmForSubscriberTriggering(timeMillis);
StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
}
},
- [](const sp<IStatsCompanionService>& sc) {
+ [](const shared_ptr<IStatsCompanionService>& sc) {
if (sc != nullptr) {
sc->cancelAlarmForSubscriberTriggering();
StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
}
})),
- mEventQueue(queue) {
+ mEventQueue(queue),
+ mBootCompleteTrigger({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag},
+ [this]() { mProcessor->onStatsdInitCompleted(getElapsedRealtimeNs()); }),
+ mStatsCompanionServiceDeathRecipient(
+ AIBinder_DeathRecipient_new(StatsService::statsCompanionServiceDied)) {
mUidMap = UidMap::getInstance();
mPullerManager = new StatsPullerManager();
StatsPuller::SetUidMap(mUidMap);
@@ -164,33 +130,30 @@
mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
getElapsedRealtimeNs(),
[this](const ConfigKey& key) {
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- auto receiver = mConfigManager->GetConfigReceiver(key);
- if (sc == nullptr) {
- VLOG("Could not find StatsCompanionService");
+ shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+ if (receiver == nullptr) {
+ VLOG("Could not find a broadcast receiver for %s", key.ToString().c_str());
return false;
- } else if (receiver == nullptr) {
- VLOG("Statscompanion could not find a broadcast receiver for %s",
- key.ToString().c_str());
- return false;
- } else {
- sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+ } else if (receiver->sendDataBroadcast(
+ mProcessor->getLastReportTimeNs(key)).isOk()) {
return true;
+ } else {
+ VLOG("Failed to send a broadcast for receiver %s", key.ToString().c_str());
+ return false;
}
},
[this](const int& uid, const vector<int64_t>& activeConfigs) {
- auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc == nullptr) {
- VLOG("Could not access statsCompanion");
- return false;
- } else if (receiver == nullptr) {
+ shared_ptr<IPendingIntentRef> receiver =
+ mConfigManager->GetActiveConfigsChangedReceiver(uid);
+ if (receiver == nullptr) {
VLOG("Could not find receiver for uid %d", uid);
return false;
- } else {
- sc->sendActiveConfigsChangedBroadcast(receiver, activeConfigs);
+ } else if (receiver->sendActiveConfigsChangedBroadcast(activeConfigs).isOk()) {
VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid);
return true;
+ } else {
+ VLOG("StatsService::active configs broadcast failed for uid %d" , uid);
+ return false;
}
});
@@ -241,36 +204,6 @@
}
/**
- * Implement our own because the default binder implementation isn't
- * properly handling SHELL_COMMAND_TRANSACTION.
- */
-status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- switch (code) {
- case SHELL_COMMAND_TRANSACTION: {
- int in = data.readFileDescriptor();
- int out = data.readFileDescriptor();
- int err = data.readFileDescriptor();
- int argc = data.readInt32();
- Vector<String8> args;
- for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
- args.add(String8(data.readString16()));
- }
- sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder());
- sp<IResultReceiver> resultReceiver =
- IResultReceiver::asInterface(data.readStrongBinder());
-
- err = command(in, out, err, args, resultReceiver);
- if (resultReceiver != nullptr) {
- resultReceiver->send(err);
- }
- return NO_ERROR;
- }
- default: { return BnStatsManager::onTransact(code, data, reply, flags); }
- }
-}
-
-/**
* Write data from statsd.
* Format for statsdStats: adb shell dumpsys stats --metadata [-v] [--proto]
* Format for data report: adb shell dumpsys stats [anything other than --metadata] [--proto]
@@ -279,20 +212,21 @@
* (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto")
* TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
*/
-status_t StatsService::dump(int fd, const Vector<String16>& args) {
- if (!checkCallingPermission(String16(kPermissionDump))) {
+status_t StatsService::dump(int fd, const char** args, uint32_t numArgs) {
+ if (!checkPermission(kPermissionDump)) {
return PERMISSION_DENIED;
}
- int lastArg = args.size() - 1;
+
+ int lastArg = numArgs - 1;
bool asProto = false;
- if (lastArg >= 0 && !args[lastArg].compare(String16("--proto"))) { // last argument
+ if (lastArg >= 0 && string(args[lastArg]) == "--proto") { // last argument
asProto = true;
lastArg--;
}
- if (args.size() > 0 && !args[0].compare(String16("--metadata"))) { // first argument
+ if (numArgs > 0 && string(args[0]) == "--metadata") { // first argument
// Request is to dump statsd stats.
bool verbose = false;
- if (lastArg >= 0 && !args[lastArg].compare(String16("-v"))) {
+ if (lastArg >= 0 && string(args[lastArg]) == "-v") {
verbose = true;
lastArg--;
}
@@ -333,8 +267,11 @@
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);
@@ -347,67 +284,74 @@
/**
* Implementation of the adb shell cmd stats command.
*/
-status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
- sp<IResultReceiver> resultReceiver) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
+status_t StatsService::handleShellCommand(int in, int out, int err, const char** argv,
+ uint32_t argc) {
+ uid_t uid = AIBinder_getCallingUid();
if (uid != AID_ROOT && uid != AID_SHELL) {
return PERMISSION_DENIED;
}
- const int argCount = args.size();
- if (argCount >= 1) {
+ Vector<String8> utf8Args;
+ utf8Args.setCapacity(argc);
+ for (uint32_t i = 0; i < argc; i++) {
+ utf8Args.push(String8(argv[i]));
+ }
+
+ if (argc >= 1) {
// adb shell cmd stats config ...
- if (!args[0].compare(String8("config"))) {
- return cmd_config(in, out, err, args);
+ if (!utf8Args[0].compare(String8("config"))) {
+ return cmd_config(in, out, err, utf8Args);
}
- if (!args[0].compare(String8("print-uid-map"))) {
- return cmd_print_uid_map(out, args);
+ if (!utf8Args[0].compare(String8("print-uid-map"))) {
+ return cmd_print_uid_map(out, utf8Args);
}
- if (!args[0].compare(String8("dump-report"))) {
- return cmd_dump_report(out, args);
+ if (!utf8Args[0].compare(String8("dump-report"))) {
+ return cmd_dump_report(out, utf8Args);
}
- if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
- return cmd_print_pulled_metrics(out, args);
+ if (!utf8Args[0].compare(String8("pull-source")) && argc > 1) {
+ return cmd_print_pulled_metrics(out, utf8Args);
}
- if (!args[0].compare(String8("send-broadcast"))) {
- return cmd_trigger_broadcast(out, args);
+ if (!utf8Args[0].compare(String8("send-broadcast"))) {
+ return cmd_trigger_broadcast(out, utf8Args);
}
- if (!args[0].compare(String8("print-stats"))) {
- return cmd_print_stats(out, args);
+ if (!utf8Args[0].compare(String8("print-stats"))) {
+ return cmd_print_stats(out, utf8Args);
}
- if (!args[0].compare(String8("meminfo"))) {
+ if (!utf8Args[0].compare(String8("meminfo"))) {
return cmd_dump_memory_info(out);
}
- if (!args[0].compare(String8("write-to-disk"))) {
+ if (!utf8Args[0].compare(String8("write-to-disk"))) {
return cmd_write_data_to_disk(out);
}
- if (!args[0].compare(String8("log-app-breadcrumb"))) {
- return cmd_log_app_breadcrumb(out, args);
+ if (!utf8Args[0].compare(String8("log-app-breadcrumb"))) {
+ return cmd_log_app_breadcrumb(out, utf8Args);
}
- if (!args[0].compare(String8("log-binary-push"))) {
- return cmd_log_binary_push(out, args);
+ if (!utf8Args[0].compare(String8("log-binary-push"))) {
+ return cmd_log_binary_push(out, utf8Args);
}
- if (!args[0].compare(String8("clear-puller-cache"))) {
+ if (!utf8Args[0].compare(String8("clear-puller-cache"))) {
return cmd_clear_puller_cache(out);
}
- if (!args[0].compare(String8("print-logs"))) {
- return cmd_print_logs(out, args);
+ if (!utf8Args[0].compare(String8("print-logs"))) {
+ return cmd_print_logs(out, utf8Args);
}
- if (!args[0].compare(String8("send-active-configs"))) {
- return cmd_trigger_active_config_broadcast(out, args);
+
+ if (!utf8Args[0].compare(String8("send-active-configs"))) {
+ return cmd_trigger_active_config_broadcast(out, utf8Args);
}
- if (!args[0].compare(String8("data-subscribe"))) {
+
+ if (!utf8Args[0].compare(String8("data-subscribe"))) {
{
std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
if (mShellSubscriber == nullptr) {
@@ -415,14 +359,10 @@
}
}
int timeoutSec = -1;
- if (argCount >= 2) {
- timeoutSec = atoi(args[1].c_str());
+ if (argc >= 2) {
+ timeoutSec = atoi(utf8Args[1].c_str());
}
- if (resultReceiver == nullptr) {
- ALOGI("Null resultReceiver given, no subscription will be started");
- return UNEXPECTED_NULL;
- }
- mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
+ mShellSubscriber->startNewSubscription(in, out, timeoutSec);
return NO_ERROR;
}
}
@@ -452,9 +392,11 @@
dprintf(out, " PKG Optional package name to print the uids of the package\n");
dprintf(out, "\n");
dprintf(out, "\n");
- dprintf(out, "usage: adb shell cmd stats pull-source [int] \n");
+ dprintf(out, "usage: adb shell cmd stats pull-source ATOM_TAG [PACKAGE] \n");
dprintf(out, "\n");
- dprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n");
+ dprintf(out, " Prints the output of a pulled atom\n");
+ dprintf(out, " UID The atom to pull\n");
+ dprintf(out, " PACKAGE The package to pull from. Default is AID_SYSTEM\n");
dprintf(out, "\n");
dprintf(out, "\n");
dprintf(out, "usage: adb shell cmd stats write-to-disk \n");
@@ -552,7 +494,7 @@
const int argCount = args.size();
if (argCount == 2) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
@@ -568,18 +510,17 @@
return UNKNOWN_ERROR;
}
ConfigKey key(uid, StrToInt64(name));
- auto receiver = mConfigManager->GetConfigReceiver(key);
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc == nullptr) {
- VLOG("Could not access statsCompanion");
- } else if (receiver == nullptr) {
- VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str())
- } else {
- sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+ shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+ if (receiver == nullptr) {
+ VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str());
+ return UNKNOWN_ERROR;
+ } else if (receiver->sendDataBroadcast(mProcessor->getLastReportTimeNs(key)).isOk()) {
VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
args[2].c_str());
+ } else {
+ VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(), args[2].c_str());
+ return UNKNOWN_ERROR;
}
-
return NO_ERROR;
}
@@ -589,7 +530,7 @@
vector<int64_t> configIds;
if (argCount == 1) {
// Automatically pick the uid and send a broadcast that has no active configs.
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
mProcessor->GetActiveConfigs(uid, configIds);
} else {
int curArg = 1;
@@ -603,7 +544,7 @@
}
curArg++;
} else {
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
}
if (curArg == argCount || args[curArg] != "--configs") {
VLOG("Reached end of args, or specify configs not set. Sending actual active configs,");
@@ -623,15 +564,15 @@
}
}
}
- auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
- sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc == nullptr) {
- VLOG("Could not access statsCompanion");
- } else if (receiver == nullptr) {
+ shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
+ if (receiver == nullptr) {
VLOG("Could not find receiver for uid %d", uid);
- } else {
- sc->sendActiveConfigsChangedBroadcast(receiver, configIds);
+ return UNKNOWN_ERROR;
+ } else if (receiver->sendActiveConfigsChangedBroadcast(configIds).isOk()) {
VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid);
+ } else {
+ VLOG("StatsService::trigger active configs changed broadcast failed for uid %d", uid);
+ return UNKNOWN_ERROR;
}
return NO_ERROR;
}
@@ -646,7 +587,7 @@
if (argCount == 3) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
name.assign(args[2].c_str(), args[2].size());
good = true;
} else if (argCount == 4) {
@@ -729,7 +670,7 @@
}
if (argCount == 2) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
name.assign(args[1].c_str(), args[1].size());
good = true;
} else if (argCount == 3) {
@@ -821,7 +762,7 @@
const int argCount = args.size();
if (argCount == 3) {
// Automatically pick the UID
- uid = IPCThreadState::self()->getCallingUid();
+ uid = AIBinder_getCallingUid();
label = atoi(args[1].c_str());
state = atoi(args[2].c_str());
good = true;
@@ -837,7 +778,8 @@
}
if (good) {
dprintf(out, "Logging AppBreadcrumbReported(%d, %d, %d) to statslog.\n", uid, label, state);
- android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED, uid, label, state);
+ android::os::statsd::util::stats_write(
+ android::os::statsd::util::APP_BREADCRUMB_REPORTED, uid, label, state);
} else {
print_cmd_help(out);
return UNKNOWN_ERROR;
@@ -852,18 +794,8 @@
dprintf(out, "Incorrect number of argument supplied\n");
return UNKNOWN_ERROR;
}
- android::String16 trainName = android::String16(args[1].c_str());
+ string trainName = string(args[1].c_str());
int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10);
- int options = 0;
- if (args[3] == "1") {
- options = options | IStatsManager::FLAG_REQUIRE_STAGING;
- }
- if (args[4] == "1") {
- options = options | IStatsManager::FLAG_ROLLBACK_ENABLED;
- }
- if (args[5] == "1") {
- options = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
- }
int32_t state = atoi(args[6].c_str());
vector<int64_t> experimentIds;
if (argCount == 8) {
@@ -874,14 +806,30 @@
}
}
dprintf(out, "Logging BinaryPushStateChanged\n");
- sendBinaryPushStateChangedAtom(trainName, trainVersion, options, state, experimentIds);
+ vector<uint8_t> experimentIdBytes;
+ writeExperimentIdsToProto(experimentIds, &experimentIdBytes);
+ LogEvent event(trainName, trainVersion, args[3], args[4], args[5], state, experimentIdBytes, 0);
+ mProcessor->OnLogEvent(&event);
return NO_ERROR;
}
status_t StatsService::cmd_print_pulled_metrics(int out, const Vector<String8>& args) {
int s = atoi(args[1].c_str());
- vector<shared_ptr<LogEvent> > stats;
- if (mPullerManager->Pull(s, &stats)) {
+ vector<int32_t> uids;
+ if (args.size() > 2) {
+ string package = string(args[2].c_str());
+ auto it = UidMap::sAidToUidMapping.find(package);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ uids.push_back(it->second);
+ } else {
+ set<int32_t> uids_set = mUidMap->getAppUid(package);
+ uids.insert(uids.end(), uids_set.begin(), uids_set.end());
+ }
+ } else {
+ uids.push_back(AID_SYSTEM);
+ }
+ vector<shared_ptr<LogEvent>> stats;
+ if (mPullerManager->Pull(s, uids, getElapsedRealtimeNs(), &stats)) {
for (const auto& it : stats) {
dprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
}
@@ -905,10 +853,9 @@
}
status_t StatsService::cmd_clear_puller_cache(int out) {
- IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
- ipc->getCallingPid(), ipc->getCallingUid());
- if (checkCallingPermission(String16(kPermissionDump))) {
+ AIBinder_getCallingPid(), AIBinder_getCallingUid());
+ if (checkPermission(kPermissionDump)) {
int cleared = mPullerManager->ForceClearPullerCache();
dprintf(out, "Puller removed %d cached data!\n", cleared);
return NO_ERROR;
@@ -918,10 +865,9 @@
}
status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
- IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(),
- ipc->getCallingUid());
- if (checkCallingPermission(String16(kPermissionDump))) {
+ VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ if (checkPermission(kPermissionDump)) {
bool enabled = true;
if (args.size() >= 2) {
enabled = atoi(args[1].c_str()) != 0;
@@ -952,24 +898,24 @@
}
uid = goodUid;
- int32_t callingUid = IPCThreadState::self()->getCallingUid();
+ int32_t callingUid = AIBinder_getCallingUid();
return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
|| (callingUid == goodUid) // Anyone can 'impersonate' themselves.
|| (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
}
-Status StatsService::informAllUidData(const ParcelFileDescriptor& fd) {
+Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) {
ENFORCE_UID(AID_SYSTEM);
// Read stream into buffer.
string buffer;
if (!android::base::ReadFdToString(fd.get(), &buffer)) {
- return exception(Status::EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
+ return exception(EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
}
// Parse buffer.
UidData uidData;
if (!uidData.ParseFromString(buffer)) {
- return exception(Status::EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
+ return exception(EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
}
vector<String16> versionStrings;
@@ -1000,24 +946,31 @@
packageNames,
installers);
+ mBootCompleteTrigger.markComplete(kUidMapReceivedTag);
VLOG("StatsService::informAllUidData UidData proto parsed successfully.");
return Status::ok();
}
-Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version,
- const String16& version_string, const String16& installer) {
+Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t version,
+ const string& versionString, const string& installer) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informOnePackage was called");
- mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version, version_string, installer);
+ String16 utf16App = String16(app.c_str());
+ String16 utf16VersionString = String16(versionString.c_str());
+ String16 utf16Installer = String16(installer.c_str());
+
+ mUidMap->updateApp(getElapsedRealtimeNs(), utf16App, uid, version, utf16VersionString,
+ utf16Installer);
return Status::ok();
}
-Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) {
+Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) {
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::informOnePackageRemoved was called");
- mUidMap->removeApp(getElapsedRealtimeNs(), app, uid);
+ String16 utf16App = String16(app.c_str());
+ mUidMap->removeApp(getElapsedRealtimeNs(), utf16App, uid);
mConfigManager->RemoveConfigs(uid);
return Status::ok();
}
@@ -1077,11 +1030,12 @@
VLOG("StatsService::informDeviceShutdown");
mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST);
mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs());
+ mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs());
return Status::ok();
}
void StatsService::sayHiToStatsCompanion() {
- sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+ shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion != nullptr) {
VLOG("Telling statsCompanion that statsd is ready");
statsCompanion->statsdReady();
@@ -1094,24 +1048,32 @@
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::statsCompanionReady was called");
- sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+ shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion == nullptr) {
- return Status::fromExceptionCode(
- Status::EX_NULL_POINTER,
- "statscompanion unavailable despite it contacting statsd!");
+ return exception(EX_NULL_POINTER,
+ "StatsCompanion unavailable despite it contacting statsd.");
}
VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
- IInterface::asBinder(statsCompanion)->linkToDeath(this);
+ AIBinder_linkToDeath(statsCompanion->asBinder().get(),
+ mStatsCompanionServiceDeathRecipient.get(), this);
mPullerManager->SetStatsCompanionService(statsCompanion);
mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
- SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
+ return Status::ok();
+}
+
+Status StatsService::bootCompleted() {
+ ENFORCE_UID(AID_SYSTEM);
+
+ VLOG("StatsService::bootCompleted was called");
+ mBootCompleteTrigger.markComplete(kBootCompleteTag);
return Status::ok();
}
void StatsService::Startup() {
mConfigManager->Startup();
mProcessor->LoadActiveConfigsFromDisk();
+ mProcessor->LoadMetadataFromDisk(getWallClockNs(), getElapsedRealtimeNs());
}
void StatsService::Terminate() {
@@ -1119,6 +1081,7 @@
if (mProcessor != nullptr) {
mProcessor->WriteDataToDisk(TERMINATION_SIGNAL_RECEIVED, FAST);
mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs());
+ mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs());
}
}
@@ -1130,12 +1093,11 @@
}
}
-Status StatsService::getData(int64_t key, const String16& packageName, vector<uint8_t>* output) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
- ConfigKey configKey(ipc->getCallingUid(), key);
+ VLOG("StatsService::getData with Uid %i", callingUid);
+ ConfigKey configKey(callingUid, key);
// The dump latency does not matter here since we do not include the current bucket, we do not
// need to pull any new data anyhow.
mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
@@ -1143,27 +1105,21 @@
return Status::ok();
}
-Status StatsService::getMetadata(const String16& packageName, vector<uint8_t>* output) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::getMetadata(vector<uint8_t>* output) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(),
- ipc->getCallingUid());
StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
return Status::ok();
}
Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
+ if (addConfigurationChecked(callingUid, key, config)) {
return Status::ok();
} else {
- ALOGE("Could not parse malformatted StatsdConfig");
- return Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT,
- "config does not correspond to a StatsdConfig proto");
+ return exception(EX_ILLEGAL_ARGUMENT, "Could not parse malformatted StatsdConfig.");
}
}
@@ -1179,23 +1135,21 @@
return true;
}
-Status StatsService::removeDataFetchOperation(int64_t key, const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
-
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), key);
+Status StatsService::removeDataFetchOperation(int64_t key,
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
+ ConfigKey configKey(callingUid, key);
mConfigManager->RemoveConfigReceiver(configKey);
return Status::ok();
}
Status StatsService::setDataFetchOperation(int64_t key,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), key);
- mConfigManager->SetConfigReceiver(configKey, intentSender);
+ ConfigKey configKey(callingUid, key);
+ mConfigManager->SetConfigReceiver(configKey, pir);
if (StorageManager::hasConfigMetricsReport(configKey)) {
VLOG("StatsService::setDataFetchOperation marking configKey %s to dump reports on disk",
configKey.ToString().c_str());
@@ -1204,301 +1158,170 @@
return Status::ok();
}
-Status StatsService::setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender,
- const String16& packageName,
+Status StatsService::setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid,
vector<int64_t>* output) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- int uid = ipc->getCallingUid();
- mConfigManager->SetActiveConfigsChangedReceiver(uid, intentSender);
+ mConfigManager->SetActiveConfigsChangedReceiver(callingUid, pir);
if (output != nullptr) {
- mProcessor->GetActiveConfigs(uid, *output);
+ mProcessor->GetActiveConfigs(callingUid, *output);
} else {
ALOGW("StatsService::setActiveConfigsChanged output was nullptr");
}
return Status::ok();
}
-Status StatsService::removeActiveConfigsChangedOperation(const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- mConfigManager->RemoveActiveConfigsChangedReceiver(ipc->getCallingUid());
+ mConfigManager->RemoveActiveConfigsChangedReceiver(callingUid);
return Status::ok();
}
-Status StatsService::removeConfiguration(int64_t key, const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::removeConfiguration(int64_t key, const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), key);
+ ConfigKey configKey(callingUid, key);
mConfigManager->RemoveConfig(configKey);
- SubscriberReporter::getInstance().removeConfig(configKey);
return Status::ok();
}
Status StatsService::setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::setBroadcastSubscriber called.");
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), configId);
+ ConfigKey configKey(callingUid, configId);
SubscriberReporter::getInstance()
- .setBroadcastSubscriber(configKey, subscriberId, intentSender);
+ .setBroadcastSubscriber(configKey, subscriberId, pir);
return Status::ok();
}
Status StatsService::unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+ const int32_t callingUid) {
+ ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::unsetBroadcastSubscriber called.");
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), configId);
+ ConfigKey configKey(callingUid, configId);
SubscriberReporter::getInstance()
.unsetBroadcastSubscriber(configKey, subscriberId);
return Status::ok();
}
-Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) {
- // Permission check not necessary as it's meant for applications to write to
- // statsd.
- android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
- IPCThreadState::self()->getCallingUid(), label,
- state);
+Status StatsService::allPullersFromBootRegistered() {
+ ENFORCE_UID(AID_SYSTEM);
+
+ VLOG("StatsService::allPullersFromBootRegistered was called");
+ mBootCompleteTrigger.markComplete(kAllPullersRegisteredTag);
return Status::ok();
}
-Status StatsService::registerPullerCallback(int32_t atomTag,
- const sp<android::os::IStatsPullerCallback>& pullerCallback,
- const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
-
- VLOG("StatsService::registerPullerCallback called.");
- mPullerManager->RegisterPullerCallback(atomTag, pullerCallback);
+Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownMillis,
+ int64_t timeoutMillis,
+ const std::vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& pullerCallback) {
+ ENFORCE_UID(AID_SYSTEM);
+ VLOG("StatsService::registerPullAtomCallback called.");
+ mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis),
+ MillisToNano(timeoutMillis), additiveFields,
+ pullerCallback);
return Status::ok();
}
-Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& packageName) {
- ENFORCE_DUMP_AND_USAGE_STATS(packageName);
-
- VLOG("StatsService::unregisterPullerCallback called.");
- mPullerManager->UnregisterPullerCallback(atomTag);
+Status StatsService::registerNativePullAtomCallback(
+ int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
+ const std::vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& pullerCallback) {
+ if (!checkPermission(kPermissionRegisterPullAtom)) {
+ return exception(
+ EX_SECURITY,
+ StringPrintf("Uid %d does not have the %s permission when registering atom %d",
+ AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag));
+ }
+ VLOG("StatsService::registerNativePullAtomCallback called.");
+ int32_t uid = AIBinder_getCallingUid();
+ mPullerManager->RegisterPullAtomCallback(uid, atomTag, MillisToNano(coolDownMillis),
+ MillisToNano(timeoutMillis), additiveFields,
+ pullerCallback);
return Status::ok();
}
-Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn,
- const int64_t trainVersionCodeIn,
- const int options,
- const int32_t state,
- const std::vector<int64_t>& experimentIdsIn) {
- // Note: We skip the usage stats op check here since we do not have a package name.
- // This is ok since we are overloading the usage_stats permission.
- // This method only sends data, it does not receive it.
- pid_t pid = IPCThreadState::self()->getCallingPid();
- uid_t uid = IPCThreadState::self()->getCallingUid();
- // Root, system, and shell always have access
- if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
- // Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionDump));
- }
- if (!checkCallingPermission(String16(kPermissionUsage))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionUsage));
- }
- }
-
- bool readTrainInfoSuccess = false;
- InstallTrainInfo trainInfoOnDisk;
- readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
-
- bool resetExperimentIds = false;
- int64_t trainVersionCode = trainVersionCodeIn;
- std::string trainNameUtf8 = std::string(String8(trainNameIn).string());
- if (readTrainInfoSuccess) {
- // Keep the old train version if we received an empty version.
- if (trainVersionCodeIn == -1) {
- trainVersionCode = trainInfoOnDisk.trainVersionCode;
- } else if (trainVersionCodeIn != trainInfoOnDisk.trainVersionCode) {
- // Reset experiment ids if we receive a new non-empty train version.
- resetExperimentIds = true;
- }
-
- // Keep the old train name if we received an empty train name.
- if (trainNameUtf8.size() == 0) {
- trainNameUtf8 = trainInfoOnDisk.trainName;
- } else if (trainNameUtf8 != trainInfoOnDisk.trainName) {
- // Reset experiment ids if we received a new valid train name.
- resetExperimentIds = true;
- }
-
- // Reset if we received a different experiment id.
- if (!experimentIdsIn.empty() &&
- (trainInfoOnDisk.experimentIds.empty() ||
- experimentIdsIn[0] != trainInfoOnDisk.experimentIds[0])) {
- resetExperimentIds = true;
- }
- }
-
- // Find the right experiment IDs
- std::vector<int64_t> experimentIds;
- if (resetExperimentIds || !readTrainInfoSuccess) {
- experimentIds = experimentIdsIn;
- } else {
- experimentIds = trainInfoOnDisk.experimentIds;
- }
-
- if (!experimentIds.empty()) {
- int64_t firstId = experimentIds[0];
- switch (state) {
- case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
- experimentIds.push_back(firstId + 1);
- break;
- case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
- experimentIds.push_back(firstId + 2);
- break;
- case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
- experimentIds.push_back(firstId + 3);
- break;
- }
- }
-
- // Flatten the experiment IDs to proto
- vector<uint8_t> experimentIdsProtoBuffer;
- writeExperimentIdsToProto(experimentIds, &experimentIdsProtoBuffer);
- StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIds);
-
- userid_t userId = multiuser_get_user_id(uid);
- bool requiresStaging = options & IStatsManager::FLAG_REQUIRE_STAGING;
- bool rollbackEnabled = options & IStatsManager::FLAG_ROLLBACK_ENABLED;
- bool requiresLowLatencyMonitor = options & IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR;
- LogEvent event(trainNameUtf8, trainVersionCode, requiresStaging, rollbackEnabled,
- requiresLowLatencyMonitor, state, experimentIdsProtoBuffer, userId);
- mProcessor->OnLogEvent(&event);
+Status StatsService::unregisterPullAtomCallback(int32_t uid, int32_t atomTag) {
+ ENFORCE_UID(AID_SYSTEM);
+ VLOG("StatsService::unregisterPullAtomCallback called.");
+ mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
return Status::ok();
}
-Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackTypeIn,
- const android::String16& packageNameIn,
- const int64_t packageVersionCodeIn,
- const int32_t rollbackReasonIn,
- const android::String16&
- failingPackageNameIn) {
- // Note: We skip the usage stats op check here since we do not have a package name.
- // This is ok since we are overloading the usage_stats permission.
- // This method only sends data, it does not receive it.
- pid_t pid = IPCThreadState::self()->getCallingPid();
- uid_t uid = IPCThreadState::self()->getCallingUid();
- // Root, system, and shell always have access
- if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
- // Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionDump));
- }
- if (!checkCallingPermission(String16(kPermissionUsage))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
- kPermissionUsage));
- }
+Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
+ if (!checkPermission(kPermissionRegisterPullAtom)) {
+ return exception(
+ EX_SECURITY,
+ StringPrintf("Uid %d does not have the %s permission when unregistering atom %d",
+ AIBinder_getCallingUid(), kPermissionRegisterPullAtom, atomTag));
}
-
- android::util::stats_write(android::util::WATCHDOG_ROLLBACK_OCCURRED,
- rollbackTypeIn, String8(packageNameIn).string(), packageVersionCodeIn,
- rollbackReasonIn, String8(failingPackageNameIn).string());
-
- // Fast return to save disk read.
- if (rollbackTypeIn != android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS
- && rollbackTypeIn !=
- android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE) {
- return Status::ok();
- }
-
- bool readTrainInfoSuccess = false;
- InstallTrainInfo trainInfoOnDisk;
- readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
-
- if (!readTrainInfoSuccess) {
- return Status::ok();
- }
- std::vector<int64_t> experimentIds = trainInfoOnDisk.experimentIds;
- if (experimentIds.empty()) {
- return Status::ok();
- }
- switch (rollbackTypeIn) {
- case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
- experimentIds.push_back(experimentIds[0] + 4);
- break;
- case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
- experimentIds.push_back(experimentIds[0] + 5);
- break;
- }
- StorageManager::writeTrainInfo(trainInfoOnDisk.trainVersionCode, trainInfoOnDisk.trainName,
- trainInfoOnDisk.status, experimentIds);
+ VLOG("StatsService::unregisterNativePullAtomCallback called.");
+ int32_t uid = AIBinder_getCallingUid();
+ mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
return Status::ok();
}
Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
- uid_t uid = IPCThreadState::self()->getCallingUid();
-
- // Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d lacks permission %s", uid, kPermissionDump));
- }
- if (!checkCallingPermission(String16(kPermissionUsage))) {
- return exception(binder::Status::EX_SECURITY,
- StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage));
- }
+ ENFORCE_UID(AID_SYSTEM);
// TODO: add verifier permission
+ experimentIdsOut->clear();
// Read the latest train info
- InstallTrainInfo trainInfo;
- if (!StorageManager::readTrainInfo(trainInfo)) {
+ vector<InstallTrainInfo> trainInfoList = StorageManager::readAllTrainInfo();
+ if (trainInfoList.empty()) {
// No train info means no experiment IDs, return an empty list
- experimentIdsOut->clear();
return Status::ok();
}
// Copy the experiment IDs to the out vector
- experimentIdsOut->assign(trainInfo.experimentIds.begin(), trainInfo.experimentIds.end());
+ for (InstallTrainInfo& trainInfo : trainInfoList) {
+ experimentIdsOut->insert(experimentIdsOut->end(),
+ trainInfo.experimentIds.begin(),
+ trainInfo.experimentIds.end());
+ }
return Status::ok();
}
-void StatsService::binderDied(const wp <IBinder>& who) {
+void StatsService::statsCompanionServiceDied(void* cookie) {
+ auto thiz = static_cast<StatsService*>(cookie);
+ thiz->statsCompanionServiceDiedImpl();
+}
+
+void StatsService::statsCompanionServiceDiedImpl() {
ALOGW("statscompanion service died");
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
if (mProcessor != nullptr) {
ALOGW("Reset statsd upon system server restarts.");
int64_t systemServerRestartNs = getElapsedRealtimeNs();
- ProtoOutputStream proto;
+ ProtoOutputStream activeConfigsProto;
mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs,
- STATSCOMPANION_DIED, &proto);
-
+ STATSCOMPANION_DIED, &activeConfigsProto);
+ metadata::StatsMetadataList metadataList;
+ mProcessor->WriteMetadataToProto(getWallClockNs(),
+ systemServerRestartNs, &metadataList);
mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST);
mProcessor->resetConfigs();
std::string serializedActiveConfigs;
- if (proto.serializeToString(&serializedActiveConfigs)) {
+ if (activeConfigsProto.serializeToString(&serializedActiveConfigs)) {
ActiveConfigList activeConfigs;
if (activeConfigs.ParseFromString(serializedActiveConfigs)) {
mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs);
}
}
+ mProcessor->SetMetadataState(metadataList, getWallClockNs(), systemServerRestartNs);
}
mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
- SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
mPullerManager->SetStatsCompanionService(nullptr);
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 991a205..324ffbd 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -17,7 +17,14 @@
#ifndef STATS_SERVICE_H
#define STATS_SERVICE_H
+#include <aidl/android/os/BnStatsd.h>
+#include <aidl/android/os/IPendingIntentRef.h>
+#include <aidl/android/os/IPullAtomCallback.h>
#include <gtest/gtest_prod.h>
+#include <utils/Looper.h>
+
+#include <mutex>
+
#include "StatsLogProcessor.h"
#include "anomaly/AlarmMonitor.h"
#include "config/ConfigManager.h"
@@ -26,32 +33,25 @@
#include "packages/UidMap.h"
#include "shell/ShellSubscriber.h"
#include "statscompanion_util.h"
-
-#include <android/frameworks/stats/1.0/IStats.h>
-#include <android/frameworks/stats/1.0/types.h>
-#include <android/os/BnStatsManager.h>
-#include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsManager.h>
-#include <binder/IResultReceiver.h>
-#include <binder/ParcelFileDescriptor.h>
-#include <utils/Looper.h>
-
-#include <mutex>
+#include "utils/MultiConditionTrigger.h"
using namespace android;
-using namespace android::binder;
-using namespace android::frameworks::stats::V1_0;
using namespace android::os;
using namespace std;
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnStatsd;
+using aidl::android::os::IPendingIntentRef;
+using aidl::android::os::IPullAtomCallback;
+using ::ndk::ScopedAIBinder_DeathRecipient;
+using ::ndk::ScopedFileDescriptor;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
-using android::hardware::Return;
-
-class StatsService : public BnStatsManager,
- public IBinder::DeathRecipient {
+class StatsService : public BnStatsd {
public:
StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue);
virtual ~StatsService();
@@ -59,21 +59,21 @@
/** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
- virtual status_t dump(int fd, const Vector<String16>& args);
- virtual status_t command(int inFd, int outFd, int err, Vector<String8>& args,
- sp<IResultReceiver> resultReceiver);
+ virtual status_t dump(int fd, const char** args, uint32_t numArgs) override;
+ virtual status_t handleShellCommand(int in, int out, int err, const char** argv,
+ uint32_t argc) override;
virtual Status systemRunning();
virtual Status statsCompanionReady();
+ virtual Status bootCompleted();
virtual Status informAnomalyAlarmFired();
virtual Status informPollAlarmFired();
virtual Status informAlarmForSubscriberTriggeringFired();
- virtual Status informAllUidData(const ParcelFileDescriptor& fd);
- virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version,
- const String16& version_string, const String16& installer);
- virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
+ virtual Status informAllUidData(const ScopedFileDescriptor& fd);
+ virtual Status informOnePackage(const string& app, int32_t uid, int64_t version,
+ const string& versionString, const string& installer);
+ virtual Status informOnePackageRemoved(const string& app, int32_t uid);
virtual Status informDeviceShutdown();
/**
@@ -95,15 +95,14 @@
* Binder call for clients to request data for this configuration key.
*/
virtual Status getData(int64_t key,
- const String16& packageName,
+ const int32_t callingUid,
vector<uint8_t>* output) override;
/**
* Binder call for clients to get metadata across all configs in statsd.
*/
- virtual Status getMetadata(const String16& packageName,
- vector<uint8_t>* output) override;
+ virtual Status getMetadata(vector<uint8_t>* output) override;
/**
@@ -112,103 +111,92 @@
*/
virtual Status addConfiguration(int64_t key,
const vector<uint8_t>& config,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/**
* Binder call to let clients register the data fetch operation for a configuration.
*/
virtual Status setDataFetchOperation(int64_t key,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) override;
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid) override;
/**
* Binder call to remove the data fetch operation for the specified config key.
*/
virtual Status removeDataFetchOperation(int64_t key,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/**
* Binder call to let clients register the active configs changed operation.
*/
- virtual Status setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender,
- const String16& packageName,
+ virtual Status setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid,
vector<int64_t>* output) override;
/**
* Binder call to remove the active configs changed operation for the specified package..
*/
- virtual Status removeActiveConfigsChangedOperation(const String16& packageName) override;
+ virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override;
/**
* Binder call to allow clients to remove the specified configuration.
*/
virtual Status removeConfiguration(int64_t key,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/**
- * Binder call to associate the given config's subscriberId with the given intentSender.
- * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
+ * Binder call to associate the given config's subscriberId with the given pendingIntentRef.
*/
virtual Status setBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender,
- const String16& packageName) override;
+ const shared_ptr<IPendingIntentRef>& pir,
+ const int32_t callingUid) override;
/**
- * Binder call to unassociate the given config's subscriberId with any intentSender.
+ * Binder call to unassociate the given config's subscriberId with any pendingIntentRef.
*/
virtual Status unsetBroadcastSubscriber(int64_t configId,
int64_t subscriberId,
- const String16& packageName) override;
+ const int32_t callingUid) override;
/** Inform statsCompanion that statsd is ready. */
virtual void sayHiToStatsCompanion();
/**
- * Binder call to get AppBreadcrumbReported atom.
+ * Binder call to notify statsd that all pullers from boot have been registered.
*/
- virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override;
+ virtual Status allPullersFromBootRegistered();
/**
- * Binder call to register a callback function for a vendor pulled atom.
- * Note: this atom must NOT have uid as a field.
+ * Binder call to register a callback function for a pulled atom.
*/
- virtual Status registerPullerCallback(int32_t atomTag,
- const sp<android::os::IStatsPullerCallback>& pullerCallback,
- const String16& packageName) override;
+ virtual Status registerPullAtomCallback(
+ int32_t uid, int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
+ const std::vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& pullerCallback) override;
/**
- * Binder call to unregister any existing callback function for a vendor pulled atom.
+ * Binder call to register a callback function for a pulled atom.
*/
- virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override;
+ virtual Status registerNativePullAtomCallback(
+ int32_t atomTag, int64_t coolDownMillis, int64_t timeoutMillis,
+ const std::vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& pullerCallback) override;
/**
- * Binder call to log BinaryPushStateChanged atom.
+ * Binder call to unregister any existing callback for the given uid and atom.
*/
- virtual Status sendBinaryPushStateChangedAtom(
- const android::String16& trainNameIn,
- const int64_t trainVersionCodeIn,
- const int options,
- const int32_t state,
- const std::vector<int64_t>& experimentIdsIn) override;
+ virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override;
/**
- * Binder call to log WatchdogRollbackOccurred atom.
+ * Binder call to unregister any existing callback for the given atom and calling uid.
*/
- virtual Status sendWatchdogRollbackOccurredAtom(
- const int32_t rollbackTypeIn,
- const android::String16& packageNameIn,
- const int64_t packageVersionCodeIn,
- const int32_t rollbackReasonIn,
- const android::String16& failingPackageNameIn) override;
+ virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
/**
* Binder call to get registered experiment IDs.
*/
virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
- /** IBinder::DeathRecipient */
- virtual void binderDied(const wp<IBinder>& who) override;
-
private:
/**
* Load system properties at init.
@@ -340,6 +328,17 @@
void set_config(int uid, const string& name, const StatsdConfig& config);
/**
+ * Death recipient callback that is called when StatsCompanionService dies.
+ * The cookie is a pointer to a StatsService object.
+ */
+ static void statsCompanionServiceDied(void* cookie);
+
+ /**
+ * Implementation of statsCompanionServiceDied.
+ */
+ void statsCompanionServiceDiedImpl();
+
+ /**
* Tracks the uid <--> package name mapping.
*/
sp<UidMap> mUidMap;
@@ -382,17 +381,27 @@
mutable mutex mShellSubscriberMutex;
std::shared_ptr<LogEventQueue> mEventQueue;
+ MultiConditionTrigger mBootCompleteTrigger;
+ static const inline string kBootCompleteTag = "BOOT_COMPLETE";
+ static const inline string kUidMapReceivedTag = "UID_MAP";
+ static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED";
+
+ ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient;
+
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
+ FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit);
+ FRIEND_TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket);
+ FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket);
FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket);
};
diff --git a/cmds/statsd/src/annotations.h b/cmds/statsd/src/annotations.h
new file mode 100644
index 0000000..cf7f543
--- /dev/null
+++ b/cmds/statsd/src/annotations.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const uint8_t ANNOTATION_ID_IS_UID = 1;
+const uint8_t ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2;
+const uint8_t ANNOTATION_ID_PRIMARY_FIELD = 3;
+const uint8_t ANNOTATION_ID_EXCLUSIVE_STATE = 4;
+const uint8_t ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5;
+const uint8_t ANNOTATION_ID_TRIGGER_STATE_RESET = 7;
+const uint8_t ANNOTATION_ID_STATE_NESTED = 8;
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
index bc36dad..b632d04 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -26,17 +26,19 @@
AlarmMonitor::AlarmMonitor(
uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
- const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
- const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm)
- : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
+ const std::function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>& updateAlarm,
+ const std::function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm)
+ : mRegisteredAlarmTimeSec(0),
+ mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
mUpdateAlarm(updateAlarm),
mCancelAlarm(cancelAlarm) {}
AlarmMonitor::~AlarmMonitor() {}
-void AlarmMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
+void AlarmMonitor::setStatsCompanionService(
+ shared_ptr<IStatsCompanionService> statsCompanionService) {
std::lock_guard<std::mutex> lock(mLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+ shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
if (statsCompanionService == nullptr) {
VLOG("Erasing link to statsCompanionService");
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
index 219695e..5c34e38 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -18,7 +18,7 @@
#include "anomaly/indexed_priority_queue.h"
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <unordered_set>
@@ -26,7 +26,9 @@
using namespace android;
-using android::os::IStatsCompanionService;
+using aidl::android::os::IStatsCompanionService;
+using std::function;
+using std::shared_ptr;
using std::unordered_set;
namespace android {
@@ -64,8 +66,9 @@
* alarm.
*/
AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
- const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
- const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm);
+ const function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>&
+ updateAlarm,
+ const function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm);
~AlarmMonitor();
/**
@@ -74,7 +77,7 @@
* If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
* update IStatsCompanionService (until such time as it is set non-null).
*/
- void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+ void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
/**
* Adds the given alarm (reference) to the queue.
@@ -123,7 +126,7 @@
/**
* Binder interface for communicating with StatsCompanionService.
*/
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+ shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
/**
* Amount by which the soonest projected alarm must differ from
@@ -147,10 +150,10 @@
int64_t secToMs(uint32_t timeSec);
// Callback function to update the alarm via StatsCompanionService.
- std::function<void(const sp<IStatsCompanionService>, int64_t)> mUpdateAlarm;
+ std::function<void(const shared_ptr<IStatsCompanionService>, int64_t)> mUpdateAlarm;
// Callback function to cancel the alarm via StatsCompanionService.
- std::function<void(const sp<IStatsCompanionService>)> mCancelAlarm;
+ std::function<void(const shared_ptr<IStatsCompanionService>)> mCancelAlarm;
};
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index a21327b..6d9beb8 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -23,7 +23,6 @@
#include "stats_util.h"
#include "storage/StorageManager.h"
-#include <statslog.h>
#include <time.h>
namespace android {
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h
index 2fb3e3b..2da4a186 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.h
+++ b/cmds/statsd/src/anomaly/AlarmTracker.h
@@ -22,12 +22,9 @@
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alarm
-#include <android/os/IStatsCompanionService.h>
#include <stdlib.h>
#include <utils/RefBase.h>
-using android::os::IStatsCompanionService;
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 7ace44e..619752c 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -18,14 +18,16 @@
#include "Log.h"
#include "AnomalyTracker.h"
-#include "subscriber_util.h"
#include "external/Perfetto.h"
#include "guardrail/StatsdStats.h"
+#include "metadata_util.h"
+#include "stats_log_util.h"
+#include "subscriber_util.h"
#include "subscriber/IncidentdReporter.h"
#include "subscriber/SubscriberReporter.h"
#include <inttypes.h>
-#include <statslog.h>
+#include <statslog_statsd.h>
#include <time.h>
namespace android {
@@ -235,8 +237,8 @@
StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
// TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
- android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
- mConfigKey.GetId(), mAlert.id());
+ util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(),
+ mConfigKey.GetId(), mAlert.id());
}
void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
@@ -262,6 +264,58 @@
triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
}
+bool AnomalyTracker::writeAlertMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::AlertMetadata* alertMetadata) {
+ bool metadataWritten = false;
+
+ if (mRefractoryPeriodEndsSec.empty()) {
+ return false;
+ }
+
+ for (const auto& it: mRefractoryPeriodEndsSec) {
+ // Do not write the timestamp to disk if it has already expired
+ if (it.second < systemElapsedTimeNs / NS_PER_SEC) {
+ continue;
+ }
+
+ metadataWritten = true;
+ if (alertMetadata->alert_dim_keyed_data_size() == 0) {
+ alertMetadata->set_alert_id(mAlert.id());
+ }
+
+ metadata::AlertDimensionKeyedData* keyedData = alertMetadata->add_alert_dim_keyed_data();
+ // We convert and write the refractory_end_sec to wall clock time because we do not know
+ // when statsd will start again.
+ int32_t refractoryEndWallClockSec = (int32_t) ((currentWallClockTimeNs / NS_PER_SEC) +
+ (it.second - systemElapsedTimeNs / NS_PER_SEC));
+
+ keyedData->set_last_refractory_ends_sec(refractoryEndWallClockSec);
+ writeMetricDimensionKeyToMetadataDimensionKey(
+ it.first, keyedData->mutable_dimension_key());
+ }
+
+ return metadataWritten;
+}
+
+void AnomalyTracker::loadAlertMetadata(
+ const metadata::AlertMetadata& alertMetadata,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ for (const metadata::AlertDimensionKeyedData& keyedData :
+ alertMetadata.alert_dim_keyed_data()) {
+ if ((uint64_t) keyedData.last_refractory_ends_sec() < currentWallClockTimeNs / NS_PER_SEC) {
+ // Do not update the timestamp if it has already expired.
+ continue;
+ }
+ MetricDimensionKey metricKey = loadMetricDimensionKeyFromProto(
+ keyedData.dimension_key());
+ int32_t refractoryPeriodEndsSec = (int32_t) keyedData.last_refractory_ends_sec() -
+ currentWallClockTimeNs / NS_PER_SEC + systemElapsedTimeNs / NS_PER_SEC;
+ mRefractoryPeriodEndsSec[metricKey] = refractoryPeriodEndsSec;
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 794ee98..bf36a3b 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -24,6 +24,7 @@
#include "AlarmMonitor.h"
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata
#include "stats_util.h" // HashableDimensionKey and DimToValMap
namespace android {
@@ -112,6 +113,17 @@
return; // The base AnomalyTracker class doesn't have alarms.
}
+ // Writes metadata of the alert (refractory_period_end_sec) to AlertMetadata.
+ // Returns true if at least one element is written to alertMetadata.
+ bool writeAlertMetadataToProto(
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs, metadata::AlertMetadata* alertMetadata);
+
+ void loadAlertMetadata(
+ const metadata::AlertMetadata& alertMetadata,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
+
protected:
// For testing only.
// Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 16c936c..ff5717e 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -23,45 +23,72 @@
import "google/protobuf/descriptor.proto";
-enum StateField {
- // Default value for fields that are not primary or exclusive state.
- STATE_FIELD_UNSET = 0;
- // Fields that represent the key that the state belongs to.
- PRIMARY = 1;
- // The field that represents the state. It's an exclusive state.
- EXCLUSIVE = 2;
-}
-
-// Used to annotate an atom that reprsents a state change. A state change atom must have exactly ONE
-// exclusive state field, and any number of primary key fields.
-// For example,
-// message UidProcessStateChanged {
-// optional int32 uid = 1 [(state_field_option).option = PRIMARY];
-// optional android.app.ProcessStateEnum state = 2 [(state_field_option).option = EXCLUSIVE];
+// Used to annotate an atom that represents a state change. A state change atom must have exactly
+// ONE exclusive state field, and any number of primary key fields. For example, message
+// UidProcessStateChanged {
+// optional int32 uid = 1 [(state_field_option).primary_field = true];
+// optional android.app.ProcessStateEnum state =
+// 2 [(state_field_option).exclusive_state = true];
// }
-// Each of this UidProcessStateChanged atom represents a state change for a specific uid.
+// Each UidProcessStateChanged atom event represents a state change for a specific uid.
// A new state automatically overrides the previous state.
//
-// If the atom has 2 or more primary fields, it means the combination of the primary fields are
-// the primary key.
+// If the atom has 2 or more primary fields, it means the combination of the
+// primary fields are the primary key.
// For example:
// message ThreadStateChanged {
-// optional int32 pid = 1 [(state_field_option).option = PRIMARY];
-// optional int32 tid = 2 [(state_field_option).option = PRIMARY];
-// optional int32 state = 3 [(state_field_option).option = EXCLUSIVE];
+// optional int32 pid = 1 [(state_field_option).primary_field = true];
+// optional int32 tid = 2 [(state_field_option).primary_field = true];
+// optional int32 state = 3 [(state_field_option).exclusive_state = true];
// }
//
// Sometimes, there is no primary key field, when the state is GLOBAL.
// For example,
-//
// message ScreenStateChanged {
-// optional android.view.DisplayStateEnum state = 1 [(state_field_option).option = EXCLUSIVE];
+// optional android.view.DisplayStateEnum state =
+// 1 [(state_field_option).exclusive_state = true];
// }
//
-// Only fields of primary types can be annotated. AttributionNode cannot be primary keys (and they
-// usually are not).
+// For state atoms with attribution chain, sometimes the primary key is the first uid in the chain.
+// For example:
+// message AudioStateChanged {
+// repeated AttributionNode attribution_node = 1
+// [(stateFieldOption).primary_field_first_uid = true];
+//
+// enum State {
+// OFF = 0;
+// ON = 1;
+// // RESET indicates all audio stopped. Used when it (re)starts (e.g. after it crashes).
+// RESET = 2;
+// }
+// optional State state = 2 [(stateFieldOption).exclusive_state = true];
+// }
message StateAtomFieldOption {
- optional StateField option = 1 [default = STATE_FIELD_UNSET];
+ // Fields that represent the key that the state belongs to.
+ // Used on simple proto fields. Do not use on attribution chains.
+ optional bool primary_field = 1 [default = false];
+
+ // The field that represents the state. It's an exclusive state.
+ optional bool exclusive_state = 2 [default = false];
+
+ // Used on an attribution chain field to indicate that the first uid is the
+ // primary field.
+ optional bool primary_field_first_uid = 3 [default = false];
+
+ // Note: We cannot annotate directly on the enums because many enums are imported from other
+ // proto files in the platform. proto-lite cc library does not support annotations unfortunately
+
+ // Knowing the default state value allows state trackers to remove entries that become the
+ // default state. If there is no default value specified, the default value is unknown, and all
+ // states will be tracked in memory.
+ optional int32 default_state_value = 4;
+
+ // A reset state signals all states go to default value. For example, BLE reset means all active
+ // BLE scans are to be turned off.
+ optional int32 trigger_state_reset_value = 5;
+
+ // If the state change needs to count nesting.
+ optional bool nested = 6 [default = true];
}
// Used to generate StatsLog.write APIs.
@@ -83,7 +110,7 @@
optional LogMode log_mode = 50002 [default = MODE_AUTOMATIC];
- optional bool allow_from_any_uid = 50003 [default = false];
+ repeated string module = 50004;
- optional string log_from_module = 50004;
-}
\ No newline at end of file
+ optional bool truncate_timestamp = 50005 [default = false];
+}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b8de3f0..ab1d3cb 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -24,6 +24,8 @@
import "frameworks/base/core/proto/android/app/enums.proto";
import "frameworks/base/core/proto/android/app/job/enums.proto";
import "frameworks/base/core/proto/android/app/settings_enums.proto";
+import "frameworks/base/core/proto/android/app/media_output_enum.proto";
+import "frameworks/base/core/proto/android/app/tvsettings_enums.proto";
import "frameworks/base/core/proto/android/bluetooth/a2dp/enums.proto";
import "frameworks/base/core/proto/android/bluetooth/enums.proto";
import "frameworks/base/core/proto/android/bluetooth/hci/enums.proto";
@@ -46,18 +48,22 @@
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto";
+import "frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto";
import "frameworks/base/core/proto/android/stats/enums.proto";
import "frameworks/base/core/proto/android/stats/intelligence/enums.proto";
import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
import "frameworks/base/core/proto/android/stats/location/location_enums.proto";
import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto";
-import "frameworks/base/core/proto/android/stats/otaupdate/updateengine_enums.proto";
+import "frameworks/base/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto";
import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
+import "frameworks/base/core/proto/android/stats/sysui/notification_enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
import "frameworks/base/core/proto/android/wifi/enums.proto";
+import "frameworks/base/core/proto/android/stats/textclassifier/textclassifier_enums.proto";
+import "frameworks/base/core/proto/android/stats/otaupdate/updateengine_enums.proto";
/**
* The primary atom class. This message defines all of the available
@@ -76,241 +82,258 @@
// Pushed atoms start at 2.
oneof pushed {
// For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
- BleScanStateChanged ble_scan_state_changed = 2 [(log_from_module) = "bluetooth"];
- ProcessStateChanged process_state_changed = 3;
- BleScanResultReceived ble_scan_result_received = 4 [(log_from_module) = "bluetooth"];
- SensorStateChanged sensor_state_changed = 5;
- GpsScanStateChanged gps_scan_state_changed = 6;
- SyncStateChanged sync_state_changed = 7;
- ScheduledJobStateChanged scheduled_job_state_changed = 8;
- ScreenBrightnessChanged screen_brightness_changed = 9;
- WakelockStateChanged wakelock_state_changed = 10;
- LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11;
- MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12 [(log_from_module) = "framework"];
- WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13 [(log_from_module) = "framework"];
- ActivityManagerSleepStateChanged activity_manager_sleep_state_changed = 14;
- MemoryFactorStateChanged memory_factor_state_changed = 15;
- ExcessiveCpuUsageReported excessive_cpu_usage_reported = 16;
- CachedKillReported cached_kill_reported = 17;
- ProcessMemoryStatReported process_memory_stat_reported = 18;
- LauncherUIChanged launcher_event = 19;
- BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
- DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
- DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
- AudioStateChanged audio_state_changed = 23;
- MediaCodecStateChanged media_codec_state_changed = 24;
- CameraStateChanged camera_state_changed = 25;
- FlashlightStateChanged flashlight_state_changed = 26;
- UidProcessStateChanged uid_process_state_changed = 27;
- ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28;
- ScreenStateChanged screen_state_changed = 29;
- BatteryLevelChanged battery_level_changed = 30;
- ChargingStateChanged charging_state_changed = 31;
- PluggedStateChanged plugged_state_changed = 32;
- InteractiveStateChanged interactive_state_changed = 33;
+ BleScanStateChanged ble_scan_state_changed = 2
+ [(module) = "bluetooth", (module) = "statsdtest"];
+ ProcessStateChanged process_state_changed = 3 [(module) = "framework"];
+ BleScanResultReceived ble_scan_result_received = 4 [(module) = "bluetooth"];
+ SensorStateChanged sensor_state_changed =
+ 5 [(module) = "framework", (module) = "statsdtest"];
+ GpsScanStateChanged gps_scan_state_changed = 6 [(module) = "framework"];
+ SyncStateChanged sync_state_changed = 7 [(module) = "framework", (module) = "statsdtest"];
+ ScheduledJobStateChanged scheduled_job_state_changed =
+ 8 [(module) = "framework", (module) = "statsdtest"];
+ ScreenBrightnessChanged screen_brightness_changed =
+ 9 [(module) = "framework", (module) = "statsdtest"];
+ WakelockStateChanged wakelock_state_changed =
+ 10 [(module) = "framework", (module) = "statsdtest"];
+ LongPartialWakelockStateChanged long_partial_wakelock_state_changed =
+ 11 [(module) = "framework"];
+ MobileRadioPowerStateChanged mobile_radio_power_state_changed =
+ 12 [(module) = "framework", (truncate_timestamp) = true];
+ WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13 [(module) = "framework"];
+ ActivityManagerSleepStateChanged activity_manager_sleep_state_changed =
+ 14 [(module) = "framework"];
+ MemoryFactorStateChanged memory_factor_state_changed = 15 [(module) = "framework"];
+ ExcessiveCpuUsageReported excessive_cpu_usage_reported = 16 [(module) = "framework"];
+ CachedKillReported cached_kill_reported = 17 [(module) = "framework"];
+ ProcessMemoryStatReported process_memory_stat_reported = 18 [(module) = "framework"];
+ LauncherUIChanged launcher_event = 19 [(module) = "sysui"];
+ BatterySaverModeStateChanged battery_saver_mode_state_changed =
+ 20 [(module) = "framework", (module) = "statsdtest"];
+ DeviceIdleModeStateChanged device_idle_mode_state_changed = 21 [(module) = "framework"];
+ DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22 [(module) = "framework"];
+ AudioStateChanged audio_state_changed =
+ 23 [(module) = "framework", (truncate_timestamp) = true];
+ MediaCodecStateChanged media_codec_state_changed = 24 [(module) = "framework"];
+ CameraStateChanged camera_state_changed = 25 [(module) = "framework"];
+ FlashlightStateChanged flashlight_state_changed = 26 [(module) = "framework"];
+ UidProcessStateChanged uid_process_state_changed =
+ 27 [(module) = "framework", (module) = "statsdtest"];
+ ProcessLifeCycleStateChanged process_life_cycle_state_changed =
+ 28 [(module) = "framework", (module) = "statsdtest"];
+ ScreenStateChanged screen_state_changed =
+ 29 [(module) = "framework", (module) = "statsdtest"];
+ BatteryLevelChanged battery_level_changed =
+ 30 [(module) = "framework", (module) = "statsdtest"];
+ ChargingStateChanged charging_state_changed = 31 [(module) = "framework"];
+ PluggedStateChanged plugged_state_changed = 32
+ [(module) = "framework", (module) = "statsdtest"];
+ InteractiveStateChanged interactive_state_changed = 33 [(module) = "framework"];
TouchEventReported touch_event_reported = 34;
- WakeupAlarmOccurred wakeup_alarm_occurred = 35;
- KernelWakeupReported kernel_wakeup_reported = 36;
- WifiLockStateChanged wifi_lock_state_changed = 37;
- WifiSignalStrengthChanged wifi_signal_strength_changed = 38;
- WifiScanStateChanged wifi_scan_state_changed = 39;
- PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
- SettingChanged setting_changed = 41;
- ActivityForegroundStateChanged activity_foreground_state_changed = 42;
- IsolatedUidChanged isolated_uid_changed = 43;
- PacketWakeupOccurred packet_wakeup_occurred = 44 [(log_from_module) = "framework"];
- WallClockTimeShifted wall_clock_time_shifted = 45;
- AnomalyDetected anomaly_detected = 46;
- AppBreadcrumbReported app_breadcrumb_reported = 47 [(allow_from_any_uid) = true];
- AppStartOccurred app_start_occurred = 48;
- AppStartCanceled app_start_canceled = 49;
- AppStartFullyDrawn app_start_fully_drawn = 50;
- LmkKillOccurred lmk_kill_occurred = 51 [(log_from_module) = "lmkd"];
- PictureInPictureStateChanged picture_in_picture_state_changed = 52;
- WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53;
- LmkStateChanged lmk_state_changed = 54 [(log_from_module) = "lmkd"];
- AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
- ShutdownSequenceReported shutdown_sequence_reported = 56;
+ WakeupAlarmOccurred wakeup_alarm_occurred = 35 [(module) = "framework"];
+ KernelWakeupReported kernel_wakeup_reported = 36 [(module) = "framework"];
+ WifiLockStateChanged wifi_lock_state_changed = 37 [(module) = "wifi"];
+ WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(module) = "wifi"];
+ WifiScanStateChanged wifi_scan_state_changed = 39 [(module) = "wifi"];
+ PhoneSignalStrengthChanged phone_signal_strength_changed =
+ 40 [(module) = "framework", (truncate_timestamp) = true];
+ SettingChanged setting_changed = 41 [(module) = "framework"];
+ ActivityForegroundStateChanged activity_foreground_state_changed =
+ 42 [(module) = "framework", (module) = "statsdtest"];
+ IsolatedUidChanged isolated_uid_changed =
+ 43 [(module) = "framework", (module) = "statsd", (module) = "statsdtest"];
+ 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 [(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"];
+ LmkKillOccurred lmk_kill_occurred = 51 [(module) = "lmkd"];
+ PictureInPictureStateChanged picture_in_picture_state_changed = 52 [(module) = "framework"];
+ WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(module) = "wifi"];
+ LmkStateChanged lmk_state_changed = 54 [(module) = "lmkd"];
+ 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];
- OverlayStateChanged overlay_state_changed = 59;
- ForegroundServiceStateChanged foreground_service_state_changed = 60;
- CallStateChanged call_state_changed = 61;
- KeyguardStateChanged keyguard_state_changed = 62;
- KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63;
- KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64;
- AppDied app_died = 65;
- ResourceConfigurationChanged resource_configuration_changed = 66;
- BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
+ DaveyOccurred davey_occurred = 58 [(module) = "statsd"];
+ OverlayStateChanged overlay_state_changed =
+ 59 [(module) = "framework", (module) = "statsdtest"];
+ ForegroundServiceStateChanged foreground_service_state_changed
+ = 60 [(module) = "framework"];
+ CallStateChanged call_state_changed =
+ 61 [(module) = "telecom", (truncate_timestamp) = true];
+ KeyguardStateChanged keyguard_state_changed = 62 [(module) = "sysui"];
+ KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63 [(module) = "sysui"];
+ KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64 [(module) = "sysui"];
+ AppDied app_died = 65 [(module) = "framework"];
+ ResourceConfigurationChanged resource_configuration_changed = 66 [(module) = "framework"];
+ BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67 [(module) = "framework"];
BluetoothConnectionStateChanged bluetooth_connection_state_changed =
- 68 [(log_from_module) = "bluetooth"];
- GpsSignalQualityChanged gps_signal_quality_changed = 69;
- UsbConnectorStateChanged usb_connector_state_changed = 70;
+ 68 [(module) = "bluetooth"];
+ GpsSignalQualityChanged gps_signal_quality_changed = 69 [(module) = "framework"];
+ UsbConnectorStateChanged usb_connector_state_changed = 70 [(module) = "framework"];
SpeakerImpedanceReported speaker_impedance_reported = 71;
HardwareFailed hardware_failed = 72;
PhysicalDropDetected physical_drop_detected = 73;
ChargeCyclesReported charge_cycles_reported = 74;
- MobileConnectionStateChanged mobile_connection_state_changed =
- 75 [(log_from_module) = "telephony"];
- MobileRadioTechnologyChanged mobile_radio_technology_changed =
- 76 [(log_from_module) = "telephony"];
- UsbDeviceAttached usb_device_attached = 77;
- AppCrashOccurred app_crash_occurred = 78;
- ANROccurred anr_occurred = 79;
- WTFOccurred wtf_occurred = 80;
- LowMemReported low_mem_reported = 81;
+ MobileConnectionStateChanged mobile_connection_state_changed = 75 [(module) = "telephony"];
+ MobileRadioTechnologyChanged mobile_radio_technology_changed = 76 [(module) = "telephony"];
+ UsbDeviceAttached usb_device_attached = 77 [(module) = "framework"];
+ AppCrashOccurred app_crash_occurred = 78 [(module) = "framework", (module) = "statsdtest"];
+ ANROccurred anr_occurred = 79 [(module) = "framework"];
+ 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];
- VibratorStateChanged vibrator_state_changed = 84;
- DeferredJobStatsReported deferred_job_stats_reported = 85;
+ 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;
- BiometricAuthenticated biometric_authenticated = 88;
- BiometricErrorOccurred biometric_error_occurred = 89;
- // Atom number 90 is available for use.
+ 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", (module) = "sysui"];
BatteryHealthSnapshot battery_health_snapshot = 91;
SlowIo slow_io = 92;
BatteryCausedShutdown battery_caused_shutdown = 93;
- PhoneServiceStateChanged phone_service_state_changed = 94;
- PhoneStateChanged phone_state_changed = 95;
+ PhoneServiceStateChanged phone_service_state_changed = 94 [(module) = "framework"];
+ PhoneStateChanged phone_state_changed = 95 [(module) = "framework"];
UserRestrictionChanged user_restriction_changed = 96;
- SettingsUIChanged settings_ui_changed = 97;
- ConnectivityStateChanged connectivity_state_changed = 98;
+ SettingsUIChanged settings_ui_changed = 97 [(module) = "settings"];
+ ConnectivityStateChanged connectivity_state_changed = 98 [(module) = "framework"];
// TODO: service state change is very noisy shortly after boot, as well
// as at other transitions - coming out of doze, device plugged in, etc.
// Consider removing this if it becomes a problem
- ServiceStateChanged service_state_changed = 99;
- ServiceLaunchReported service_launch_reported = 100;
- FlagFlipUpdateOccurred flag_flip_update_occurred = 101;
- BinaryPushStateChanged binary_push_state_changed = 102;
- DevicePolicyEvent device_policy_event = 103;
- DocsUIFileOperationCanceledReported docs_ui_file_op_canceled =
- 104 [(log_from_module) = "docsui"];
- DocsUIFileOperationCopyMoveModeReported
- docs_ui_file_op_copy_move_mode_reported =
- 105 [(log_from_module) = "docsui"];
- DocsUIFileOperationFailureReported docs_ui_file_op_failure =
- 106 [(log_from_module) = "docsui"];
- DocsUIFileOperationReported docs_ui_provider_file_op =
- 107 [(log_from_module) = "docsui"];
- DocsUIInvalidScopedAccessRequestReported
- docs_ui_invalid_scoped_access_request =
- 108 [(log_from_module) = "docsui"];
- DocsUILaunchReported docs_ui_launch_reported =
- 109 [(log_from_module) = "docsui"];
- DocsUIRootVisitedReported docs_ui_root_visited =
- 110 [(log_from_module) = "docsui"];
- DocsUIStartupMsReported docs_ui_startup_ms =
- 111 [(log_from_module) = "docsui"];
- DocsUIUserActionReported docs_ui_user_action_reported =
- 112 [(log_from_module) = "docsui"];
- WifiEnabledStateChanged wifi_enabled_state_changed = 113;
- WifiRunningStateChanged wifi_running_state_changed = 114;
- AppCompacted app_compacted = 115;
- NetworkDnsEventReported network_dns_event_reported = 116 [(log_from_module) = "resolv"];
+ ServiceStateChanged service_state_changed = 99 [(module) = "framework"];
+ ServiceLaunchReported service_launch_reported = 100 [(module) = "framework"];
+ FlagFlipUpdateOccurred flag_flip_update_occurred = 101 [(module) = "framework"];
+ BinaryPushStateChanged binary_push_state_changed = 102 [(module) = "statsd"];
+ DevicePolicyEvent device_policy_event = 103 [(module) = "framework"];
+ DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104 [(module) = "docsui"];
+ DocsUIFileOperationCopyMoveModeReported docs_ui_file_op_copy_move_mode_reported =
+ 105 [(module) = "docsui"];
+ DocsUIFileOperationFailureReported docs_ui_file_op_failure = 106 [(module) = "docsui"];
+ DocsUIFileOperationReported docs_ui_provider_file_op = 107 [(module) = "docsui"];
+ DocsUIInvalidScopedAccessRequestReported docs_ui_invalid_scoped_access_request =
+ 108 [(module) = "docsui"];
+ DocsUILaunchReported docs_ui_launch_reported = 109 [(module) = "docsui"];
+ DocsUIRootVisitedReported docs_ui_root_visited = 110 [(module) = "docsui"];
+ DocsUIStartupMsReported docs_ui_startup_ms = 111 [(module) = "docsui"];
+ DocsUIUserActionReported docs_ui_user_action_reported = 112 [(module) = "docsui"];
+ WifiEnabledStateChanged wifi_enabled_state_changed = 113 [(module) = "framework"];
+ WifiRunningStateChanged wifi_running_state_changed = 114
+ [(module) = "framework", deprecated = true];
+ AppCompacted app_compacted = 115 [(module) = "framework"];
+ NetworkDnsEventReported network_dns_event_reported = 116 [(module) = "resolv"];
DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported =
- 117 [(log_from_module) = "docsui"];
- DocsUIPickResultReported docs_ui_pick_result_reported =
- 118 [(log_from_module) = "docsui"];
- DocsUISearchModeReported docs_ui_search_mode_reported =
- 119 [(log_from_module) = "docsui"];
- DocsUISearchTypeReported docs_ui_search_type_reported =
- 120 [(log_from_module) = "docsui"];
- DataStallEvent data_stall_event = 121 [(log_from_module) = "network_stack"];
- RescuePartyResetReported rescue_party_reset_reported = 122;
- SignedConfigReported signed_config_reported = 123;
- GnssNiEventReported gnss_ni_event_reported = 124;
+ 117 [(module) = "docsui"];
+ DocsUIPickResultReported docs_ui_pick_result_reported = 118 [(module) = "docsui"];
+ DocsUISearchModeReported docs_ui_search_mode_reported = 119 [(module) = "docsui"];
+ DocsUISearchTypeReported docs_ui_search_type_reported = 120 [(module) = "docsui"];
+ DataStallEvent data_stall_event = 121 [(module) = "network_stack"];
+ RescuePartyResetReported rescue_party_reset_reported = 122 [(module) = "framework"];
+ SignedConfigReported signed_config_reported = 123 [(module) = "framework"];
+ GnssNiEventReported gnss_ni_event_reported = 124 [(module) = "framework"];
BluetoothLinkLayerConnectionEvent bluetooth_link_layer_connection_event =
- 125 [(log_from_module) = "bluetooth"];
+ 125 [(module) = "bluetooth"];
BluetoothAclConnectionStateChanged bluetooth_acl_connection_state_changed =
- 126 [(log_from_module) = "bluetooth"];
+ 126 [(module) = "bluetooth"];
BluetoothScoConnectionStateChanged bluetooth_sco_connection_state_changed =
- 127 [(log_from_module) = "bluetooth"];
- AppDowngraded app_downgraded = 128;
+ 127 [(module) = "bluetooth"];
+ AppDowngraded app_downgraded = 128 [(module) = "framework"];
AppOptimizedAfterDowngraded app_optimized_after_downgraded = 129;
- LowStorageStateChanged low_storage_state_changed = 130;
- GnssNfwNotificationReported gnss_nfw_notification_reported = 131;
- GnssConfigurationReported gnss_configuration_reported = 132;
+ LowStorageStateChanged low_storage_state_changed = 130 [(module) = "framework"];
+ GnssNfwNotificationReported gnss_nfw_notification_reported = 131 [(module) = "framework"];
+ GnssConfigurationReported gnss_configuration_reported = 132 [(module) = "framework"];
UsbPortOverheatEvent usb_port_overheat_event_reported = 133;
- NfcErrorOccurred nfc_error_occurred = 134;
- NfcStateChanged nfc_state_changed = 135;
- NfcBeamOccurred nfc_beam_occurred = 136;
- NfcCardemulationOccurred nfc_cardemulation_occurred = 137;
- NfcTagOccurred nfc_tag_occurred = 138;
- NfcHceTransactionOccurred nfc_hce_transaction_occurred = 139;
- SeStateChanged se_state_changed = 140;
- SeOmapiReported se_omapi_reported = 141;
- BroadcastDispatchLatencyReported broadcast_dispatch_latency_reported = 142;
- AttentionManagerServiceResultReported attention_manager_service_result_reported = 143;
- AdbConnectionChanged adb_connection_changed = 144;
+ NfcErrorOccurred nfc_error_occurred = 134 [(module) = "nfc"];
+ NfcStateChanged nfc_state_changed = 135 [(module) = "nfc"];
+ NfcBeamOccurred nfc_beam_occurred = 136 [(module) = "nfc"];
+ NfcCardemulationOccurred nfc_cardemulation_occurred = 137 [(module) = "nfc"];
+ NfcTagOccurred nfc_tag_occurred = 138 [(module) = "nfc"];
+ NfcHceTransactionOccurred nfc_hce_transaction_occurred = 139 [(module) = "nfc"];
+ SeStateChanged se_state_changed = 140 [(module) = "secure_element"];
+ SeOmapiReported se_omapi_reported = 141 [(module) = "secure_element"];
+ BroadcastDispatchLatencyReported broadcast_dispatch_latency_reported =
+ 142 [(module) = "framework"];
+ AttentionManagerServiceResultReported attention_manager_service_result_reported =
+ 143 [(module) = "framework"];
+ AdbConnectionChanged adb_connection_changed = 144 [(module) = "framework"];
SpeechDspStatReported speech_dsp_stat_reported = 145;
- UsbContaminantReported usb_contaminant_reported = 146;
- WatchdogRollbackOccurred watchdog_rollback_occurred = 147;
- BiometricSystemHealthIssueDetected biometric_system_health_issue_detected = 148;
- BubbleUIChanged bubble_ui_changed = 149;
- ScheduledJobConstraintChanged scheduled_job_constraint_changed = 150;
+ UsbContaminantReported usb_contaminant_reported = 146 [(module) = "framework"];
+ WatchdogRollbackOccurred watchdog_rollback_occurred =
+ 147 [(module) = "framework", (module) = "statsd"];
+ BiometricSystemHealthIssueDetected biometric_system_health_issue_detected =
+ 148 [(module) = "framework"];
+ BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"];
+ ScheduledJobConstraintChanged scheduled_job_constraint_changed =
+ 150 [(module) = "framework"];
BluetoothActiveDeviceChanged bluetooth_active_device_changed =
- 151 [(log_from_module) = "bluetooth"];
+ 151 [(module) = "bluetooth"];
BluetoothA2dpPlaybackStateChanged bluetooth_a2dp_playback_state_changed =
- 152 [(log_from_module) = "bluetooth"];
+ 152 [(module) = "bluetooth"];
BluetoothA2dpCodecConfigChanged bluetooth_a2dp_codec_config_changed =
- 153 [(log_from_module) = "bluetooth"];
+ 153 [(module) = "bluetooth"];
BluetoothA2dpCodecCapabilityChanged bluetooth_a2dp_codec_capability_changed =
- 154 [(log_from_module) = "bluetooth"];
+ 154 [(module) = "bluetooth"];
BluetoothA2dpAudioUnderrunReported bluetooth_a2dp_audio_underrun_reported =
- 155 [(log_from_module) = "bluetooth"];
+ 155 [(module) = "bluetooth"];
BluetoothA2dpAudioOverrunReported bluetooth_a2dp_audio_overrun_reported =
- 156 [(log_from_module) = "bluetooth"];
+ 156 [(module) = "bluetooth"];
BluetoothDeviceRssiReported bluetooth_device_rssi_reported =
- 157 [(log_from_module) = "bluetooth"];
+ 157 [(module) = "bluetooth"];
BluetoothDeviceFailedContactCounterReported
- bluetooth_device_failed_contact_counter_reported = 158 [(log_from_module) = "bluetooth"];
+ bluetooth_device_failed_contact_counter_reported = 158 [(module) = "bluetooth"];
BluetoothDeviceTxPowerLevelReported bluetooth_device_tx_power_level_reported =
- 159 [(log_from_module) = "bluetooth"];
+ 159 [(module) = "bluetooth"];
BluetoothHciTimeoutReported bluetooth_hci_timeout_reported =
- 160 [(log_from_module) = "bluetooth"];
+ 160 [(module) = "bluetooth"];
BluetoothQualityReportReported bluetooth_quality_report_reported =
- 161 [(log_from_module) = "bluetooth"];
+ 161 [(module) = "bluetooth"];
BluetoothDeviceInfoReported bluetooth_device_info_reported =
- 162 [(log_from_module) = "bluetooth"];
+ 162 [(module) = "bluetooth"];
BluetoothRemoteVersionInfoReported bluetooth_remote_version_info_reported =
- 163 [(log_from_module) = "bluetooth"];
+ 163 [(module) = "bluetooth"];
BluetoothSdpAttributeReported bluetooth_sdp_attribute_reported =
- 164 [(log_from_module) = "bluetooth"];
+ 164 [(module) = "bluetooth"];
BluetoothBondStateChanged bluetooth_bond_state_changed =
- 165 [(log_from_module) = "bluetooth"];
+ 165 [(module) = "bluetooth"];
BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported =
- 166 [(log_from_module) = "bluetooth"];
+ 166 [(module) = "bluetooth"];
BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported =
- 167 [(log_from_module) = "bluetooth"];
- ScreenTimeoutExtensionReported screen_timeout_extension_reported = 168;
- ProcessStartTime process_start_time = 169;
+ 167 [(module) = "bluetooth"];
+ ScreenTimeoutExtensionReported screen_timeout_extension_reported =
+ 168 [(module) = "framework"];
+ ProcessStartTime process_start_time = 169 [(module) = "framework"];
PermissionGrantRequestResultReported permission_grant_request_result_reported =
- 170 [(log_from_module) = "permissioncontroller"];
+ 170 [(module) = "permissioncontroller"];
BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
- DeviceIdentifierAccessDenied device_identifier_access_denied = 172;
- BubbleDeveloperErrorReported bubble_developer_error_reported = 173;
- AssistGestureStageReported assist_gesture_stage_reported = 174;
- AssistGestureFeedbackReported assist_gesture_feedback_reported = 175;
- AssistGestureProgressReported assist_gesture_progress_reported = 176;
- TouchGestureClassified touch_gesture_classified = 177;
- HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true];
- StyleUIChanged style_ui_changed = 179 [(log_from_module) = "style"];
+ DeviceIdentifierAccessDenied device_identifier_access_denied =
+ 172 [(module) = "telephony_common"];
+ BubbleDeveloperErrorReported bubble_developer_error_reported = 173 [(module) = "framework"];
+ AssistGestureStageReported assist_gesture_stage_reported = 174 [(module) = "sysui"];
+ 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 [(module) = "framework"];
+ StyleUIChanged style_ui_changed = 179 [(module) = "sysui"];
PrivacyIndicatorsInteracted privacy_indicators_interacted =
- 180 [(log_from_module) = "permissioncontroller"];
- AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181;
- NetworkStackReported network_stack_reported = 182 [(log_from_module) = "network_stack"];
- AppMovedStorageReported app_moved_storage_reported = 183;
- BiometricEnrolled biometric_enrolled = 184;
- SystemServerWatchdogOccurred system_server_watchdog_occurred = 185;
- TombStoneOccurred tomb_stone_occurred = 186;
+ 180 [(module) = "permissioncontroller"];
+ AppInstallOnExternalStorageReported app_install_on_external_storage_reported =
+ 181 [(module) = "framework"];
+ NetworkStackReported network_stack_reported = 182 [(module) = "network_stack"];
+ AppMovedStorageReported app_moved_storage_reported = 183 [(module) = "framework"];
+ BiometricEnrolled biometric_enrolled = 184 [(module) = "framework"];
+ SystemServerWatchdogOccurred system_server_watchdog_occurred = 185 [(module) = "framework"];
+ TombStoneOccurred tomb_stone_occurred = 186 [(module) = "framework"];
BluetoothClassOfDeviceReported bluetooth_class_of_device_reported =
- 187 [(log_from_module) = "bluetooth"];
+ 187 [(module) = "bluetooth"];
IntelligenceEventReported intelligence_event_reported =
- 188 [(log_from_module) = "intelligence"];
- ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189;
+ 188 [(module) = "intelligence"];
+ ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed =
+ 189 [(module) = "framework"];
RoleRequestResultReported role_request_result_reported =
- 190 [(log_from_module) = "permissioncontroller"];
+ 190 [(module) = "permissioncontroller"];
MediametricsAudiopolicyReported mediametrics_audiopolicy_reported = 191;
MediametricsAudiorecordReported mediametrics_audiorecord_reported = 192;
MediametricsAudiothreadReported mediametrics_audiothread_reported = 193;
@@ -321,131 +344,258 @@
MediametricsMediadrmReported mediametrics_mediadrm_reported = 198;
MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199;
MediametricsRecorderReported mediametrics_recorder_reported = 200;
- CarPowerStateChanged car_power_state_changed = 203;
- GarageModeInfo garage_mode_info = 204;
- TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"];
- ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = 206;
- ContentCaptureServiceEvents content_capture_service_events = 207;
- ContentCaptureSessionEvents content_capture_session_events = 208;
- ContentCaptureFlushed content_capture_flushed = 209;
- LocationManagerApiUsageReported location_manager_api_usage_reported = 210;
+ MediametricsDrmManagerReported mediametrics_drmmanager_reported = 201;
+ CarPowerStateChanged car_power_state_changed = 203 [(module) = "car"];
+ GarageModeInfo garage_mode_info = 204 [(module) = "car"];
+ TestAtomReported test_atom_reported = 205 [(module) = "cts"];
+ ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported =
+ 206 [(module) = "framework"];
+ ContentCaptureServiceEvents content_capture_service_events = 207 [(module) = "framework"];
+ ContentCaptureSessionEvents content_capture_session_events = 208 [(module) = "framework"];
+ ContentCaptureFlushed content_capture_flushed = 209 [(module) = "framework"];
+ LocationManagerApiUsageReported location_manager_api_usage_reported =
+ 210 [(module) = "framework"];
ReviewPermissionsFragmentResultReported review_permissions_fragment_result_reported =
- 211 [(log_from_module) = "permissioncontroller"];
+ 211 [(module) = "permissioncontroller"];
RuntimePermissionsUpgradeResult runtime_permissions_upgrade_result =
- 212 [(log_from_module) = "permissioncontroller"];
+ 212 [(module) = "permissioncontroller"];
GrantPermissionsActivityButtonActions grant_permissions_activity_button_actions =
- 213 [(log_from_module) = "permissioncontroller"];
+ 213 [(module) = "permissioncontroller"];
LocationAccessCheckNotificationAction location_access_check_notification_action =
- 214 [(log_from_module) = "permissioncontroller"];
+ 214 [(module) = "permissioncontroller"];
AppPermissionFragmentActionReported app_permission_fragment_action_reported =
- 215 [(log_from_module) = "permissioncontroller"];
+ 215 [(module) = "permissioncontroller"];
AppPermissionFragmentViewed app_permission_fragment_viewed =
- 216 [(log_from_module) = "permissioncontroller"];
+ 216 [(module) = "permissioncontroller"];
AppPermissionsFragmentViewed app_permissions_fragment_viewed =
- 217 [(log_from_module) = "permissioncontroller"];
+ 217 [(module) = "permissioncontroller"];
PermissionAppsFragmentViewed permission_apps_fragment_viewed =
- 218 [(log_from_module) = "permissioncontroller"];
- ExclusionRectStateChanged exclusion_rect_state_changed = 223;
- BackGesture back_gesture_reported_reported = 224;
-
+ 218 [(module) = "permissioncontroller"];
+ TextSelectionEvent text_selection_event = 219 [(module) = "textclassifier"];
+ TextLinkifyEvent text_linkify_event = 220 [(module) = "textclassifier"];
+ ConversationActionsEvent conversation_actions_event = 221 [(module) = "textclassifier"];
+ LanguageDetectionEvent language_detection_event = 222 [(module) = "textclassifier"];
+ ExclusionRectStateChanged exclusion_rect_state_changed = 223 [(module) = "framework"];
+ BackGesture back_gesture_reported_reported = 224 [(module) = "sysui"];
UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225;
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];
- PerfettoUploaded perfetto_uploaded =
- 229 [(log_from_module) = "perfetto"];
- VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
- BootTimeEventDuration boot_time_event_duration_reported = 239;
- BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240;
+ 228 [(module) = "framework"];
+ PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
+ VmsClientConnectionStateChanged vms_client_connection_state_changed =
+ 230 [(module) = "car"];
+ MediaProviderScanOccurred media_provider_scan_occurred = 233 [(module) = "mediaprovider"];
+ MediaContentDeleted media_content_deleted = 234 [(module) = "mediaprovider"];
+ MediaProviderPermissionRequested media_provider_permission_requested =
+ 235 [(module) = "mediaprovider"];
+ MediaProviderSchemaChanged media_provider_schema_changed = 236 [(module) = "mediaprovider"];
+ MediaProviderIdleMaintenanceFinished media_provider_idle_maintenance_finished =
+ 237 [(module) = "mediaprovider"];
+ RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238 [(module) = "framework"];
+ BootTimeEventDuration boot_time_event_duration_reported = 239 [(module) = "framework"];
+ BootTimeEventElapsedTime boot_time_event_elapsed_time_reported =
+ 240 [(module) = "framework"];
BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
- BootTimeEventErrorCode boot_time_event_error_code_reported = 242;
- UserspaceRebootReported userspace_reboot_reported = 243 [(log_from_module) = "framework"];
+ BootTimeEventErrorCode boot_time_event_error_code_reported = 242 [(module) = "framework"];
+ UserspaceRebootReported userspace_reboot_reported = 243 [(module) = "framework"];
+ NotificationReported notification_reported = 244 [(module) = "framework"];
+ NotificationPanelReported notification_panel_reported = 245 [(module) = "sysui"];
+ NotificationChannelModified notification_channel_modified = 246 [(module) = "framework"];
+ IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"];
+ IntegrityRulesPushed integrity_rules_pushed = 248 [(module) = "framework"];
+ CellBroadcastMessageReported cb_message_reported =
+ 249 [(module) = "cellbroadcast"];
+ CellBroadcastMessageError cb_message_error =
+ 250 [(module) = "cellbroadcast"];
+ WifiHealthStatReported wifi_health_stat_reported = 251 [(module) = "wifi"];
+ WifiFailureStatReported wifi_failure_stat_reported = 252 [(module) = "wifi"];
+ WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
+ AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"];
SnapshotMergeReported snapshot_merge_reported = 255;
- NetworkIpProvisioningReported network_ip_provisioning_reported = 290 [(log_from_module) = "network_stack"];
- NetworkDhcpRenewReported network_dhcp_renew_reported = 291 [(log_from_module) = "network_stack"];
- NetworkValidationReported network_validation_reported = 292 [(log_from_module) = "network_stack"];
- NetworkStackQuirkReported network_stack_quirk_reported = 293 [(log_from_module) = "network_stack"];
+ ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended =
+ 256 [(module) = "framework"];
+ 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", (module) = "sysui"];
+ TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"];
+ LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"];
+ PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"];
+ UserLifecycleJourneyReported user_lifecycle_journey_reported = 264 [(module) = "framework"];
+ UserLifecycleEventOccurred user_lifecycle_event_occurred = 265 [(module) = "framework"];
+ AccessibilityShortcutReported accessibility_shortcut_reported =
+ 266 [(module) = "framework"];
+ AccessibilityServiceReported accessibility_service_reported = 267 [(module) = "settings"];
+ DocsUIDragAndDropReported docs_ui_drag_and_drop_reported = 268 [(module) = "docsui"];
+ AppUsageEventOccurred app_usage_event_occurred = 269 [(module) = "framework"];
+ AutoRevokeNotificationClicked auto_revoke_notification_clicked =
+ 270 [(module) = "permissioncontroller"];
+ AutoRevokeFragmentAppViewed auto_revoke_fragment_app_viewed =
+ 271 [(module) = "permissioncontroller"];
+ AutoRevokedAppInteraction auto_revoked_app_interaction =
+ 272 [(module) = "permissioncontroller", (module) = "settings"];
+ AppPermissionGroupsFragmentAutoRevokeAction
+ app_permission_groups_fragment_auto_revoke_action =
+ 273 [(module) = "permissioncontroller"];
+ EvsUsageStatsReported evs_usage_stats_reported = 274 [(module) = "evs"];
+ AudioPowerUsageDataReported audio_power_usage_data_reported = 275;
+ TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"];
+ MediaOutputOpSwitchReported mediaoutput_op_switch_reported =
+ 277 [(module) = "settings"];
+ 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 [(log_from_module) = "network_tethering"];
+ 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.
}
// Pulled events will start at field 10000.
- // Next: 10080
+ // Next: 10084
oneof pulled {
- WifiBytesTransfer wifi_bytes_transfer = 10000;
- WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
- MobileBytesTransfer mobile_bytes_transfer = 10002;
- MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = 10003;
- BluetoothBytesTransfer bluetooth_bytes_transfer = 10006;
- KernelWakelock kernel_wakelock = 10004;
- SubsystemSleepState subsystem_sleep_state = 10005;
- CpuTimePerFreq cpu_time_per_freq = 10008;
- CpuTimePerUid cpu_time_per_uid = 10009;
- CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
- WifiActivityInfo wifi_activity_info = 10011;
- ModemActivityInfo modem_activity_info = 10012;
- BluetoothActivityInfo bluetooth_activity_info = 10007;
- ProcessMemoryState process_memory_state = 10013;
- SystemElapsedRealtime system_elapsed_realtime = 10014;
- SystemUptime system_uptime = 10015;
- CpuActiveTime cpu_active_time = 10016;
- CpuClusterTime cpu_cluster_time = 10017;
- DiskSpace disk_space = 10018 [deprecated=true];
- RemainingBatteryCapacity remaining_battery_capacity = 10019;
- FullBatteryCapacity full_battery_capacity = 10020;
- Temperature temperature = 10021;
- BinderCalls binder_calls = 10022;
- BinderCallsExceptions binder_calls_exceptions = 10023;
- LooperStats looper_stats = 10024;
- DiskStats disk_stats = 10025;
- DirectoryUsage directory_usage = 10026;
- AppSize app_size = 10027;
- CategorySize category_size = 10028;
- ProcStats proc_stats = 10029;
- BatteryVoltage battery_voltage = 10030;
- NumFingerprintsEnrolled num_fingerprints_enrolled = 10031;
- DiskIo disk_io = 10032;
- PowerProfile power_profile = 10033;
- ProcStatsPkgProc proc_stats_pkg_proc = 10034;
- ProcessCpuTime process_cpu_time = 10035;
- NativeProcessMemoryState native_process_memory_state = 10036;
- CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
+ WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
+ WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
+ MobileBytesTransfer mobile_bytes_transfer =
+ 10002 [(module) = "framework", (truncate_timestamp) = true];
+ MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg =
+ 10003 [(module) = "framework", (truncate_timestamp) = true];
+ BluetoothBytesTransfer bluetooth_bytes_transfer = 10006 [(module) = "framework"];
+ KernelWakelock kernel_wakelock = 10004 [(module) = "framework"];
+ SubsystemSleepState subsystem_sleep_state = 10005 [(module) = "statsdtest"];
+ CpuTimePerFreq cpu_time_per_freq = 10008 [(module) = "framework"];
+ CpuTimePerUid cpu_time_per_uid = 10009 [(module) = "framework", (module) = "statsdtest"];
+ CpuTimePerUidFreq cpu_time_per_uid_freq =
+ 10010 [(module) = "framework", (module) = "statsd"];
+ WifiActivityInfo wifi_activity_info = 10011 [(module) = "framework"];
+ ModemActivityInfo modem_activity_info = 10012 [(module) = "framework"];
+ BluetoothActivityInfo bluetooth_activity_info = 10007 [(module) = "framework"];
+ ProcessMemoryState process_memory_state = 10013 [(module) = "framework"];
+ SystemElapsedRealtime system_elapsed_realtime = 10014 [(module) = "framework"];
+ SystemUptime system_uptime = 10015 [(module) = "framework"];
+ CpuActiveTime cpu_active_time = 10016 [(module) = "framework", (module) = "statsdtest"];
+ CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework"];
+ DiskSpace disk_space = 10018 [deprecated=true, (module) = "statsdtest"];
+ RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"];
+ FullBatteryCapacity full_battery_capacity = 10020 [(module) = "framework"];
+ Temperature temperature = 10021 [(module) = "framework", (module) = "statsdtest"];
+ BinderCalls binder_calls = 10022 [(module) = "framework", (module) = "statsd"];
+ BinderCallsExceptions binder_calls_exceptions = 10023 [(module) = "framework"];
+ LooperStats looper_stats = 10024 [(module) = "framework", (module) = "statsd"];
+ DiskStats disk_stats = 10025 [(module) = "framework"];
+ DirectoryUsage directory_usage = 10026 [(module) = "framework"];
+ AppSize app_size = 10027 [(module) = "framework"];
+ CategorySize category_size = 10028 [(module) = "framework"];
+ ProcStats proc_stats = 10029 [(module) = "framework"];
+ BatteryVoltage battery_voltage = 10030 [(module) = "framework"];
+ NumFingerprintsEnrolled num_fingerprints_enrolled = 10031 [(module) = "framework"];
+ DiskIo disk_io = 10032 [(module) = "framework"];
+ PowerProfile power_profile = 10033 [(module) = "framework"];
+ ProcStatsPkgProc proc_stats_pkg_proc = 10034 [(module) = "framework"];
+ ProcessCpuTime process_cpu_time = 10035 [(module) = "framework"];
+ CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037 [(module) = "framework"];
OnDevicePowerMeasurement on_device_power_measurement = 10038;
- DeviceCalculatedPowerUse device_calculated_power_use = 10039;
- DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid = 10040;
- DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
- ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042;
- BatteryLevel battery_level = 10043;
- BuildInformation build_information = 10044;
- BatteryCycleCount battery_cycle_count = 10045;
- DebugElapsedClock debug_elapsed_clock = 10046;
- DebugFailingElapsedClock debug_failing_elapsed_clock = 10047;
- NumFacesEnrolled num_faces_enrolled = 10048;
- RoleHolder role_holder = 10049;
- DangerousPermissionState dangerous_permission_state = 10050;
- TrainInfo train_info = 10051;
- TimeZoneDataInfo time_zone_data_info = 10052;
- ExternalStorageInfo external_storage_info = 10053;
+ DeviceCalculatedPowerUse device_calculated_power_use = 10039 [(module) = "framework"];
+ DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid =
+ 10040 [(module) = "framework"];
+ DeviceCalculatedPowerBlameOther device_calculated_power_blame_other =
+ 10041 [(module) = "framework"];
+ ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042 [(module) = "framework"];
+ BatteryLevel battery_level = 10043 [(module) = "framework"];
+ BuildInformation build_information = 10044 [(module) = "framework"];
+ BatteryCycleCount battery_cycle_count = 10045 [(module) = "framework"];
+ DebugElapsedClock debug_elapsed_clock = 10046 [(module) = "framework"];
+ DebugFailingElapsedClock debug_failing_elapsed_clock = 10047 [(module) = "framework"];
+ NumFacesEnrolled num_faces_enrolled = 10048 [(module) = "framework"];
+ RoleHolder role_holder = 10049 [(module) = "framework"];
+ DangerousPermissionState dangerous_permission_state = 10050 [(module) = "framework"];
+ TrainInfo train_info = 10051 [(module) = "statsd"];
+ TimeZoneDataInfo time_zone_data_info = 10052 [(module) = "framework"];
+ ExternalStorageInfo external_storage_info = 10053 [(module) = "framework"];
GpuStatsGlobalInfo gpu_stats_global_info = 10054;
GpuStatsAppInfo gpu_stats_app_info = 10055;
- SystemIonHeapSize system_ion_heap_size = 10056;
- AppsOnExternalStorageInfo apps_on_external_storage_info = 10057;
- FaceSettings face_settings = 10058;
- CoolingDevice cooling_device = 10059;
- AppOps app_ops = 10060;
- ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
- VmsClientStats vms_client_stats = 10065;
- NotificationRemoteViews notification_remote_views = 10066;
- VoiceCallSession voice_call_session = 10076 [(log_from_module) = "telephony"];
- VoiceCallRatUsage voice_call_rat_usage = 10077 [(log_from_module) = "telephony"];
- SimSlotState sim_slot_state = 10078 [(log_from_module) = "telephony"];
- SupportedRadioAccessFamily supported_radio_access_family =
- 10079 [(log_from_module) = "telephony"];
+ SystemIonHeapSize system_ion_heap_size = 10056 [deprecated = true, (module) = "framework"];
+ AppsOnExternalStorageInfo apps_on_external_storage_info = 10057 [(module) = "framework"];
+ FaceSettings face_settings = 10058 [(module) = "framework"];
+ CoolingDevice cooling_device = 10059 [(module) = "framework"];
+ AppOps app_ops = 10060 [(module) = "framework"];
+ ProcessSystemIonHeapSize process_system_ion_heap_size = 10061 [(module) = "framework"];
+ SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062;
+ SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063;
+ ProcessMemorySnapshot process_memory_snapshot = 10064 [(module) = "framework"];
+ VmsClientStats vms_client_stats = 10065 [(module) = "car"];
+ NotificationRemoteViews notification_remote_views = 10066 [(module) = "framework"];
+ DangerousPermissionStateSampled dangerous_permission_state_sampled =
+ 10067 [(module) = "framework"];
+ GraphicsStats graphics_stats = 10068;
+ RuntimeAppOpAccess runtime_app_op_access = 10069 [(module) = "framework"];
+ IonHeapSize ion_heap_size = 10070 [(module) = "framework"];
+ PackageNotificationPreferences package_notification_preferences =
+ 10071 [(module) = "framework"];
+ PackageNotificationChannelPreferences package_notification_channel_preferences =
+ 10072 [(module) = "framework"];
+ PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences =
+ 10073 [(module) = "framework"];
+ GnssStats gnss_stats = 10074 [(module) = "framework"];
+ AttributedAppOps attributed_app_ops = 10075 [(module) = "framework"];
+ VoiceCallSession voice_call_session = 10076 [(module) = "telephony"];
+ VoiceCallRatUsage voice_call_rat_usage = 10077 [(module) = "telephony"];
+ SimSlotState sim_slot_state = 10078 [(module) = "telephony"];
+ SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
+ SettingSnapshot setting_snapshot = 10080 [(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.
// Field numbers 100,000 - 199,999 are reserved for non-AOSP (e.g. OEMs) to use.
// Field numbers 200,000 and above are reserved for future use; do not use them at all.
+
+ reserved 10036;
}
/**
@@ -534,7 +684,8 @@
*/
message ScreenStateChanged {
// New screen state, from frameworks/base/core/proto/android/view/enums.proto.
- optional android.view.DisplayStateEnum state = 1 [(state_field_option).option = EXCLUSIVE];
+ optional android.view.DisplayStateEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -545,10 +696,11 @@
* frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
*/
message UidProcessStateChanged {
- optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true];
+ optional int32 uid = 1 [(state_field_option).primary_field = true, (is_uid) = true];
// The state, from frameworks/base/core/proto/android/app/enums.proto.
- optional android.app.ProcessStateEnum state = 2 [(state_field_option).option = EXCLUSIVE];
+ optional android.app.ProcessStateEnum state = 2
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -580,7 +732,8 @@
ASLEEP = 1;
AWAKE = 2;
}
- optional State state = 1 [(state_field_option).option = EXCLUSIVE];
+ optional State state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -599,7 +752,7 @@
CRITICAL = 4; // critical memory.
}
- optional State factor = 1 [(state_field_option).option = EXCLUSIVE];
+ optional State factor = 1 [(state_field_option).exclusive_state = true];
}
/**
@@ -632,6 +785,96 @@
}
/**
+ * Logs the change in wifi health.
+ *
+ * Logged from:
+ * frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiDataStall.java
+ */
+message WifiHealthStatReported {
+ enum Band {
+ UNKNOWN = 0;
+ // All of 2.4GHz band
+ BAND_2G = 1;
+ // Frequencies in the range of [5150, 5250) GHz
+ BAND_5G_LOW = 2;
+ // Frequencies in the range of [5250, 5725) GHz
+ BAND_5G_MIDDLE = 3;
+ // Frequencies in the range of [5725, 5850) GHz
+ BAND_5G_HIGH = 4;
+ // Frequencies in the range of [5925, 6425) GHz
+ BAND_6G_LOW = 5;
+ // Frequencies in the range of [6425, 6875) GHz
+ BAND_6G_MIDDLE = 6;
+ // Frequencies in the range of [6875, 7125) GHz
+ BAND_6G_HIGH = 7;
+ }
+ // duration this stat is obtained over in milliseconds
+ optional int32 duration_millis = 1;
+ // whether wifi is classified as sufficient for the user's data traffic, determined
+ // by whether the calculated throughput exceeds the average demand within |duration_millis|
+ optional bool is_sufficient = 2;
+ // whether cellular data is available
+ optional bool is_cell_data_available = 3;
+ // the Band bucket the connected network is on
+ optional Band band = 4;
+}
+
+/**
+ * Logged when wifi detects a significant change in connection failure rate.
+ *
+ * Logged from: frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiHealthMonitor.java
+ *
+ */
+message WifiFailureStatReported {
+ enum AbnormalityType {
+ UNKNOWN = 0;
+ SIGNIFICANT_INCREASE = 1;
+ SIGNIFICANT_DECREASE = 2;
+ SIMPLY_HIGH = 3;
+ }
+ enum FailureType {
+ FAILURE_UNKNOWN = 0;
+ FAILURE_CONNECTION = 1;
+ FAILURE_ASSOCIATION_REJECTION = 2;
+ FAILURE_ASSOCIATION_TIMEOUT = 3;
+ FAILURE_AUTHENTICATION = 4;
+ FAILURE_NON_LOCAL_DISCONNECTION = 5;
+ FAILURE_SHORT_CONNECTION_DUE_TO_NON_LOCAL_DISCONNECTION = 6;
+ }
+ // Reason for uploading this stat
+ optional AbnormalityType abnormality_type = 1;
+ // The particular type of failure
+ optional FailureType failure_type = 2;
+ // How many times we have encountered this combination of AbnormalityType and FailureType
+ optional int32 failure_count = 3;
+}
+
+/**
+ * Logs whether a wifi connection is successful and reasons for failure if it isn't.
+ *
+ * Logged from:
+ * frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java
+ */
+message WifiConnectionResultReported {
+ enum FailureCode {
+ FAILURE_UNKNOWN = 0;
+ FAILURE_ASSOCIATION_TIMEOUT = 1;
+ FAILURE_ASSOCIATION_REJECTION = 2;
+ FAILURE_AUTHENTICATION_GENERAL = 3;
+ FAILURE_AUTHENTICATION_EAP = 4;
+ FAILURE_DHCP = 5;
+ FAILURE_NETWORK_DISCONNECTION = 6;
+ FAILURE_ROAM_TIMEOUT = 7;
+ }
+ // true represents a successful connection
+ optional bool connection_result = 1;
+ // reason for the connection failure
+ optional FailureCode failure_code = 2;
+ // scan rssi before the connection attempt
+ optional int32 rssi = 3;
+}
+
+/**
* Logs when memory stats of a process is reported.
*
* Logged from:
@@ -686,7 +929,8 @@
* packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
*/
message BleScanStateChanged {
- repeated AttributionNode attribution_node = 1;
+ repeated AttributionNode attribution_node = 1
+ [(state_field_option).primary_field_first_uid = true];
enum State {
OFF = 0;
@@ -694,14 +938,19 @@
// RESET indicates all ble stopped. Used when it (re)starts (e.g. after it crashes).
RESET = 2;
}
- optional State state = 2;
+ optional State state = 2 [
+ (state_field_option).exclusive_state = true,
+ (state_field_option).default_state_value = 0 /* State.OFF */,
+ (state_field_option).trigger_state_reset_value = 2 /* State.RESET */,
+ (state_field_option).nested = true
+ ];
// Does the scan have a filter.
- optional bool is_filtered = 3;
+ optional bool is_filtered = 3 [(state_field_option).primary_field = true];
// Whether the scan is a CALLBACK_TYPE_FIRST_MATCH scan. Called 'background' scan internally.
- optional bool is_first_match = 4;
+ optional bool is_first_match = 4 [(state_field_option).primary_field = true];
// Whether the scan set to piggy-back off the results of other scans (SCAN_MODE_OPPORTUNISTIC).
- optional bool is_opportunistic = 5;
+ optional bool is_opportunistic = 5 [(state_field_option).primary_field = true];
}
/**
@@ -833,11 +1082,23 @@
FREQUENT = 2;
RARE = 3;
NEVER = 4;
+ RESTRICTED = 5;
}
optional Bucket standby_bucket = 5 [default = UNKNOWN];
// The job id (as assigned by the app).
optional int32 job_id = 6;
+
+ // One flag for each of the API constraints defined by Jobscheduler. Does not include implcit
+ // constraints as they are always assumed to be set.
+ optional bool has_charging_constraint = 7;
+ optional bool has_battery_not_low_constraint = 8;
+ optional bool has_storage_not_low_constraint = 9;
+ optional bool has_timing_delay_constraint = 10;
+ optional bool has_deadline_constraint = 11;
+ optional bool has_idle_constraint = 12;
+ optional bool has_connectivity_constraint = 13;
+ optional bool has_content_trigger_constraint = 14;
}
/**
@@ -919,14 +1180,15 @@
* TODO
*/
message WakelockStateChanged {
- repeated AttributionNode attribution_node = 1;
+ repeated AttributionNode attribution_node = 1
+ [(state_field_option).primary_field_first_uid = true];
// The type (level) of the wakelock; e.g. a partial wakelock or a full wakelock.
// From frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.WakeLockLevelEnum type = 2;
+ optional android.os.WakeLockLevelEnum type = 2 [(state_field_option).primary_field = true];
// The wakelock tag (Called tag in the Java API, sometimes name elsewhere).
- optional string tag = 3;
+ optional string tag = 3 [(state_field_option).primary_field = true];
enum State {
RELEASE = 0;
@@ -934,7 +1196,11 @@
CHANGE_RELEASE = 2;
CHANGE_ACQUIRE = 3;
}
- optional State state = 4;
+ optional State state = 4 [
+ (state_field_option).exclusive_state = true,
+ (state_field_option).default_state_value = 0,
+ (state_field_option).nested = true
+ ];
}
/**
@@ -984,7 +1250,8 @@
OFF = 0;
ON = 1;
}
- optional State state = 1;
+ optional State state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -994,7 +1261,8 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message DeviceIdleModeStateChanged {
- optional android.server.DeviceIdleModeEnum state = 1;
+ optional android.server.DeviceIdleModeEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
@@ -1005,7 +1273,8 @@
* frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message DeviceIdlingModeStateChanged {
- optional android.server.DeviceIdleModeEnum state = 1;
+ optional android.server.DeviceIdleModeEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -1038,7 +1307,8 @@
*/
message ChargingStateChanged {
// State of the battery, from frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.BatteryStatusEnum state = 1;
+ optional android.os.BatteryStatusEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -1049,7 +1319,8 @@
*/
message PluggedStateChanged {
// Whether the device is plugged in, from frameworks/base/core/proto/android/os/enums.proto.
- optional android.os.BatteryPluggedStateEnum state = 1;
+ optional android.os.BatteryPluggedStateEnum state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
/**
@@ -1067,18 +1338,8 @@
// Name of source package (for historical reasons, since BatteryStats tracked it).
optional string package_name = 3;
- // These enum values match the STANDBY_BUCKET_XXX constants defined in UsageStatsManager.java.
- enum Bucket {
- UNKNOWN = 0;
- EXEMPTED = 5;
- ACTIVE = 10;
- WORKING_SET = 20;
- FREQUENT = 30;
- RARE = 40;
- NEVER = 50;
- }
// The App Standby bucket of the app that scheduled the alarm at the time the alarm fired.
- optional Bucket app_standby_bucket = 4;
+ optional AppStandbyBucketChanged.Bucket app_standby_bucket = 4;
}
/**
@@ -1139,6 +1400,8 @@
}
/**
+ * This atom is deprecated starting in R.
+ *
* Logs when an app causes Wifi to run. In this context, 'to run' means to use Wifi Client Mode.
* TODO: Include support for Hotspot, perhaps by using an extra field to denote 'mode'.
* Note that Wifi Scanning is monitored separately in WifiScanStateChanged.
@@ -1789,12 +2052,15 @@
REASON_EXPLICIT_HEALTH_CHECK = 2;
REASON_APP_CRASH = 3;
REASON_APP_NOT_RESPONDING = 4;
+ REASON_NATIVE_CRASH_DURING_BOOT = 5;
}
optional RollbackReasonType rollback_reason = 4;
// Set by RollbackPackageHealthObserver to be the package that is failing when a rollback
// is initiated. Empty if the package is unknown.
optional string failing_package_name = 5;
+
+ optional TrainExperimentIds experiment_ids = 6 [(log_mode) = MODE_BYTES];
}
/**
@@ -2518,8 +2784,9 @@
STATE_DISCONNECTED = 0;
STATE_CONNECTED = 1;
}
- optional State state = 1;
- optional string id = 2;
+ optional State state = 1
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
+ optional string id = 2 [(state_field_option).primary_field = true];
// Last active session in ms.
// 0 when the port is in connected state.
optional int64 last_connect_duration_millis = 3;
@@ -2750,21 +3017,32 @@
message BackGesture {
enum BackType {
- DEFAULT_BACK_TYPE = 0;
- COMPLETED = 1;
- COMPLETED_REJECTED = 2; // successful because coming from rejected area
- INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area
- INCOMPLETE = 4;
+ DEFAULT_BACK_TYPE = 0;
+ COMPLETED = 1;
+ COMPLETED_REJECTED = 2; // successful because coming from rejected area
+ INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area
+ INCOMPLETE = 4; // Unsuccessful, for reasons other than below.
+ INCOMPLETE_FAR_FROM_EDGE = 5; // Unsuccessful, far from the edge.
+ INCOMPLETE_MULTI_TOUCH = 6; // Unsuccessful, multi touch.
+ INCOMPLETE_LONG_PRESS = 7; // Unsuccessful, long press.
+ INCOMPLETE_VERTICAL_MOVE = 8; // Unsuccessful, move vertically.
}
optional BackType type = 1;
- optional int32 y_coordinate = 2; // y coordinate for ACTION_DOWN event
+ optional int32 y_coordinate = 2 [deprecated = true]; // y coordinate for ACTION_DOWN event
+ optional int32 start_x = 4; // X coordinate for ACTION_DOWN event.
+ optional int32 start_y = 5; // Y coordinate for ACTION_DOWN event.
+ optional int32 end_x = 6; // X coordinate for ACTION_MOVE event.
+ optional int32 end_y = 7; // Y coordinate for ACTION_MOVE event.
+ optional int32 left_boundary = 8; // left edge width + left inset
+ optional int32 right_boundary = 9; // screen width - (right edge width + right inset)
+
enum WindowHorizontalLocation {
DEFAULT_LOCATION = 0;
LEFT = 1;
RIGHT = 2;
}
- optional WindowHorizontalLocation x_location = 3;
+ optional WindowHorizontalLocation x_location = 3 [deprecated = true];
}
message ExclusionRectStateChanged {
@@ -2783,14 +3061,125 @@
optional int32 duration_millis = 7;
}
-message LauncherUIChanged {
- optional android.stats.launcher.LauncherAction action = 1;
- optional android.stats.launcher.LauncherState src_state = 2;
- optional android.stats.launcher.LauncherState dst_state = 3;
- optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES];
- optional bool is_swipe_up_enabled = 5;
+/**
+ * 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:
+ * packages/apps/Launcher3
+ */
+message LauncherUIChanged {
+ optional android.stats.launcher.LauncherAction action = 1 [deprecated = true];
+ optional android.stats.launcher.LauncherState src_state = 2;
+ optional android.stats.launcher.LauncherState dst_state = 3;
+ optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES, deprecated = true];
+ optional bool is_swipe_up_enabled = 5 [deprecated = true];
+
+ // The event id (e.g., app launch, drag and drop, long press)
+ optional int32 event_id = 6;
+ // The event's source or target id (e.g., icon, task, button)
+ optional int32 target_id = 7;
+ // If the target needs to be tracked, use this id field
+ optional int32 instance_id = 8;
+ optional int32 uid = 9 [(is_uid) = true];
+ optional string package_name = 10;
+ optional string component_name = 11;
+
+ // (x, y) coordinate and the index information of the target on the container
+ optional int32 grid_x = 12 [default = -1];
+ optional int32 grid_y = 13 [default = -1];
+ optional int32 page_id = 14 [default = -2];
+
+ // e.g., folder icon's (x, y) location and index information on the workspace
+ optional int32 grid_x_parent = 15 [default = -1];
+ optional int32 grid_y_parent = 16 [default = -1];
+ optional int32 page_id_parent = 17 [default = -2];
+
+ // e.g., SEARCHBOX_ALLAPPS, FOLDER_WORKSPACE
+ optional int32 hierarchy = 18;
+
+ optional bool is_work_profile = 19;
+
+ // Used to store the predicted rank of the target
+ optional int32 rank = 20 [default = -1];
+
+ // e.g., folderLabelState can be captured in the following two fields
+ optional int32 from_state = 21;
+ optional int32 to_state = 22;
+
+ // e.g., autofilled or suggested texts that are not user entered
+ optional string edittext = 23;
+
+ // e.g., number of contents inside a container (e.g., icons inside a folder)
+ optional int32 cardinality = 24;
+}
+
+/**
+ * Used for snapshot of the HomeScreen UI elements
+ *
+ * Logged from:
+ * packages/apps/Launcher3
+ */
+message LauncherStaticLayout {
+ // The event id (e.g., snapshot, drag and drop)
+ optional int32 event_id = 1;
+ // The event's source or target id (e.g., icon, shortcut, widget)
+ optional int32 target_id = 2;
+ // If the target needs to be tracked, use this id field
+ optional int32 instance_id = 3;
+ optional int32 uid = 4 [(is_uid) = true];
+ optional string package_name = 5;
+ optional string component_name = 6;
+
+ // (x, y) coordinate and the index information of the target on the container
+ optional int32 grid_x = 7 [default = -1];
+ optional int32 grid_y = 8 [default = -1];
+ optional int32 page_id = 9 [default = -2];
+
+ // e.g., folder icon's (x, y) location and index information on the workspace
+ // e.g., when used with widgets target, use these values for (span_x, span_y)
+ optional int32 grid_x_parent = 10 [default = -1];
+ optional int32 grid_y_parent = 11 [default = -1];
+ optional int32 page_id_parent = 12 [default = -2];
+
+ // UNKNOWN = 0
+ // HOTSEAT = 1
+ // WORKSPACE = 2
+ // FOLDER_HOTSEAT = 3
+ // FOLDER_WORKSPACE = 4
+ optional int32 hierarchy = 13;
+
+ optional bool is_work_profile = 14;
+
+ // e.g., PIN, WIDGET TRAY, APPS TRAY, PREDICTION
+ optional int32 origin = 15;
+
+ // e.g., number of icons inside a folder
+ optional int32 cardinality = 16;
+
+ // e.g., (x, y) span of the widget inside homescreen grid system
+ optional int32 span_x = 17 [default = 1];
+ optional int32 span_y = 18 [default = 1];
+}
+
+/**
+ * Logs when Wallpaper or ThemePicker UI has changed.
+ *
+ * Logged from:
+ * packages/apps/ThemePicker
+ * packages/apps/WallpaperPicker2
+ */
message StyleUIChanged {
optional android.stats.style.Action action = 1;
optional int32 color_package_hash = 2;
@@ -2848,12 +3237,14 @@
message TouchEventReported {
/**
* The fields latency_{min|max|mean|stdev} represent minimum, maximum, mean,
- * and the standard deviation of latency between the kernel and framework
- * for touchscreen events. The units are microseconds.
+ * and the standard deviation of the time spent processing touchscreen events
+ * in the kernel and inputflinger. The units are microseconds.
*
- * The number is measured as the difference between the time at which
- * the input event was received in the evdev driver,
- * and the time at which the input event was received in EventHub.
+ * On supported devices, the starting point is taken during the hard interrupt inside the
+ * kernel touch driver. On all other devices, the starting point is taken inside
+ * the kernel's input event subsystem upon receipt of the input event.
+ * The ending point is taken inside InputDispatcher, just after the input event
+ * is sent to the app.
*/
// Minimum value
optional float latency_min_micros = 1;
@@ -3023,7 +3414,7 @@
optional string process_name = 3;
// The pid if available. -1 means not available.
- optional sint32 pid = 4;
+ optional int32 pid = 4;
optional string package_name = 5;
@@ -3059,7 +3450,7 @@
optional string process_name = 3;
// The pid if available. -1 means not available.
- optional sint32 pid = 4;
+ optional int32 pid = 4;
optional android.server.ErrorSource error_source = 5;
}
@@ -3295,9 +3686,9 @@
* services/core/java/com/android/server/wm/Session.java
*/
message OverlayStateChanged {
- optional int32 uid = 1 [(is_uid) = true];
+ optional int32 uid = 1 [(state_field_option).primary_field = true, (is_uid) = true];
- optional string package_name = 2;
+ optional string package_name = 2 [(state_field_option).primary_field = true];
optional bool using_alert_window = 3;
@@ -3305,15 +3696,16 @@
ENTERED = 1;
EXITED = 2;
}
- optional State state = 4;
+ optional State state = 4
+ [(state_field_option).exclusive_state = true, (state_field_option).nested = false];
}
-/*
+/**
* Logs foreground service starts and stops.
* Note that this is not when a service starts or stops, but when it is
* considered foreground.
* Logged from
- * //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
*/
message ForegroundServiceStateChanged {
optional int32 uid = 1 [(is_uid) = true];
@@ -3325,6 +3717,45 @@
EXIT = 2;
}
optional State state = 3;
+
+ // Whether the fgs is allowed while-in-use permissions, i.e. is considered 'in-use' to the user.
+ // (If the fgs was started while the app wasn't TOP it usually will be denied these permissions)
+ optional bool allow_while_in_use_permission = 4;
+}
+
+/**
+ * Logs the number of times a uid accesses a sensitive AppOp during a foreground service session.
+ * A foreground service session is any continuous period during which the uid holds at least one
+ * foreground service; the atom will be pushed when the uid no longer holds any foreground services.
+ * Accesses initiated while the uid is in the TOP state are ignored.
+ * Sessions with no attempted accesses are not logged.
+ * Logged from
+ * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ForegroundServiceAppOpSessionEnded {
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The operation's name.
+ // Only following four ops are logged
+ // COARSE_LOCATION = 0
+ // FINE_LOCATION = 1
+ // CAMERA = 26
+ // RECORD_AUDIO = 27
+ optional android.app.AppOpEnum app_op_name = 2 [default = APP_OP_NONE];
+
+ // The uid's permission mode for accessing the AppOp during this fgs session.
+ enum Mode {
+ MODE_UNKNOWN = 0;
+ MODE_ALLOWED = 1; // Always allowed
+ MODE_IGNORED = 2; // Denied
+ MODE_FOREGROUND = 3; // Allow-while-in-use (or allowed-one-time)
+ }
+ optional Mode app_op_mode = 3;
+
+ // Number of times this AppOp was requested and allowed.
+ optional int32 count_ops_accepted = 4;
+ // Number of times this AppOp was requested but denied.
+ optional int32 count_ops_rejected = 5;
}
/**
@@ -3471,7 +3902,7 @@
*/
message AppDied {
// timestamp(elapsedRealtime) of record creation
- optional uint64 timestamp_millis = 1 [(state_field_option).option = EXCLUSIVE];
+ optional uint64 timestamp_millis = 1 [(state_field_option).exclusive_state = true];
}
/**
@@ -3486,6 +3917,144 @@
}
/**
+ * Atom for simple logging of user interaction and impression events, such as "the user touched
+ * this button" or "this dialog was displayed".
+ * Keep the UI event stream clean: don't use for system or background events.
+ * Log using the UiEventLogger wrapper - don't write with the StatsLog API directly.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/
+ */
+message UiEventReported {
+ // The event_id.
+ optional int32 event_id = 1;
+ // The event's source or target uid and package, if applicable.
+ // For example, the package posting a notification, or the destination package of a share.
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+ // An identifier used to disambiguate which logs refer to a particular instance of some
+ // UI element. Useful when there might be multiple instances simultaneously active.
+ optional int32 instance_id = 4;
+}
+
+/**
+ * Reports a notification was created or updated.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/notification/
+ */
+message NotificationReported {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The notifying app's uid and package.
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+ // A small system-assigned identifier for the notification.
+ // Locally probably-unique, but expect collisions across users and/or days.
+ optional int32 instance_id = 4;
+ optional int32 notification_id_hash = 5; // Small hash of the app-assigned notif ID + tag
+ optional int32 channel_id_hash = 6; // Small hash of app-assigned channel ID
+
+ // Grouping information
+ optional int32 group_id_hash = 7; // Small hash of the group ID of the notification
+ optional int32 group_instance_id = 8; // Instance_id of the group-summary notification
+ optional bool is_group_summary = 9; // Tags the group-summary notification
+
+ // Attributes
+ optional string category = 10; // App-assigned notification category (API-defined strings)
+ optional int32 style = 11; // App-assigned notification style
+ optional int32 num_people = 12; // Number of Person records attached to the notification
+
+ // Ordering, importance and interruptiveness
+
+ optional int32 position = 13; // Position in NotificationManager's list
+
+ optional android.stats.sysui.NotificationImportance importance = 14;
+ optional int32 alerting = 15; // Bitfield, 1=buzz 2=beep 4=blink
+
+ enum NotificationImportanceExplanation {
+ IMPORTANCE_EXPLANATION_UNKNOWN = 0;
+ IMPORTANCE_EXPLANATION_APP = 1; // App-specified channel importance.
+ IMPORTANCE_EXPLANATION_USER = 2; // User-specified channel importance.
+ IMPORTANCE_EXPLANATION_ASST = 3; // Notification Assistant override.
+ IMPORTANCE_EXPLANATION_SYSTEM = 4; // System override.
+ // Like _APP, but based on pre-channels priority signal.
+ IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS = 5;
+ }
+
+ optional NotificationImportanceExplanation importance_source = 16;
+ optional android.stats.sysui.NotificationImportance importance_initial = 17;
+ optional NotificationImportanceExplanation importance_initial_source = 18;
+ optional android.stats.sysui.NotificationImportance importance_asst = 19;
+ optional int32 assistant_hash = 20;
+ optional float assistant_ranking_score = 21;
+}
+
+message Notification {
+ // The notifying app's uid and package.
+ optional int32 uid = 1 [(is_uid) = true];
+ optional string package_name = 2;
+ // A small system-assigned identifier for the notification.
+ optional int32 instance_id = 3;
+
+ // Grouping information.
+ optional int32 group_instance_id = 4;
+ optional bool is_group_summary = 5;
+
+ // The section of the shade that the notification is in.
+ // See NotificationSectionsManager.PriorityBucket.
+ enum NotificationSection {
+ SECTION_UNKNOWN = 0;
+ SECTION_HEADS_UP = 1;
+ SECTION_MEDIA_CONTROLS = 2;
+ SECTION_PEOPLE = 3;
+ SECTION_ALERTING = 4;
+ SECTION_SILENT = 5;
+ }
+ optional NotificationSection section = 6;
+}
+
+message NotificationList {
+ repeated Notification notifications = 1; // An ordered sequence of notifications.
+}
+
+/**
+ * Reports a notification panel was displayed, e.g. from the lockscreen or status bar.
+ *
+ * Logged from:
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/
+ */
+message NotificationPanelReported {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ optional int32 num_notifications = 2;
+ // The notifications in the panel, in the order that they appear there.
+ optional NotificationList notifications = 3 [(log_mode) = MODE_BYTES];
+}
+
+/**
+ * Reports a notification channel, or channel group, was created, updated, or deleted.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/notification/
+ */
+message NotificationChannelModified {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The notifying app's uid and package.
+ optional int32 uid = 2 [(is_uid) = true];
+ optional string package_name = 3;
+ // Hash of app-assigned notification channel ID or channel-group ID
+ optional int32 channel_id_hash = 4;
+ // Previous importance setting, if applicable
+ optional android.stats.sysui.NotificationImportance old_importance = 5;
+ // New importance setting
+ optional android.stats.sysui.NotificationImportance importance = 6;
+}
+
+
+/**
* Logs when a biometric acquire event occurs.
*
* Logged from:
@@ -3622,6 +4191,7 @@
/**
* Potential experiment ids that goes with a train install.
+ * Should be kept in sync with experiment_ids.proto.
*/
message TrainExperimentIds {
repeated int64 experiment_id = 1;
@@ -3673,12 +4243,16 @@
INSTALL_FAILURE_DOWNLOAD = 23;
INSTALL_FAILURE_STATE_MISMATCH = 24;
INSTALL_FAILURE_COMMIT = 25;
+ REBOOT_TRIGGERED = 26;
}
optional State state = 6;
// Possible experiment ids for monitoring this push.
optional TrainExperimentIds experiment_ids = 7 [(log_mode) = MODE_BYTES];
// user id
optional int32 user_id = 8;
+ optional int32 reason = 9;
+ // Whether or not this is a rollback event
+ optional bool is_rollback = 10;
}
/* Test atom, is not logged anywhere */
@@ -3851,7 +4425,7 @@
DIALOG_LINE_ITEM = 5;
}
- optional Type type = 1 [(state_field_option).option = EXCLUSIVE];
+ optional Type type = 1 [(state_field_option).exclusive_state = true];
// Used if the type is LINE_ITEM
optional string package_name = 2;
@@ -3981,6 +4555,145 @@
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.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/scan/ModernMediaScanner.java
+ */
+message MediaProviderScanOccurred {
+ enum Reason {
+ // Scan triggered due to unknown reason
+ UNKNOWN = 0;
+ // Scan triggered due to storage volume being mounted
+ MOUNTED = 1;
+ // Scan triggered due to explicit user action or app request
+ DEMAND = 2;
+ // Scan triggered due to idle maintenance
+ IDLE = 3;
+ }
+
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // Reason why this scan was triggered
+ optional Reason reason = 2;
+ // Total number of files scanned
+ optional int64 item_count = 3;
+ // Duration of scan, normalized per file
+ optional float normalized_duration_millis = 4;
+ // Number of database inserts, normalized per file
+ optional float normalized_insert_count = 5;
+ // Number of database updates, normalized per file
+ optional float normalized_update_count = 6;
+ // Number of database deletes, normalized per file
+ optional float normalized_delete_count = 7;
+}
+
+/**
+ * Logs when an app has asked MediaProvider to delete media belonging to the user.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message MediaContentDeleted {
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // UID of app that requested deletion
+ optional int32 uid = 2 [(is_uid) = true];
+ // Number of items that were deleted
+ optional int32 item_count = 3;
+}
+
+/**
+ * Logs when an app has asked MediaProvider to grant them access to media belonging to the user.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/PermissionActivity.java
+ */
+message MediaProviderPermissionRequested {
+ enum Result {
+ UNKNOWN = 0;
+ USER_GRANTED = 1;
+ AUTO_GRANTED = 2;
+ USER_DENIED = 3;
+ USER_DENIED_WITH_PREJUDICE = 4;
+ AUTO_DENIED = 5;
+ }
+
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // UID of app that requested permission
+ optional int32 uid = 2 [(is_uid) = true];
+ // Number of items that were requested
+ optional int32 item_count = 3;
+ // Result of this request
+ optional Result result = 4;
+}
+
+/**
+ * Logs when MediaProvider has finished upgrading or downgrading its database schema.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/DatabaseHelper.java
+ */
+message MediaProviderSchemaChanged {
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // Old database version code
+ optional int32 version_from = 2;
+ // New database version code
+ optional int32 version_to = 3;
+ // Total number of files in database
+ optional int64 item_count = 4;
+ // Duration of schema change, normalized per file
+ optional float normalized_duration_millis = 5;
+}
+
+/**
+ * Logs when MediaProvider has finished an idle maintenance job.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message MediaProviderIdleMaintenanceFinished {
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+
+ // Total number of files in database
+ optional int64 item_count = 2;
+ // Duration of idle maintenance, normalized per file
+ optional float normalized_duration_millis = 3;
+ // Number of thumbnails found to be stale, normalized per file
+ optional float normalized_stale_thumbnails = 4;
+ // Number of items found to be expired, normalized per file
+ optional float normalized_expired_media = 5;
+}
+
/**
* Represents boot time event with duration in ms.
*
@@ -4190,8 +4903,7 @@
* after an OTA.
*
* Logged from:
- * - system/core/fs_mgr/libsnapshot/snapshot.cpp
- * - system/core/fs_mgr/libsnapshot/snapshotctl.cpp
+ * - system/update_engine/cleanup_previous_update_action.cc
*/
message SnapshotMergeReported {
// Keep in sync with
@@ -4229,6 +4941,106 @@
// 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;
}
//////////////////////////////////////////////////////////////////////
@@ -4315,6 +5127,66 @@
}
/**
+ * Used for pull network statistics via mobile|wifi networks, and sliced by interesting dimensions.
+ * Note that the data is expected to be sliced into more dimensions in future. In other words,
+ * the caller must not assume any row of data is one full report when filtering with a set of
+ * matching conditions, because future data may represent with multiple rows what is currently
+ * represented by one.
+ * To avoid being broken by future slicing, callers must take care to aggregate rows even if they
+ * query all the existing columns.
+ *
+ * Pulled from:
+ * StatsPullAtomService (using NetworkStatsService to get NetworkStats)
+ */
+message DataUsageBytesTransfer {
+ // State of this record. Should be NetworkStats#SET_DEFAULT or NetworkStats#SET_FOREGROUND to
+ // indicate the foreground state, or NetworkStats#SET_ALL to indicate the record is for all
+ // states combined, not including debug states. See NetworkStats#SET_*.
+ optional int32 state = 1;
+
+ optional int64 rx_bytes = 2;
+
+ optional int64 rx_packets = 3;
+
+ optional int64 tx_bytes = 4;
+
+ optional int64 tx_packets = 5;
+
+ // Radio Access Technology (RAT) type of this record, should be one of
+ // TelephonyManager#NETWORK_TYPE_* constants, or NetworkTemplate#NETWORK_TYPE_ALL to indicate
+ // the record is for all rat types combined.
+ optional int32 rat_type = 6;
+
+ // Mcc/Mnc read from sim if the record is for a specific subscription, null indicates the
+ // record is combined across subscriptions.
+ optional string sim_mcc = 7;
+ optional string sim_mnc = 8;
+
+ // Allows mobile virtual network operators (MVNOs) to be identified with individual IDs.
+ // See TelephonyManager#getSimCarrierId.
+ optional int32 carrier_id = 9;
+
+ // Enumeration of opportunistic states with an additional ALL state indicates the record is
+ // combined regardless of the boolean value in its field.
+ enum DataSubscriptionState {
+ UNKNOWN = 0; // For server side backward compatibility.
+ ALL = 1;
+ OPPORTUNISTIC = 2;
+ NOT_OPPORTUNISTIC = 3;
+ }
+ // Mark whether the subscription is an opportunistic data subscription, and ALL indicates the
+ // record is combined across opportunistic data subscriptions.
+ // See {@link SubscriptionManager#setOpportunistic}.
+ optional DataSubscriptionState opportunistic_data_sub = 10;
+
+ // Indicate whether NR is connected, server side could use this with RAT type to determine if
+ // the record is for 5G NSA (Non Stand Alone) mode, where the primary cell is still LTE and
+ // network allocates a secondary 5G cell so telephony reports RAT = LTE along with NR state as
+ // connected.
+ optional bool is_nr_connected = 11;
+}
+
+/**
* Pulls bytes transferred via bluetooth. It is pulled from Bluetooth controller.
*
* Pulled from:
@@ -4521,8 +5393,8 @@
optional int64 page_major_fault = 5;
// RSS
- // Value is read from /proc/PID/status. Or from memory.stat, field
- // total_rss if per-app memory cgroups are enabled.
+ // Value is read from memory.stat, field total_rss if per-app memory
+ // cgroups are enabled. Otherwise, value from /proc/pid/stat.
optional int64 rss_in_bytes = 6;
// CACHE
@@ -4532,56 +5404,17 @@
// SWAP
// Value is read from memory.stat, field total_swap if per-app memory
- // cgroups are enabled. Otherwise, VmSwap from /proc/PID/status.
+ // cgroups are enabled. Otherwise, 0.
optional int64 swap_in_bytes = 8;
- // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
+ // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always -1.
optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true];
- // Elapsed real time when the process started.
- // Value is read from /proc/PID/stat, field 22. 0 if read from per-app memory cgroups.
- optional int64 start_time_nanos = 10;
+ // Deprecated: use ProcessMemorySnapshot atom instead. Always -1.
+ optional int64 start_time_nanos = 10 [deprecated = true];
- // Anonymous page size plus swap size. Values are read from /proc/PID/status.
- optional int32 anon_rss_and_swap_in_kilobytes = 11;
-}
-
-/*
- * Logs the memory stats for a native process (from procfs).
- *
- * Pulled from StatsCompanionService for selected native processes.
- */
-message NativeProcessMemoryState {
- // The uid if available. -1 means not available.
- optional int32 uid = 1 [(is_uid) = true];
-
- // The process name.
- // Value read from /proc/PID/cmdline.
- optional string process_name = 2;
-
- // # of page-faults
- optional int64 page_fault = 3;
-
- // # of major page-faults
- optional int64 page_major_fault = 4;
-
- // RSS
- // Value read from /proc/PID/status.
- optional int64 rss_in_bytes = 5;
-
- // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
- optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true];
-
- // Elapsed real time when the process started.
- // Value is read from /proc/PID/stat, field 22.
- optional int64 start_time_nanos = 7;
-
- // SWAP
- // Value read from /proc/PID/status, field VmSwap.
- optional int64 swap_in_bytes = 8;
-
- // Anonymous page size plus swap size. Values are read from /proc/PID/status.
- optional int32 anon_rss_and_swap_in_kilobytes = 9;
+ // Deprecated: use ProcessMemorySnapshot atom instead. Always -1.
+ optional int32 anon_rss_and_swap_in_kilobytes = 11 [deprecated = true];
}
/*
@@ -4601,9 +5434,53 @@
// Provided by ActivityManagerService or read from /proc/PID/cmdline.
optional string process_name = 2;
+ // Deprecated: use rss_high_water_mark_in_kilobytes instead. This field is
+ // computed by converting kilobytes to bytes.
+ optional int64 rss_high_water_mark_in_bytes = 3 [deprecated = true];
+
// RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in
// /proc/PID/status.
- optional int64 rss_high_water_mark_in_bytes = 3;
+ optional int32 rss_high_water_mark_in_kilobytes = 4;
+}
+
+/*
+ * Logs the memory stats for a process.
+ *
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService)
+ * and for selected native processes.
+ */
+message ProcessMemorySnapshot {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The process name.
+ // Usually package name or process cmdline.
+ // Provided by ActivityManagerService or read from /proc/PID/cmdline.
+ optional string process_name = 2;
+
+ // The pid of the process.
+ // Allows to disambiguate instances of the process.
+ optional int32 pid = 3;
+
+ // The current OOM score adjustment value.
+ // Read from ProcessRecord for managed processes.
+ // Placeholder -1001 (OOM_SCORE_ADJ_MIN - 1, outside of allowed range) for native ones.
+ optional int32 oom_score_adj = 4;
+
+ // The current RSS of the process.
+ // VmRSS from /proc/pid/status.
+ optional int32 rss_in_kilobytes = 5;
+
+ // The current anon RSS of the process.
+ // RssAnon from /proc/pid/status.
+ optional int32 anon_rss_in_kilobytes = 6;
+
+ // The current swap size of the process.
+ // VmSwap from /proc/pid/status.
+ optional int32 swap_in_kilobytes = 7;
+
+ // The sum of rss_in_kilobytes and swap_in_kilobytes.
+ optional int32 anon_rss_and_swap_in_kilobytes = 8;
}
/*
@@ -5026,48 +5903,81 @@
}
message AggStats {
- optional int64 min = 1;
+ // These are all in byte resolution.
+ optional int64 min = 1 [deprecated = true];
+ optional int64 average = 2 [deprecated = true];
+ optional int64 max = 3 [deprecated = true];
- optional int64 average = 2;
-
- optional int64 max = 3;
+ // These are all in kilobyte resolution. Can fit in int32, so smaller on the wire than the above
+ // int64 fields.
+ optional int32 mean_kb = 4;
+ optional int32 max_kb = 5;
}
+// A reduced subset of process states; reducing the number of possible states allows more
+// aggressive device-side aggregation of statistics and hence reduces metric upload size.
+enum ProcessStateAggregated {
+ PROCESS_STATE_UNKNOWN = 0;
+ // Persistent system process.
+ PROCESS_STATE_PERSISTENT = 1;
+ // Top activity; actually any visible activity.
+ PROCESS_STATE_TOP = 2;
+ // Process binding to top or a foreground service.
+ PROCESS_STATE_BOUND_TOP_OR_FGS = 3;
+ // Processing running a foreground service.
+ PROCESS_STATE_FGS = 4;
+ // Important foreground process (ime, wallpaper, etc).
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 5;
+ // Important background process.
+ PROCESS_STATE_BACKGROUND = 6;
+ // Process running a receiver.
+ PROCESS_STATE_RECEIVER = 7;
+ // All kinds of cached processes.
+ PROCESS_STATE_CACHED = 8;
+}
+
+// Next tag: 13
message ProcessStatsStateProto {
optional android.service.procstats.ScreenState screen_state = 1;
- optional android.service.procstats.MemoryState memory_state = 2;
+ optional android.service.procstats.MemoryState memory_state = 2 [deprecated = true];
// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
// and not frameworks/base/core/java/android/app/ActivityManager.java
- optional android.service.procstats.ProcessState process_state = 3;
+ optional android.service.procstats.ProcessState process_state = 3 [deprecated = true];
+
+ optional ProcessStateAggregated process_state_aggregated = 10;
// Millisecond uptime duration spent in this state
- optional int64 duration_millis = 4;
+ optional int64 duration_millis = 4 [deprecated = true];
+ // Same as above, but with minute resolution so it fits into an int32.
+ optional int32 duration_minutes = 11;
// Millisecond elapsed realtime duration spent in this state
- optional int64 realtime_duration_millis = 9;
+ optional int64 realtime_duration_millis = 9 [deprecated = true];
+ // Same as above, but with minute resolution so it fits into an int32.
+ optional int32 realtime_duration_minutes = 12;
// # of samples taken
optional int32 sample_size = 5;
// PSS is memory reserved for this process
- optional AggStats pss = 6;
+ optional AggStats pss = 6 [deprecated = true];
// USS is memory shared between processes, divided evenly for accounting
- optional AggStats uss = 7;
+ optional AggStats uss = 7 [deprecated = true];
// RSS is memory resident for this process
optional AggStats rss = 8;
}
-// Next Tag: 7
+// Next Tag: 8
message ProcessStatsProto {
// Name of process.
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 {
@@ -5080,7 +5990,7 @@
// PSS stats during cached kill
optional AggStats cached_pss = 3;
}
- optional Kill kill = 3;
+ optional Kill kill = 3 [deprecated = true];
// Time and memory spent in various states.
repeated ProcessStatsStateProto states = 5;
@@ -5088,6 +5998,28 @@
// Total time process has been running... screen_state, memory_state, and process_state
// will not be set.
optional ProcessStatsStateProto total_running_state = 6;
+
+ // Association data for this process in this state;
+ // each entry here is one association.
+ repeated ProcessStatsAssociationProto assocs = 7;
+}
+
+// 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 [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;
+
+ // Uptime total duration in seconds this association (service binding) was around.
+ optional int32 total_duration_secs = 4;
}
message PackageServiceOperationStatsProto {
@@ -5236,6 +6168,10 @@
*/
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;
}
/**
@@ -5263,6 +6199,141 @@
optional NotificationRemoteViewsProto notification_remote_views = 1;
}
+/**
+ * Atom that contains a list of a package's preferences, pulled from NotificationManagerService.java
+ */
+message PackageNotificationPreferences {
+ // Uid under which the package is installed.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Notification importance, which specifies when and how a notification is displayed.
+ // Specified under core/java/android/app/NotificationManager.java.
+ optional int32 importance = 2;
+ // Lockscreen visibility as set by the user.
+ optional int32 visibility = 3;
+ // Bitfield mask indicating what fields were locked by the user (see LockableAppfields in
+ // PreferencesHelper.java)
+ optional int32 user_locked_fields = 4;
+}
+
+/**
+ * Atom that contains a list of a package's channel preferences, pulled from
+ * NotificationManagerService.java.
+ */
+message PackageNotificationChannelPreferences {
+ // Uid under which the package is installed.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Channel's ID. Should always be available.
+ optional string channel_id = 2;
+ // Channel's name. Should always be available.
+ optional string channel_name = 3;
+ // Channel's description. Optionally set by the channel creator.
+ optional string description = 4;
+ // Notification importance, which specifies when and how a notification is displayed. Specified
+ // under core/java/android/app/NotificationManager.java.
+ optional int32 importance = 5;
+ // Bitmask representing which fields have been set by the user. See field bitmask descriptions
+ // at core/java/android/app/NotificationChannel.java
+ optional int32 user_locked_fields = 6;
+ // Indicates if the channel was deleted by the app.
+ optional bool is_deleted = 7;
+ // Indicates if the channel was marked as a conversation by the app.
+ optional bool is_conversation = 8;
+ // Indicates if the channel is a conversation that was demoted by the user.
+ optional bool is_demoted_conversation = 9;
+ // Indicates if the channel is a conversation that was marked as important by the user.
+ optional bool is_important_conversation = 10;
+}
+
+/**
+ * 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.
+ */
+message PackageNotificationChannelGroupPreferences {
+ // Uid under which the package is installed.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Channel Group's ID. Should always be available.
+ optional string group_id = 2;
+ // Channel Group's name. Should always be available.
+ optional string group_name = 3;
+ // Channel Group's description. Optionally set by group creator.
+ optional string description = 4;
+ // Indicates if notifications from this channel group are blocked.
+ optional bool is_blocked = 5;
+ // Bitmask representing which fields have been set by the user. See field bitmask descriptions
+ // at core/java/android/app/NotificationChannelGroup.java
+ optional int32 user_locked_fields = 6;
+}
+
message PowerProfileProto {
optional double cpu_suspend = 1;
@@ -5494,6 +6565,16 @@
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.
@@ -5785,6 +6866,15 @@
optional int32 repeatedly_pick_times = 7;
}
+/** Logs the drag and drop of files.
+
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIDragAndDropReported {
+ optional bool drag_initiated_from_docsui = 1;
+}
+
/**
* Logs when an app's memory is compacted.
*
@@ -6431,10 +7521,10 @@
optional int64 request_id = 1;
// UID of package requesting the permission grant
- optional int32 requesting_uid = 2 [(is_uid) = true];
+ optional int32 uid = 2 [(is_uid) = true];
// Name of package requesting the permission grant
- optional string requesting_package_name = 3;
+ optional string package_name = 3;
// The permission to be granted
optional string permission_name = 4;
@@ -6462,6 +7552,20 @@
AUTO_DENIED = 8;
// permission request was ignored because permission is restricted
IGNORED_RESTRICTED_PERMISSION = 9;
+ // one time permission was granted by user action
+ USER_GRANTED_ONE_TIME = 10;
+ // user ignored request by leaving the request screen without choosing any option
+ USER_IGNORED = 11;
+ // user granted the permission after being linked to settings
+ USER_GRANTED_IN_SETTINGS = 12;
+ // user denied the permission after being linked to settings
+ USER_DENIED_IN_SETTINGS = 13;
+ // user denied the permission with prejudice after being linked to settings
+ USER_DENIED_WITH_PREJUDICE_IN_SETTINGS = 14;
+ // permission was automatically revoked after one-time permission expired
+ AUTO_ONE_TIME_PERMISSION_REVOKED = 15;
+ // permission was automatically revoked for unused app
+ AUTO_UNUSED_APP_PERMISSION_REVOKED = 16;
}
// The result of the permission grant
optional Result result = 6;
@@ -6922,8 +8026,58 @@
}
/**
- * State of a dangerous permission requested by a package
+ * Track Legacy DRM usage
+ * Logged from
+ * frameworks/av/drm/drmserver/DrmManager.cpp
*/
+message MediametricsDrmManagerReported {
+ optional int64 timestamp_nanos = 1;
+ optional string package_name = 2;
+ optional int64 package_version_code = 3;
+ optional int64 media_apex_version = 4;
+
+ enum Method {
+ METHOD_NOT_FOUND = -1;
+ GET_CONSTRAINTS = 0;
+ GET_METADATA = 1;
+ CAN_HANDLE = 2;
+ PROCESS_DRM_INFO = 3;
+ ACQUIRE_DRM_INFO = 4;
+ SAVE_RIGHTS = 5;
+ GET_ORIGINAL_MIME_TYPE = 6;
+ GET_DRM_OBJECT_TYPE = 7;
+ CHECK_RIGHTS_STATUS = 8;
+ REMOVE_RIGHTS = 9;
+ REMOVE_ALL_RIGHTS = 10;
+ OPEN_CONVERT_SESSION = 11;
+ OPEN_DECRYPT_SESSION = 12;
+ }
+
+ // plugin_id+description inform which Legacy DRM plugins are still in use on device
+ optional string plugin_id = 5;
+ optional string description = 6;
+ optional Method method = 7;
+ optional string mime_types = 8;
+
+ optional int64 get_constraints_count = 9;
+ optional int64 get_metadata_count = 10;
+ optional int64 can_handle_count = 11;
+ optional int64 process_drm_info_count = 12;
+ optional int64 acquire_drm_info_count = 13;
+ optional int64 save_rights_count = 14;
+ optional int64 get_original_mime_type_count = 15;
+ optional int64 get_drm_object_type_count = 16;
+ optional int64 check_rights_status_count = 17;
+ optional int64 remove_rights_count = 18;
+ optional int64 remove_all_rights_count = 19;
+ optional int64 open_convert_session_count = 20;
+ optional int64 open_decrypt_session_count = 21;
+}
+
+/**
+ * State of a dangerous permission requested by a package
+ * Pulled from: StatsCompanionService
+*/
message DangerousPermissionState {
// Name of the permission
optional string permission_name = 1;
@@ -6955,7 +8109,8 @@
optional string method_name = 2;
// True if the package is preinstalled.
- optional bool is_preinstalled = 3;
+ // Starting from Android 11, this boolean is not set and will always be false.
+ optional bool is_preinstalled = 3 [deprecated = true];
// True if the package is privileged.
// Starting from Android 11, this boolean is not set and will always be false.
@@ -7003,6 +8158,7 @@
INSTALL_FAILURE_DOWNLOAD = 23;
INSTALL_FAILURE_STATE_MISMATCH = 24;
INSTALL_FAILURE_COMMIT = 25;
+ REBOOT_TRIGGERED = 26;
}
optional Status status = 4;
}
@@ -7131,6 +8287,12 @@
// CPU Vulkan implementation is in use.
optional bool cpu_vulkan_in_use = 6;
+
+ // App is not doing pre-rotation correctly.
+ optional bool false_prerotation = 7;
+
+ // App creates GLESv1 context.
+ optional bool gles_1_in_use = 8;
}
/*
@@ -7139,11 +8301,27 @@
* Pulled from StatsCompanionService.
*/
message SystemIonHeapSize {
+ // Deprecated due to limited support of ion stats in debugfs.
+ // Use `IonHeapSize` instead.
+ option deprecated = true;
+
// Size of the system ion heap in bytes.
+ // Read from debugfs.
optional int64 size_in_bytes = 1;
}
/*
+ * Logs the total size of the ion heap.
+ *
+ * Pulled from StatsCompanionService.
+ */
+message IonHeapSize {
+ // Total size of all ion heaps in kilobytes.
+ // Read from: /sys/kernel/ion/total_heaps_kb.
+ optional int32 total_size_kb = 1;
+}
+
+/*
* Logs the per-process size of the system ion heap.
*
* Pulled from StatsCompanionService.
@@ -7234,8 +8412,14 @@
* Logged from the Intelligence mainline module.
*/
message IntelligenceEventReported {
+ // The event type.
optional android.stats.intelligence.EventType event_id = 1;
+ // Success, failure.
optional android.stats.intelligence.Status status = 2;
+ // How many times the event occured (to report a batch of high frequency events).
+ optional int32 count = 3;
+ // How long the event took (sum of durations if count > 1)
+ optional int64 duration_millis = 4;
}
/**
@@ -7258,6 +8442,245 @@
}
/**
+ * 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:
@@ -7275,11 +8698,11 @@
// Uid of the package requesting the op
optional int32 uid = 1 [(is_uid) = true];
- // Nmae of the package performing the op
+ // Name of the package performing the op
optional string package_name = 2;
- // operation id; maps to the OP_* constants in AppOpsManager.java
- optional int32 op_id = 3;
+ // operation id
+ optional android.app.AppOpEnum op_id = 3 [default = APP_OP_NONE];
// The number of times the op was granted while the app was in the
// foreground (only for trusted requests)
@@ -7304,6 +8727,58 @@
// For long-running operations, total duration of the operation
// while the app was in the background (only for trusted requests)
optional int64 trusted_background_duration_millis = 9;
+
+ // Whether AppOps is guarded by Runtime permission
+ optional bool is_runtime_permission = 10;
+}
+
+/**
+ * Historical app ops data per package and attribution tag.
+ */
+message AttributedAppOps {
+ // Uid of the package requesting the op
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Name of the package performing the op
+ optional string package_name = 2;
+
+ // tag; provided by developer when accessing related API, limited at 50 chars by API.
+ // Attributions must be provided through manifest using <attribution> tag available in R and
+ // above.
+ optional string tag = 3;
+
+ // operation id
+ optional android.app.AppOpEnum op = 4 [default = APP_OP_NONE];
+
+ // The number of times the op was granted while the app was in the
+ // foreground (only for trusted requests)
+ optional int64 trusted_foreground_granted_count = 5;
+
+ // The number of times the op was granted while the app was in the
+ // background (only for trusted requests)
+ optional int64 trusted_background_granted_count = 6;
+
+ // The number of times the op was rejected while the app was in the
+ // foreground (only for trusted requests)
+ optional int64 trusted_foreground_rejected_count = 7;
+
+ // The number of times the op was rejected while the app was in the
+ // background (only for trusted requests)
+ optional int64 trusted_background_rejected_count = 8;
+
+ // For long-running operations, total duration of the operation
+ // while the app was in the foreground (only for trusted requests)
+ optional int64 trusted_foreground_duration_millis = 9;
+
+ // For long-running operations, total duration of the operation
+ // while the app was in the background (only for trusted requests)
+ optional int64 trusted_background_duration_millis = 10;
+
+ // Whether AppOps is guarded by Runtime permission
+ optional bool is_runtime_permission = 11;
+
+ // Sampling rate used on device, from 0 to 100
+ optional int32 sampling_rate = 12;
}
/**
@@ -7418,6 +8893,9 @@
// Button clicked by user - same as bit flags in buttons_presented with only single bit set
optional int32 button_clicked = 5;
+
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 6;
}
/**
@@ -7470,6 +8948,28 @@
// The result of the permission grant
optional bool permission_granted = 6;
+
+ // State of Permission Flags after grant as per android.content.pm.PermissionFlags
+ optional int32 permission_flags = 7;
+
+ enum Button {
+ UNDEFINED = 0;
+ // Allow button
+ ALLOW = 1;
+ // Deny button
+ DENY = 2;
+ // Ask every time button
+ ASK_EVERY_TIME = 3;
+ // Allow all the time button
+ ALLOW_ALWAYS = 4;
+ // Allow only while using the app button
+ ALLOW_FOREGROUND = 5;
+ // Same is Deny button but shown in while in use dialog
+ DENY_FOREGROUND = 6;
+ }
+
+ // Button pressed in the dialog
+ optional Button button_pressed = 8;
}
/**
@@ -7490,7 +8990,8 @@
}
/**
-* Information about a AppPermissionsFragment viewed by user
+* Information about a AppPermissionGroupsFragment viewed by user. Fragment has been renamed, but
+* the log retains the old fragment name.
*/
message AppPermissionsFragmentViewed {
// id which identifies single session of user interacting with permission controller
@@ -7518,7 +9019,6 @@
}
optional Category category = 6;
}
-
/**
* Information about a PermissionAppsFragment viewed by user.
* Logged from ui/handheld/PermissionAppsFragment.java
@@ -7551,6 +9051,478 @@
}
/**
+* Log that the Auto Revoke notification has been clicked
+* Logged from ui/ManagePermissionsActivity
+*/
+message AutoRevokeNotificationClicked {
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 1;
+}
+
+/**
+* Log that an app has been displayed on the auto revoke page, and lists one permission that was
+* auto revoked for it.
+* Logged from ui/handheld/AutoRevokeFragment
+*/
+message AutoRevokeFragmentAppViewed {
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 1;
+
+ // UID of package for which permissions are viewed
+ optional int32 uid = 2 [(is_uid) = true];
+
+ // Name of package for which permissions are viewed
+ optional string package_name = 3;
+
+ // The name of a permission group that has been revoked
+ optional string permission_group_name = 4;
+
+ // The age of the app- more than three months old, or more than six months
+ enum Age {
+ UNDEFINED = 0;
+ NEWER_BUCKET = 1;
+ OLDER_BUCKET = 2;
+ }
+
+ // How long the app has been unused. Currently, newer bucket is 3 months, older is 6 months
+ optional Age age = 5;
+}
+
+/**
+* Log that the user has interacted with an app on the auto revoke fragment
+* Logged from ui/handheld/AutoRevokeFragment
+*/
+message AutoRevokedAppInteraction {
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 1;
+
+ // UID of package for which permissions are viewed
+ optional int32 uid = 2 [(is_uid) = true];
+
+ // Name of package for which permissions are viewed
+ optional string package_name = 3;
+
+ enum Action {
+ UNDEFINED = 0;
+ REMOVE = 1;
+ OPEN = 2;
+ APP_INFO = 3;
+ PERMISSIONS = 4;
+ REMOVE_IN_SETTINGS = 5;
+ OPEN_IN_SETTINGS = 6;
+ }
+
+ // The action the user took to interact with the app
+ optional Action action = 4;
+}
+
+/**
+* Log that the AppPermissionGroupsFragment has been interacted with for the possible purposes of
+* auto revoke, or that the auto revoke switch has been changed
+* Logged from ui/handheld/AppPermissionGroupsFragment
+ */
+message AppPermissionGroupsFragmentAutoRevokeAction {
+ // id which identifies single session of user interacting with permission controller
+ optional int64 session_id = 1;
+
+ // UID of package for which permissions are viewed
+ optional int32 uid = 2 [(is_uid) = true];
+
+ // Name of package for which permissions are viewed
+ optional string package_name = 3;
+
+ enum Action {
+ UNDEFINED = 0;
+ OPENED_FOR_AUTO_REVOKE = 1;
+ OPENED_FROM_INTENT = 2;
+ SWITCH_ENABLED = 3;
+ SWITCH_DISABLED = 4;
+ }
+
+ // The action the user took to interact with the fragment
+ optional Action action = 4;
+}
+
+/**
+ * Logs when there is a smart selection related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message TextSelectionEvent {
+ // A session ID.
+ optional string session_id = 1;
+
+ // Event type of this event.
+ optional android.stats.textclassifier.EventType event_type = 2;
+
+ // Name of the annotator model that is involved in this event.
+ optional string model_name = 3;
+
+ // Type of widget that was involved in triggering this event.
+ optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+ // Index of this event in a session.
+ optional int32 event_index = 5;
+
+ // Entity type that is involved.
+ optional string entity_type = 6;
+
+ // Relative word index of the start of the selection.
+ optional int32 relative_word_start_index = 7;
+
+ // Relative word (exclusive) index of the end of the selection.
+ optional int32 relative_word_end_index = 8;
+
+ // Relative word index of the start of the smart selection.
+ optional int32 relative_suggested_word_start_index = 9;
+
+ // Relative word (exclusive) index of the end of the smart selection.
+ optional int32 relative_suggested_word_end_index = 10;
+
+ // Name of source package.
+ optional string package_name = 11;
+
+ // Name of the LangID model that is involved in this event.
+ optional string langid_model_name = 12;
+}
+
+/**
+ * Logs when there is a smart linkify related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message TextLinkifyEvent {
+ // A session ID.
+ optional string session_id = 1;
+
+ // Event type of this event.
+ optional android.stats.textclassifier.EventType event_type = 2;
+
+ // Name of the annotator model that is involved in this event.
+ optional string model_name = 3;
+
+ // Type of widget that was involved in triggering this event.
+ optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+ // Index of this event in a session.
+ optional int32 event_index = 5;
+
+ // Entity type that is involved.
+ optional string entity_type = 6;
+
+ // Number of links detected.
+ optional int32 num_links = 7;
+
+ // The total length of all links.
+ optional int32 linked_text_length = 8;
+
+ // Length of input text.
+ optional int32 text_length = 9;
+
+ // Time spent on generating links in ms.
+ optional int64 latency_millis = 10;
+
+ // Name of source package.
+ optional string package_name = 11;
+
+ // Name of the LangID model that is involved in this event.
+ optional string langid_model_name = 12;
+}
+
+/**
+ * Logs when there is a conversation actions related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message ConversationActionsEvent {
+ // A session ID.
+ optional string session_id = 1;
+
+ // Event type of this event.
+ optional android.stats.textclassifier.EventType event_type = 2;
+
+ // Name of the actions model that is involved in this event.
+ optional string model_name = 3;
+
+ // Type of widget that was involved in triggering this event.
+ optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+ // The first entity type that is involved.
+ optional string first_entity_type = 5;
+
+ // The second entity type that is involved.
+ optional string second_entity_type = 6;
+
+ // The third entity type that is involved.
+ optional string third_entity_type = 7;
+
+ // The score of the first entity type.
+ optional float score = 8;
+
+ // Name of source package.
+ optional string package_name = 9;
+
+ // Name of the annotator model that is involved in this event.
+ optional string annotator_model_name = 10;
+
+ // Name of the LangID model that is involved in this event.
+ optional string langid_model_name = 11;
+}
+
+/**
+ * Logs when there is a language detection related event.
+ * See frameworks/base/core/java/android/view/textclassifier/TextClassifierEvent.java
+ * Logged from: TextClassifierEventLogger.java
+ */
+message LanguageDetectionEvent {
+ // A session ID.
+ optional string session_id = 1;
+
+ // Event type of this event.
+ optional android.stats.textclassifier.EventType event_type = 2;
+
+ // Name of the language detection model that is involved in this event.
+ optional string model_name = 3;
+
+ // Type of widget that was involved in triggering this event.
+ optional android.stats.textclassifier.WidgetType widget_type = 4;
+
+ // Detected language.
+ optional string language_tag = 5;
+
+ // Score of the detected language.
+ optional float score = 6;
+
+ // Position of this action.
+ optional int32 action_index = 7;
+
+ // Name of source package.
+ optional string package_name = 8;
+}
+
+/**
+ * Information about an OTA update attempt by update_engine.
+ * Logged from platform/system/update_engine/metrics_reporter_android.cc
+ */
+message UpdateEngineUpdateAttemptReported {
+ // The number of attempts for the update engine to apply a given payload.
+ optional int32 attempt_number = 1;
+
+ optional android.stats.otaupdate.PayloadType payload_type = 2;
+
+ // The total time in minutes for the update engine to apply a given payload.
+ // The time is calculated by calling clock_gettime() / CLOCK_BOOTTIME; and
+ // it's increased when the system is sleeping.
+ optional int32 duration_boottime_in_minutes = 3;
+
+ // The total time in minutes for the update engine to apply a given payload.
+ // The time is calculated by calling clock_gettime() / CLOCK_MONOTONIC_RAW;
+ // and it's not increased when the system is sleeping.
+ optional int32 duration_monotonic_in_minutes = 4;
+
+ // The size of the payload in MiBs.
+ optional int32 payload_size_mib = 5;
+
+ // The attempt result reported by the update engine for an OTA update.
+ optional android.stats.otaupdate.AttemptResult attempt_result = 6;
+
+ // The error code reported by the update engine after an OTA update attempt
+ // on A/B devices.
+ optional android.stats.otaupdate.ErrorCode error_code = 7;
+
+ // The build fingerprint of the source system. The value is read from a
+ // 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;
+}
+
+/**
+ * Information about all the attempts the device make before finishing the
+ * successful update.
+ * Logged from platform/system/update_engine/metrics_reporter_android.cc
+ */
+message UpdateEngineSuccessfulUpdateReported {
+ // The number of attempts for the update engine to apply the payload for a
+ // successful update.
+ optional int32 attempt_count = 1;
+
+ optional android.stats.otaupdate.PayloadType payload_type = 2;
+
+ optional int32 payload_size_mib = 3;
+
+ // The total number of bytes downloaded by update_engine since the last
+ // successful update.
+ optional int32 total_bytes_downloaded_mib = 4;
+
+ // The ratio in percentage of the over-downloaded bytes compared to the
+ // total bytes needed to successfully install the update. e.g. 200 if we
+ // download 200MiB in total for a 100MiB package.
+ optional int32 download_overhead_percentage = 5;
+
+ // The total time in minutes for the update engine to apply the payload for a
+ // successful update.
+ optional int32 total_duration_minutes = 6;
+
+ // The number of reboot of the device during a successful update.
+ optional int32 reboot_count = 7;
+}
+
+/**
+ * Reported when the RebootEscrow HAL has attempted to recover the escrowed
+ * key to indicate whether it was successful or not.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+ */
+message RebootEscrowRecoveryReported {
+ optional bool successful = 1;
+}
+
+/**
+ * Global display pipeline metrics reported by SurfaceFlinger.
+ * Pulled from:
+ * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsGlobalInfo {
+ // Total number of frames presented during the tracing period
+ optional int64 total_frames = 1;
+ // Total number of frames missed
+ optional int64 missed_frames = 2;
+ // Total number of frames that fell back to client composition
+ optional int64 client_composition_frames = 3;
+ // Total time the display was turned on
+ optional int64 display_on_millis = 4;
+ // Total time that was spent performing animations.
+ // This is derived from the present-to-present layer histogram
+ optional int64 animation_millis = 5;
+ // Total number of event connections tracked by SurfaceFlinger at the time
+ // of this pull. If this number grows prohibitively large, then this can
+ // cause jank due to resource contention.
+ optional int32 event_connection_count = 6;
+ // Set of timings measured from when SurfaceFlinger began compositing a
+ // frame, until the frame was requested to be presented to the display. This
+ // measures SurfaceFlinger's total CPU walltime on the critical path per
+ // frame.
+ optional FrameTimingHistogram frame_duration = 7
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when SurfaceFlinger first began using the
+ // GPU to composite a frame, until the GPU has finished compositing that
+ // frame. This measures the total additional time SurfaceFlinger needed to
+ // perform due to falling back into GPU composition.
+ optional FrameTimingHistogram render_engine_timing = 8
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+}
+
+/**
+ * Per-layer display pipeline metrics reported by SurfaceFlinger.
+ * The number of layers uploaded will be restricted due to size limitations.
+ * Pulled from:
+ * frameworks/native/services/surfaceflinger/TimeStats/TimeStats.cpp
+ */
+message SurfaceflingerStatsLayerInfo {
+ // The layer for this set of metrics
+ // For now we can infer that the package name is included in the layer
+ // name.
+ optional string layer_name = 1;
+ // Total number of frames presented
+ optional int64 total_frames = 2;
+ // Total number of dropped frames while latching a buffer for this layer.
+ optional int64 dropped_frames = 3;
+ // Set of timings measured between successive presentation timestamps.
+ optional FrameTimingHistogram present_to_present = 4
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when an app queued a buffer for
+ // presentation, until the buffer was actually presented to the
+ // display.
+ optional FrameTimingHistogram post_to_present = 5
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when a buffer is ready to be presented,
+ // until the buffer was actually presented to the display.
+ optional FrameTimingHistogram acquire_to_present = 6
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when a buffer was latched by
+ // SurfaceFlinger, until the buffer was presented to the display
+ optional FrameTimingHistogram latch_to_present = 7
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from the desired presentation to the actual
+ // presentation time
+ optional FrameTimingHistogram desired_to_present = 8
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Set of timings measured from when an app queued a buffer for
+ // presentation, until the buffer was ready to be presented.
+ optional FrameTimingHistogram post_to_acquire = 9
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Frames missed latch because the acquire fence didn't fire
+ optional int64 late_acquire_frames = 10;
+ // Frames latched early because the desired present time was bad
+ optional int64 bad_desired_present_frames = 11;
+}
+
+/**
+ * Histogram of frame counts bucketed by time in milliseconds.
+ * Because of size limitations, we hard-cap the number of buckets, with
+ * buckets for corresponding to larger milliseconds being less precise.
+ */
+message FrameTimingHistogram {
+ // Timings in milliseconds that describes a set of histogram buckets
+ repeated int32 time_millis_buckets = 1;
+ // Number of frames that match to each time_millis, i.e. the bucket
+ // contents
+ // It's required that len(time_millis) == len(frame_count)
+ repeated int64 frame_counts = 2;
+}
+
+/**
+ * Janky event as reported by SurfaceFlinger.
+ * This event is intended to be consumed by a Perfetto subscriber for
+ * automated trace collection.
+ *
+ * Logged from:
+ * frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
+ */
+message DisplayJankReported {
+ // Informational field for how long the janky event lasted in milliseconds
+ optional int64 event_duration_millis = 1;
+ // Number of frame deadlines missed, where SurfaceFlinger failed to update
+ // the display on time.
+ optional int32 present_deadlines_missed = 2;
+}
+
+/**
+ * Information about camera facing and API level usage.
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/camera/CameraServiceProxy.java
+ */
+message CameraActionEvent {
+ // Camera session duration
+ optional int64 duration_millis = 1;
+
+ // Camera API level used
+ optional int32 api_level = 2;
+
+ // Name of client package
+ optional string package_name = 3;
+
+ // Camera facing
+ enum Facing {
+ UNKNOWN = 0;
+ BACK = 1;
+ FRONT = 2;
+ EXTERNAL = 3;
+ }
+ optional Facing facing = 4;
+}
+
+/**
* Logs when a compatibility change is affecting an app.
*
* Logged from:
@@ -7583,6 +9555,7 @@
// Where it was logged from.
optional Source source = 4;
+
}
/**
@@ -7649,70 +9622,123 @@
}
/**
- * Information about an OTA update attempt by update_engine.
- * Logged from platform/system/update_engine/metrics_reporter_android.cc
- */
-message UpdateEngineUpdateAttemptReported {
- // The number of attempts for the update engine to apply a given payload.
- optional int32 attempt_number = 1;
+ * State of a dangerous permission requested by a package - sampled
+ * Pulled from: StatsCompanionService.java with data obtained from PackageManager API
+*/
+message DangerousPermissionStateSampled {
+ // Name of the permission
+ optional string permission_name = 1;
- optional android.stats.otaupdate.PayloadType payload_type = 2;
+ // Uid of the package
+ optional int32 uid = 2 [(is_uid) = true];
- // The total time in minutes for the update engine to apply a given payload.
- // The time is calculated by calling clock_gettime() / CLOCK_BOOTTIME; and
- // it's increased when the system is sleeping.
- optional int32 duration_boottime_in_minutes = 3;
+ // If the permission is granted to the uid
+ optional bool is_granted = 3;
- // The total time in minutes for the update engine to apply a given payload.
- // The time is calculated by calling clock_gettime() / CLOCK_MONOTONIC_RAW;
- // and it's not increased when the system is sleeping.
- optional int32 duration_monotonic_in_minutes = 4;
-
- // The size of the payload in MiBs.
- optional int32 payload_size_mib = 5;
-
- // The attempt result reported by the update engine for an OTA update.
- optional android.stats.otaupdate.AttemptResult attempt_result = 6;
-
- // The error code reported by the update engine after an OTA update attempt
- // on A/B devices.
- optional android.stats.otaupdate.ErrorCode error_code = 7;
-
- // The build fingerprint of the source system. The value is read from a
- // 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;
+ // Permission flags as per android.content.pm.PermissionFlags
+ optional int32 permission_flags = 4;
}
/**
- * Information about all the attempts the device make before finishing the
- * successful update.
- * Logged from platform/system/update_engine/metrics_reporter_android.cc
+ * HWUI stats for a given app.
*/
-message UpdateEngineSuccessfulUpdateReported {
- // The number of attempts for the update engine to apply the payload for a
- // successful update.
- optional int32 attempt_count = 1;
+message GraphicsStats {
+ // The package name of the app
+ optional string package_name = 1;
- optional android.stats.otaupdate.PayloadType payload_type = 2;
+ // The version code of the app
+ optional int64 version_code = 2;
- optional int32 payload_size_mib = 3;
+ // The start & end timestamps in UTC as
+ // milliseconds since January 1, 1970
+ // Compatible with java.util.Date#setTime()
+ optional int64 start_millis = 3;
- // The total number of bytes downloaded by update_engine since the last
- // successful update.
- optional int32 total_bytes_downloaded_mib = 4;
+ optional int64 end_millis = 4;
- // The ratio in percentage of the over-downloaded bytes compared to the
- // total bytes needed to successfully install the update. e.g. 200 if we
- // download 200MiB in total for a 100MiB package.
- optional int32 download_overhead_percentage = 5;
+ // HWUI renders pipeline type: GL (1) or Vulkan (2).
+ enum PipelineType {
+ UNKNOWN = 0;
+ GL = 1;
+ VULKAN = 2;
+ }
- // The total time in minutes for the update engine to apply the payload for a
- // successful update.
- optional int32 total_duration_minutes = 6;
+ // HWUI renders pipeline type: GL or Vulkan.
+ optional PipelineType pipeline = 5;
- // The number of reboot of the device during a successful update.
- optional int32 reboot_count = 7;
+ // Distinct frame count.
+ optional int32 total_frames = 6;
+
+ // Number of "missed vsync" events.
+ optional int32 missed_vsync_count = 7;
+
+ // Number of frames in triple-buffering scenario (high input latency)
+ optional int32 high_input_latency_count = 8;
+
+ // Number of "slow UI thread" events.
+ optional int32 slow_ui_thread_count = 9;
+
+ // Number of "slow bitmap upload" events.
+ optional int32 slow_bitmap_upload_count = 10;
+
+ // Number of "slow draw" events.
+ optional int32 slow_draw_count = 11;
+
+ // Number of frames that missed their deadline (aka, visibly janked)
+ optional int32 missed_deadline_count = 12;
+
+ // The frame time histogram for the package
+ optional FrameTimingHistogram cpu_histogram = 13
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+
+ // The gpu frame time histogram for the package
+ optional FrameTimingHistogram gpu_histogram = 14
+ [(android.os.statsd.log_mode) = MODE_BYTES];
+
+ // UI mainline module version.
+ optional int64 version_ui_module = 15;
+
+ // If true, these are HWUI stats for up to a 24h period for a given app from today.
+ // If false, these are HWUI stats for a 24h period for a given app from the last complete
+ // day (yesterday). Stats from yesterday stay constant, while stats from today may change as
+ // more apps are running / rendering.
+ optional bool is_today = 16;
+}
+
+/**
+ * Message related to dangerous (runtime) app ops access
+ */
+message RuntimeAppOpAccess {
+ // Uid of the package accessing app op
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Name of the package accessing app op
+ optional string package_name = 2;
+
+ // deprecated - set to empty string
+ optional string op_deprecated = 3 [deprecated = true];
+
+ // attribution_tag; provided by developer when accessing related API, limited at 50 chars by
+ // API. Attributions must be provided through manifest using <attribution> tag available in R
+ // and above.
+ optional string attribution_tag = 4;
+
+ // message related to app op access, limited to 600 chars by API
+ optional string message = 5;
+
+ enum SamplingStrategy {
+ DEFAULT = 0;
+ UNIFORM = 1;
+ RARELY_USED = 2;
+ BOOT_TIME_SAMPLING = 3;
+ UNIFORM_OPS = 4;
+ }
+
+ // sampling strategy used to collect this message
+ optional SamplingStrategy sampling_strategy = 6;
+
+ // operation id
+ optional android.app.AppOpEnum op = 7 [default = APP_OP_NONE];
}
/*
@@ -7755,6 +9781,274 @@
optional UserEncryptionState user_encryption_state = 3;
}
+/*
+ * Logs integrity check information during each install.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+ */
+message IntegrityCheckResultReported {
+ optional string package_name = 1;
+ optional string app_certificate_hash = 2;
+ optional int64 version_code = 3;
+ optional string installer_package_name = 4;
+ enum Response {
+ UNKNOWN = 0;
+ ALLOWED = 1;
+ REJECTED = 2;
+ FORCE_ALLOWED = 3;
+ }
+ optional Response response = 5;
+ // An estimate on the cause of the response. This will only be populated for
+ // REJECTED and FORCE_ALLOWED
+ optional bool caused_by_app_cert_rule = 6;
+ optional bool caused_by_installer_rule = 7;
+}
+
+/**
+ * Logs the information about the rules and the provider whenever rules are
+ * pushed into AppIntegrityManager.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+ */
+message IntegrityRulesPushed {
+ optional bool success = 1;
+ // Package name of the app that pushed the rules.
+ optional string rule_provider = 2;
+ // Version string of arbitrary format provided by the rule provider to
+ // identify the rules.
+ optional string rule_version = 3;
+}
+
+/**
+ * Logs when a cell broadcast message is received on the device.
+ *
+ * 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
+ enum CbType {
+ UNKNOWN_TYPE = 0;
+ GSM = 1;
+ CDMA = 2;
+ 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;
+}
+
+/**
+ * Logs when an error occurs while handling a cell broadcast message;
+ *
+ * Logged from CellBroadcastService module:
+ * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/
+ */
+message CellBroadcastMessageError {
+ // The type of error raised when trying to handle a cell broadcast message
+ enum ErrorType {
+ UNKNOWN_TYPE = 0;
+ CDMA_DECODING_ERROR = 1;
+ CDMA_SCP_EMPTY = 2;
+ CDMA_SCP_HANDLING_ERROR = 3;
+ GSM_INVALID_HEADER_LENGTH = 4;
+ GSM_UNSUPPORTED_HEADER_MESSAGE_TYPE = 5;
+ GSM_UNSUPPORTED_HEADER_DATA_CODING_SCHEME = 6;
+ GSM_INVALID_PDU = 7;
+ GSM_INVALID_GEO_FENCING_DATA = 8;
+ GSM_UMTS_INVALID_WAC = 9;
+ FAILED_TO_INSERT_TO_DB = 10;
+ UNEXPECTED_GEOMETRY_FROM_FWK = 11;
+ 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
+ optional ErrorType type = 1;
+
+ // Exception message (or log message) associated with the error (max 1000 chars)
+ optional string exception_message = 2;
+}
+
+/**
+ * Logs when a tune occurs through device's Frontend.
+ * This is atom ID 276.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/tv/tuner/Tuner.java
+ */
+message TvTunerStateChanged {
+ enum State {
+ UNKNOWN = 0;
+ TUNING = 1; // Signal is tuned
+ LOCKED = 2; // the signal is locked
+ NOT_LOCKED = 3; // the signal isn’t locked.
+ SIGNAL_LOST = 4; // the signal was locked, but is lost now.
+ SCANNING = 5; // the signal is scanned
+ SCAN_STOPPED = 6; // the scan is stopped.
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // 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.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java
+ */
+message AppFreezeChanged {
+ // The type of event.
+ enum Action {
+ UNKNOWN = 0;
+ FREEZE_APP = 1;
+ UNFREEZE_APP = 2;
+ }
+ optional Action action = 1;
+
+ // Pid of the process being frozen.
+ optional int32 pid = 2;
+
+ // Name of the process being frozen.
+ optional string process_name = 3;
+
+ // Time since last unfrozen.
+ optional int64 time_unfrozen_millis = 4;
+}
+
/**
* Pulls information for a single voice call.
*
@@ -7906,6 +10200,808 @@
}
/**
+ * Logs gnss stats from location service provider
+ *
+ * Pulled from:
+ * frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+ */
+
+message GnssStats {
+ // Number of location reports since boot
+ optional int64 location_reports = 1;
+
+ // Total pulled reports of Location failures since boot
+ optional int64 location_failure_reports = 2;
+
+ // Number of time to first fix reports since boot
+ 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_millis = 4;
+
+ // Number of position accuracy reports since boot
+ optional int64 position_accuracy_reports = 5;
+
+ // Total pulled reported position accuracy (in meters) since boot
+ optional int64 position_accuracy_meters = 6;
+
+ // Number of top 4 average CN0 reports since boot
+ optional int64 top_four_average_cn0_reports = 7;
+
+ // Total pulled reported of top 4 average CN0 (dB-mHz) since boot
+ optional int64 top_four_average_cn0_db_mhz = 8;
+
+ // Number of l5 top 4 average CN0 reports since boot
+ optional int64 l5_top_four_average_cn0_reports = 9;
+
+ // Total pulled reported of l5 top 4 average CN0 (dB-mHz) since boot
+ optional int64 l5_top_four_average_cn0_db_mhz = 10;
+
+ // Total number of sv status messages reports since boot
+ optional int64 sv_status_reports = 11;
+
+ // Total number of sv status messages reports, where sv is used in fix since boot
+ optional int64 sv_status_reports_used_in_fix = 12;
+
+ // Total number of L5 sv status messages reports since boot
+ optional int64 l5_sv_status_reports = 13;
+
+ // Total number of L5 sv status messages reports, where sv is used in fix since boot
+ optional int64 l5_sv_status_reports_used_in_fix = 14;
+}
+
+/**
+ * Logs when an app is moved to a different standby bucket.
+ *
+ * Logged from:
+ * frameworks/base/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+ */
+message AppStandbyBucketChanged {
+ optional string package_name = 1;
+
+ // Should be 0, 10, 11, 12, etc. where 0 is the owner. See UserHandle for more documentation.
+ optional int32 user_id = 2;
+
+ // These enum values match the constants defined in UsageStatsManager.java.
+ enum Bucket {
+ BUCKET_UNKNOWN = 0;
+ BUCKET_EXEMPTED = 5;
+ BUCKET_ACTIVE = 10;
+ BUCKET_WORKING_SET = 20;
+ BUCKET_FREQUENT = 30;
+ BUCKET_RARE = 40;
+ BUCKET_RESTRICTED = 45;
+ BUCKET_NEVER = 50;
+ }
+ optional Bucket bucket = 3;
+
+ enum MainReason {
+ MAIN_UNKNOWN = 0;
+ MAIN_DEFAULT = 0x0100;
+ MAIN_TIMEOUT = 0x0200;
+ MAIN_USAGE = 0x0300;
+ MAIN_FORCED_BY_USER = 0x0400;
+ MAIN_PREDICTED = 0x0500;
+ MAIN_FORCED_BY_SYSTEM = 0x0600;
+ }
+ optional MainReason main_reason = 4;
+
+ // A more detailed reason for the standby bucket change. The sub reason name is dependent on
+ // the main reason. Values are one of the REASON_SUB_XXX constants defined in
+ // UsageStatsManager.java.
+ optional int32 sub_reason = 5;
+}
+
+/**
+* Reports a started sharesheet transaction.
+*
+* Logged from:
+* frameworks/base/core/java/com/android/internal/app/ChooserActivity.java
+*/
+message SharesheetStarted {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The calling app's package name.
+ optional string package_name = 2;
+ // An identifier to tie together multiple logs relating to the same share event
+ optional int32 instance_id = 3;
+ // The mime type of the share
+ optional string mime_type = 4;
+ // The number of direct targets the calling app is providing that will be shown.
+ optional int32 num_app_provided_direct_targets = 5;
+ // The number of app targets the calling app is providing that will be shown.
+ optional int32 num_app_provided_app_targets = 6;
+ // True if the share originates from the workprofile
+ optional bool is_workprofile = 7;
+
+ enum SharesheetPreviewType { // Constants from ChooserActivity.java
+ CONTENT_PREVIEW_TYPE_UNKNOWN = 0; // Default for proto 2 / 3 compatibility.
+ CONTENT_PREVIEW_IMAGE = 1; // The preview shown in the sharesheet is an image.
+ CONTENT_PREVIEW_FILE = 2; // The preview shown in the sharesheet is a file.
+ CONTENT_PREVIEW_TEXT = 3; // The preview shown in the sharesheet is text.
+ }
+ // How the sharesheet preview is presented.
+ optional SharesheetPreviewType preview_type = 8;
+
+ enum ResolverActivityIntent { // Intents handled by ResolverActivity.java
+ INTENT_DEFAULT = 0;
+ INTENT_ACTION_VIEW = 1;
+ INTENT_ACTION_EDIT = 2;
+ INTENT_ACTION_SEND = 3;
+ INTENT_ACTION_SENDTO = 4;
+ INTENT_ACTION_SEND_MULTIPLE = 5;
+ INTENT_ACTION_IMAGE_CAPTURE = 6;
+ INTENT_ACTION_MAIN = 7;
+ }
+ // The intent being processed (only SEND and SEND_MULTIPLE are system sharesheet)
+ optional ResolverActivityIntent intent_type = 9;
+}
+
+/**
+ * Reports a ranking selection event.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/app/ChooserActivity.java (sharesheet)
+ */
+message RankingSelected {
+ // The event_id (as for UiEventReported).
+ optional int32 event_id = 1;
+ // The relevant app's package name (can be source or picked package).
+ optional string package_name = 2;
+ // An identifier to tie together multiple logs relating to the same share event.
+ optional int32 instance_id = 3;
+ // Which of the ranked targets got picked, default starting position 0.
+ optional int32 position_picked = 4;
+}
+
+/**
+ * Logs when TvSettings UI is interacted at.
+ *
+ * Logged from: packages/apps/TvSettings
+ */
+message TvSettingsUIInteracted {
+
+ /** The UI action category */
+ optional android.app.tvsettings.Action action = 1;
+
+ /** The ID of the entry that the users actioned on */
+ optional android.app.tvsettings.ItemId item_id = 2;
+}
+
+/**
+ * Logs information about a package installation using package installer V2 APIs.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
+ */
+message PackageInstallerV2Reported {
+ // Whether this installation uses Incremental File System
+ optional bool is_incremental = 1;
+ // Name of the package that is intended to be installed
+ optional string package_name = 2;
+ // The duration between when the install was requested to when the install has completed
+ optional int64 duration_millis = 3;
+ // Installation result in final integer, which are SystemApi's.
+ // Return_code 1 indicates success.
+ // For full list, see frameworks/base/core/java/android/content/pm/PackageManager.java
+ optional int32 return_code = 4;
+ // Total size of the APKs installed for this package
+ optional int64 apks_size_bytes = 5;
+}
+
+/**
+ * Logs settings provider values.
+ *
+ * Use DeviceConfig.getProperties to get a list Setting key, query the data from content provider,
+ * then write the value to proto.
+ *
+ */
+message SettingSnapshot {
+
+ // Setting key
+ optional string name = 1;
+
+ enum SettingsValueType {
+ NOTASSIGNED = 0;
+ ASSIGNED_BOOL_TYPE = 1;
+ ASSIGNED_INT_TYPE = 2;
+ ASSIGNED_FLOAT_TYPE = 3;
+ ASSIGNED_STRING_TYPE = 4;
+ };
+ // Setting value type
+ optional SettingsValueType type = 2;
+
+ optional bool bool_value = 3;
+
+ optional int32 int_value = 4;
+
+ optional float float_value = 5;
+
+ optional string str_value = 6;
+
+ // Android user index. 0 for primary user, 10, 11 for secondary or profile user
+ optional int32 user_id = 7;
+}
+
+/**
+ * An event logged to indicate that a user journey is about to be performed. This atom includes
+ * relevant information about the users involved in the journey. A UserLifecycleEventOccurred event
+ * will immediately follow this atom which will describe the event(s) and its state.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/UserController.java
+ * frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
+ */
+message UserLifecycleJourneyReported {
+ // An identifier to track a chain of user lifecycle events occurring (referenced in the
+ // UserLifecycleEventOccurred atom)
+ optional int64 session_id = 1;
+
+ // Indicates what type of user journey this session is related to
+ enum Journey {
+ UNKNOWN = 0; // Undefined user lifecycle journey
+ USER_SWITCH_UI = 1; // A user switch journey where a UI is shown
+ USER_SWITCH_FG = 2; // A user switch journey without a UI shown
+ USER_START = 3; // A user start journey
+ USER_CREATE = 4; // A user creation journey
+ }
+ optional Journey journey = 2;
+ // Which user the journey is originating from - could be -1 for certain phases (eg USER_CREATE)
+ // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest)
+ optional int32 origin_user = 3;
+ // Which user the journey is targeting
+ // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest)
+ optional int32 target_user = 4;
+
+ // What is the user type of the target user
+ // These should be in sync with USER_TYPE_* flags defined in UserManager.java
+ enum UserType {
+ TYPE_UNKNOWN = 0;
+ FULL_SYSTEM = 1;
+ FULL_SECONDARY = 2;
+ FULL_GUEST = 3;
+ FULL_DEMO = 4;
+ FULL_RESTRICTED = 5;
+ PROFILE_MANAGED = 6;
+ SYSTEM_HEADLESS = 7;
+ }
+ optional UserType user_type = 5;
+ // What are the flags attached to the target user
+ optional int32 user_flags = 6;
+}
+
+/**
+ * An event logged when a specific user lifecycle event is performed. These events should be
+ * correlated with a UserLifecycleJourneyReported atom via the session_id.
+ * Note: journeys can span over multiple events, hence some events may share a single session id.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/UserController.java
+ * frameworks/base/services/core/java/com/android/server/pm/UserManagerService.java
+ */
+message UserLifecycleEventOccurred {
+ // An id which links back to user details (reported in the UserLifecycleJourneyReported atom)
+ optional int64 session_id = 1;
+ // The target user for this event (same as target_user in the UserLifecycleJourneyReported atom)
+ // This integer is a UserIdInt (eg 0 for the system user, 10 for secondary/guest)
+ optional int32 user_id = 2;
+
+ enum Event {
+ UNKNOWN = 0; // Indicates that the associated user journey timed-out or resulted in an error
+ SWITCH_USER = 1; // Indicates that this is a user switch event
+ START_USER = 2; // Indicates that this is a user start event
+ CREATE_USER = 3; // Indicates that this is a user create event
+ USER_RUNNING_LOCKED = 4; // Indicates that user is running in locked state
+ UNLOCKING_USER = 5; // Indicates that this is a user unlocking event
+ UNLOCKED_USER = 6; // Indicates that this is a user unlocked event
+ }
+ optional Event event = 3;
+
+ enum State {
+ NONE = 0; // Indicates the associated event has no start/end defined
+ BEGIN = 1;
+ FINISH = 2;
+ }
+ optional State state = 4; // Represents the state of an event (beginning/ending)
+}
+
+/**
+ * Logs when accessibility shortcut clicked.
+ *
+ * Logged from:
+ * frameworks/base/services/accessibility/java/com/android/server/accessibility
+ */
+message AccessibilityShortcutReported {
+ // The accessibility feature(including installed a11y service, framework a11y feature,
+ // and installed a11y activity) package name that is assigned to the accessibility shortcut.
+ optional string package_name = 1;
+
+ // The definition of the accessibility shortcut.
+ // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto.
+ optional android.stats.accessibility.ShortcutType shortcut_type = 2;
+
+ // The definition of the service status.
+ // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto.
+ optional android.stats.accessibility.ServiceStatus service_status = 3;
+}
+
+/**
+ * Logs when accessibility service status changed.
+ *
+ * Logged from:
+ * packages/apps/Settings/src/com/android/settings/accessibility
+ */
+message AccessibilityServiceReported {
+ // The accessibility service package name.
+ optional string package_name = 1;
+
+ // The definition of the service status.
+ // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto.
+ optional android.stats.accessibility.ServiceStatus service_status = 2;
+}
+
+/**
+ * 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
+ // services/core/java/com/android/server/power/Notifier.java#onWakeUp
+ optional int32 wake_up_reason = 1;
+}
+
+/**
+ * Logs app usage events.
+ */
+message AppUsageEventOccurred {
+ optional int32 uid = 1 [(is_uid) = true];
+ optional string package_name = 2;
+ optional string class_name = 3;
+
+ enum EventType {
+ NONE = 0;
+ MOVE_TO_FOREGROUND = 1;
+ MOVE_TO_BACKGROUND = 2;
+ }
+ optional EventType event_type = 4;
+}
+
+/*
+ * Quality metrics logged when EVS cameras are active.
+ *
+ * Logged from:
+ * packages/services/Car/evs/manager/1.1/Enumerator.cpp
+ */
+message EvsUsageStatsReported {
+
+ // Camera identifier to distinguish the source camera device. This is not
+ // globally unique and therefore cannot be used to identify the user and/or
+ // the device.
+ optional int32 device_id = 1;
+
+ // Peak number of clients during the service
+ optional int32 peak_num_clients = 2;
+
+ // Number of erroneous events during the service
+ optional int32 num_errors = 3;
+
+ // Round trip latency of the very first frame
+ optional int64 first_latency_millis = 4;
+
+ // Average frame round trip latency
+ optional float avg_latency_millis = 5;
+
+ // Peak frame round trip latency
+ optional int64 peak_latency_millis = 6;
+
+ // Total number of frames received
+ optional int64 total_frames = 7;
+
+ // Number of frames ignored
+ optional int64 ignored_frames = 8;
+
+ // Number of dropped frames to synchronize camera devices
+ optional int64 dropped_frames_to_sync = 9;
+
+ // The duration of the service
+ optional int64 duration_millis = 10;
+}
+
+/**
+ * Logs audio power usage stats.
+ *
+ * Pushed from:
+ * frameworks/av/services/mediametrics/AudioPowerUsage.cpp
+ */
+message AudioPowerUsageDataReported {
+ /**
+ * Device used for input/output
+ *
+ * All audio devices please refer to below file:
+ * system/media/audio/include/system/audio-base.h
+ *
+ * Define our own enum values because we don't report all audio devices.
+ * Currently, we only report built-in audio devices such as handset, speaker,
+ * built-in mics, common audio devices such as wired headset, usb headset
+ * and bluetooth devices.
+ */
+ enum AudioDevice {
+ OUTPUT_EARPIECE = 0x1; // handset
+ OUTPUT_SPEAKER = 0x2; // dual speaker
+ OUTPUT_WIRED_HEADSET = 0x4; // 3.5mm headset
+ OUTPUT_USB_HEADSET = 0x8; // usb headset
+ OUTPUT_BLUETOOTH_SCO = 0x10; // bluetooth sco
+ OUTPUT_BLUETOOTH_A2DP = 0x20; // a2dp
+ OUTPUT_SPEAKER_SAFE = 0x40; // bottom speaker
+
+ INPUT_DEVICE_BIT = 0x40000000; // non-negative positive int32.
+ INPUT_BUILTIN_MIC = 0x40000001; // buildin mic
+ INPUT_BUILTIN_BACK_MIC = 0x40000002; // buildin back mic
+ INPUT_WIRED_HEADSET_MIC = 0x40000004; // 3.5mm headset mic
+ INPUT_USB_HEADSET_MIC = 0x40000008; // usb headset mic
+ INPUT_BLUETOOTH_SCO = 0x40000010; // bluetooth sco mic
+ }
+ optional AudioDevice audio_device = 1;
+
+ // Duration of the audio in seconds
+ optional int32 duration_secs = 2;
+
+ // Average volume (0 ... 1.0)
+ optional float average_volume = 3;
+
+ enum AudioType {
+ UNKNOWN_TYPE = 0;
+ VOICE_CALL_TYPE = 1; // voice call
+ VOIP_CALL_TYPE = 2; // voip call, including uplink and downlink
+ MEDIA_TYPE = 3; // music and system sound
+ RINGTONE_NOTIFICATION_TYPE = 4; // ringtone and notification
+ ALARM_TYPE = 5; // alarm type
+ // record type
+ CAMCORDER_TYPE = 6; // camcorder
+ RECORD_TYPE = 7; // other recording
+ }
+ optional AudioType type = 4;
+}
+
+/**
+ * Pulls bytes transferred over WiFi and mobile networks sliced by uid, is_metered, and tag.
+ *
+ * Pulled from:
+ * StatsPullAtomService, which uses NetworkStatsService to query NetworkStats.
+ */
+message BytesTransferByTagAndMetered {
+ optional int32 uid = 1 [(is_uid) = true];
+
+ optional bool is_metered = 2;
+
+ optional int32 tag = 3;
+
+ optional int64 rx_bytes = 4;
+
+ optional int64 rx_packets = 5;
+
+ optional int64 tx_bytes = 6;
+
+ optional int64 tx_packets = 7;
+}
+
+/*
+ * Logs when the Media Output Switcher finishes a media switch operation.
+ *
+ * Logged from:
+ * packages/apps/Settings/src/com/android/settings/media/MediaOutputSliceWorker.java
+ */
+message MediaOutputOpSwitchReported {
+ // Source medium type before switching.
+ optional android.app.settings.mediaoutput.MediumType source = 1;
+
+ // Target medium type after switching.
+ optional android.app.settings.mediaoutput.MediumType target = 2;
+
+ // The result of switching.
+ optional android.app.settings.mediaoutput.SwitchResult result = 3;
+
+ // The detail code of a switching result.
+ optional android.app.settings.mediaoutput.SubResult subresult = 4;
+
+ /*
+ * The package name of a pre-installed app, whose media session is being switched.
+ */
+ optional string media_session_package_name = 5;
+
+ // The amount of available wired devices when a switching is being performed.
+ optional int32 available_wired_device_count = 6;
+
+ // The amount of available Bluetooth devices a switching is being performed.
+ optional int32 available_bt_device_count = 7;
+
+ // The amount of available remote devices when a switching is being performed.
+ optional int32 available_remote_device_count = 8;
+
+ // 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
@@ -7999,9 +11095,9 @@
optional KeyBlobUsageRequirements key_blob_usage_reqs = 11;
enum Type {
- KEY_OPERATION = 0;
- KEY_CREATION = 1;
- KEY_ATTESTATION = 2;
+ key_operation = 0;
+ key_creation = 1;
+ key_attestation = 2;
}
/** Key creation event, operation event or attestation event? */
optional Type type = 12;
@@ -8012,3 +11108,72 @@
/** 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 cf0cc3d..e9875ba 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -37,7 +37,8 @@
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,15 +74,15 @@
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);
return false;
} else {
- ALOGW("Child initialization success %lld ", (long long)child);
+ VLOG("Child initialization success %lld ", (long long)child);
}
if (allConditionTrackers[childIndex]->isSliced()) {
@@ -95,6 +96,11 @@
childTracker->getLogTrackerIndex().end());
}
+ mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
+ initialConditionCache);
+ initialConditionCache[mIndex] =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, initialConditionCache);
+
// unmark this node in the recursion stack.
stack[mIndex] = false;
@@ -105,20 +111,14 @@
void CombinationConditionTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
- const std::vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+ vector<ConditionState>& conditionCache) const {
// So far, this is fine as there is at most one child having sliced output.
for (const int childIndex : mChildren) {
if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
- dimensionFields,
- isSubOutputDimensionFields,
isPartialLink,
- conditionCache,
- dimensionsKeySet);
+ conditionCache);
}
}
conditionCache[mIndex] =
@@ -147,17 +147,14 @@
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
@@ -173,25 +170,6 @@
}
}
-ConditionState CombinationConditionTracker::getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const std::vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
- vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
- // So far, this is fine as there is at most one child having sliced output.
- for (const int childIndex : mChildren) {
- conditionCache[childIndex] = conditionCache[childIndex] |
- allConditions[childIndex]->getMetConditionDimension(
- allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
- }
- evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
- if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
- dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
- }
- return conditionCache[mIndex];
-}
-
bool CombinationConditionTracker::equalOutputDimensions(
const std::vector<sp<ConditionTracker>>& allConditions,
const vector<Matcher>& dimensions) const {
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 481cb20..39ff0ab 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -32,8 +32,8 @@
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,
@@ -43,17 +43,8 @@
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- std::vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
- ConditionState getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+ std::vector<ConditionState>& conditionCache) const override;
// Only one child predicate can have dimension.
const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 5ff0e1d..62736c8 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -36,7 +36,7 @@
mIndex(index),
mInitialized(false),
mTrackerIndex(),
- mNonSlicedConditionState(ConditionState::kUnknown),
+ mUnSlicedPartCondition(ConditionState::kUnknown),
mSliced(false){};
virtual ~ConditionTracker(){};
@@ -51,10 +51,12 @@
// 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,39 +74,19 @@
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.
// [allConditions]: all condition trackers. This is needed because the condition evaluation is
// done recursively
- // [dimensionFields]: the needed dimension fields which should be all or subset of the condition
- // tracker output dimension.
- // [isSubOutputDimensionFields]: true if the needed dimension fields which is strictly subset of
- // the condition tracker output dimension.
// [isPartialLink]: true if the link specified by 'conditionParameters' contains all the fields
// in the condition tracker output dimension.
// [conditionCache]: the cache holding the condition evaluation values.
- // [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination
- // condition, it assumes that only one child predicate is sliced.
virtual void isConditionMet(
const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- std::vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
-
- virtual ConditionState getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const = 0;
+ std::vector<ConditionState>& conditionCache) const = 0;
// return the list of LogMatchingTracker index that this ConditionTracker uses.
virtual const std::set<int>& getLogTrackerIndex() const {
@@ -140,8 +122,9 @@
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:
@@ -156,10 +139,16 @@
// 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/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index f371316..c542032 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -22,27 +22,15 @@
using std::vector;
ConditionState ConditionWizard::query(const int index, const ConditionKey& parameters,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- const bool isPartialLink,
- std::unordered_set<HashableDimensionKey>* dimensionKeySet) {
+ const bool isPartialLink) {
vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
mAllConditions[index]->isConditionMet(
- parameters, mAllConditions, dimensionFields, isSubOutputDimensionFields, isPartialLink,
- cache, *dimensionKeySet);
+ parameters, mAllConditions, isPartialLink,
+ cache);
return cache[index];
}
-ConditionState ConditionWizard::getMetConditionDimension(
- const int index, const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const {
- return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
- isSubOutputDimensionFields,
- *dimensionsKeySet);
-}
-
const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions(
const int index) const {
return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions);
@@ -79,4 +67,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 2c88147..8926479 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -40,15 +40,7 @@
// The ConditionTracker at [conditionIndex] can be a CombinationConditionTracker. In this case,
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
virtual ConditionState query(const int conditionIndex, const ConditionKey& conditionParameters,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- const bool isPartialLink,
- std::unordered_set<HashableDimensionKey>* dimensionKeySet);
-
- virtual ConditionState getMetConditionDimension(
- const int index, const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const;
+ const bool isPartialLink);
virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(const int index) const;
virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 12ff184..efb4d49 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -85,12 +85,6 @@
mInitialValue = ConditionState::kUnknown;
}
- mNonSlicedConditionState = mInitialValue;
-
- if (!mSliced) {
- mUnSlicedPart = mInitialValue;
- }
-
mInitialized = true;
}
@@ -101,9 +95,11 @@
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 @@
mInitialValue = ConditionState::kFalse;
mSlicedConditionState.clear();
conditionCache[mIndex] = ConditionState::kFalse;
- if (!mSliced) {
- mUnSlicedPart = ConditionState::kFalse;
- }
}
bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -305,9 +298,7 @@
conditionCache[mIndex] =
itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
}
- mUnSlicedPart = conditionCache[mIndex];
}
-
return;
}
@@ -333,18 +324,12 @@
}
conditionCache[mIndex] = overallState;
conditionChangedCache[mIndex] = overallChanged;
- if (!mSliced) {
- mUnSlicedPart = overallState;
- }
}
void SimpleConditionTracker::isConditionMet(
const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+ vector<ConditionState>& conditionCache) const {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
@@ -356,18 +341,13 @@
if (pair == conditionParameters.end()) {
ConditionState conditionState = ConditionState::kNotEvaluated;
- if (dimensionFields.size() > 0 && dimensionFields[0].mMatcher.getTag() == mDimensionTag) {
- conditionState = conditionState | getMetConditionDimension(
- allConditions, dimensionFields, isSubOutputDimensionFields, dimensionsKeySet);
- } else {
- conditionState = conditionState | mInitialValue;
- if (!mSliced) {
- const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
- if (itr != mSlicedConditionState.end()) {
- ConditionState sliceState =
- itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionState = conditionState | sliceState;
- }
+ conditionState = conditionState | mInitialValue;
+ if (!mSliced) {
+ const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+ if (itr != mSlicedConditionState.end()) {
+ ConditionState sliceState =
+ itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
}
}
conditionCache[mIndex] = conditionState;
@@ -385,15 +365,6 @@
slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
if (slice.first.contains(key)) {
conditionState = conditionState | sliceState;
- if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
- if (isSubOutputDimensionFields) {
- HashableDimensionKey dimensionKey;
- filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
- dimensionsKeySet.insert(dimensionKey);
- } else {
- dimensionsKeySet.insert(slice.first);
- }
- }
}
}
} else {
@@ -403,15 +374,6 @@
ConditionState sliceState =
startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
conditionState = conditionState | sliceState;
- if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
- if (isSubOutputDimensionFields) {
- HashableDimensionKey dimensionKey;
- filterValues(dimensionFields, startedCountIt->first.getValues(), &dimensionKey);
- dimensionsKeySet.insert(dimensionKey);
- } else {
- dimensionsKeySet.insert(startedCountIt->first);
- }
- }
}
}
@@ -419,41 +381,6 @@
VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
}
-ConditionState SimpleConditionTracker::getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
- ConditionState conditionState = mInitialValue;
- if (dimensionFields.size() == 0 || mOutputDimensions.size() == 0 ||
- dimensionFields[0].mMatcher.getTag() != mOutputDimensions[0].mMatcher.getTag()) {
- const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
- if (itr != mSlicedConditionState.end()) {
- ConditionState sliceState =
- itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionState = conditionState | sliceState;
- }
- return conditionState;
- }
-
- for (const auto& slice : mSlicedConditionState) {
- ConditionState sliceState =
- slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionState = conditionState | sliceState;
-
- if (sliceState == ConditionState::kTrue && dimensionFields.size() > 0) {
- if (isSubOutputDimensionFields) {
- HashableDimensionKey dimensionKey;
- filterValues(dimensionFields, slice.first.getValues(), &dimensionKey);
- dimensionsKeySet.insert(dimensionKey);
- } else {
- dimensionsKeySet.insert(slice.first);
- }
- }
- }
- return conditionState;
-}
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 47d1ece..ea7f87b 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -37,8 +37,8 @@
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,
@@ -48,17 +48,8 @@
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
const bool isPartialLink,
- std::vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
- ConditionState getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+ std::vector<ConditionState>& conditionCache) const override;
virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
const std::vector<sp<ConditionTracker>>& allConditions) const {
diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateTracker.cpp
deleted file mode 100644
index 1965ce6..0000000
--- a/cmds/statsd/src/condition/StateTracker.cpp
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "StateTracker.h"
-#include "guardrail/StatsdStats.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::string;
-using std::unordered_set;
-using std::vector;
-
-StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int index,
- const SimplePredicate& simplePredicate,
- const unordered_map<int64_t, int>& trackerNameIndexMap,
- const vector<Matcher> primaryKeys)
- : ConditionTracker(id, index), mConfigKey(key), mPrimaryKeys(primaryKeys) {
- if (simplePredicate.has_start()) {
- auto pair = trackerNameIndexMap.find(simplePredicate.start());
- if (pair == trackerNameIndexMap.end()) {
- ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
- return;
- }
- mStartLogMatcherIndex = pair->second;
- mTrackerIndex.insert(mStartLogMatcherIndex);
- } else {
- ALOGW("Condition %lld must have a start matcher", (long long)id);
- return;
- }
-
- if (simplePredicate.has_dimensions()) {
- translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
- if (mOutputDimensions.size() > 0) {
- mSliced = true;
- mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
- } else {
- ALOGW("Condition %lld has invalid dimensions", (long long)id);
- return;
- }
- } else {
- ALOGW("Condition %lld being a state tracker, but has no dimension", (long long)id);
- return;
- }
-
- if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
- mInitialValue = ConditionState::kFalse;
- } else {
- mInitialValue = ConditionState::kUnknown;
- }
-
- mNonSlicedConditionState = mInitialValue;
- mInitialized = true;
-}
-
-StateTracker::~StateTracker() {
- VLOG("~StateTracker()");
-}
-
-bool StateTracker::init(const vector<Predicate>& allConditionConfig,
- const vector<sp<ConditionTracker>>& allConditionTrackers,
- const unordered_map<int64_t, int>& conditionIdIndexMap,
- vector<bool>& stack) {
- return mInitialized;
-}
-
-void StateTracker::dumpState() {
- VLOG("StateTracker %lld DUMP:", (long long)mConditionId);
- for (const auto& value : mSlicedState) {
- VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str());
- }
- VLOG("Last Changed to True: ");
- for (const auto& value : mLastChangedToTrueDimensions) {
- VLOG("%s", value.toString().c_str());
- }
- VLOG("Last Changed to False: ");
- for (const auto& value : mLastChangedToFalseDimensions) {
- VLOG("%s", value.toString().c_str());
- }
-}
-
-bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) {
- if (mSlicedState.find(newKey) != mSlicedState.end()) {
- // if the condition is not sliced or the key is not new, we are good!
- return false;
- }
- // 1. Report the tuple count if the tuple count > soft limit
- if (mSlicedState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mSlicedState.size() + 1;
- StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("Predicate %lld dropping data for dimension key %s",
- (long long)mConditionId, newKey.toString().c_str());
- return true;
- }
- }
- return false;
-}
-
-void StateTracker::evaluateCondition(const LogEvent& event,
- const vector<MatchingState>& eventMatcherValues,
- const vector<sp<ConditionTracker>>& mAllConditions,
- vector<ConditionState>& conditionCache,
- vector<bool>& conditionChangedCache) {
- mLastChangedToTrueDimensions.clear();
- mLastChangedToFalseDimensions.clear();
- if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
- // it has been evaluated.
- VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
- return;
- }
-
- if (mStartLogMatcherIndex >= 0 &&
- eventMatcherValues[mStartLogMatcherIndex] != MatchingState::kMatched) {
- conditionCache[mIndex] =
- mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionChangedCache[mIndex] = false;
- return;
- }
-
- VLOG("StateTracker evaluate event %s", event.ToString().c_str());
-
- // Primary key can exclusive fields must be simple fields. so there won't be more than
- // one keys matched.
- HashableDimensionKey primaryKey;
- HashableDimensionKey state;
- if ((mPrimaryKeys.size() > 0 && !filterValues(mPrimaryKeys, event.getValues(), &primaryKey)) ||
- !filterValues(mOutputDimensions, event.getValues(), &state)) {
- ALOGE("Failed to filter fields in the event?? panic now!");
- conditionCache[mIndex] =
- mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
- conditionChangedCache[mIndex] = false;
- return;
- }
- hitGuardRail(primaryKey);
-
- VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str());
-
- auto it = mSlicedState.find(primaryKey);
- if (it == mSlicedState.end()) {
- mSlicedState[primaryKey] = state;
- conditionCache[mIndex] = ConditionState::kTrue;
- mLastChangedToTrueDimensions.insert(state);
- conditionChangedCache[mIndex] = true;
- } else if (!(it->second == state)) {
- mLastChangedToFalseDimensions.insert(it->second);
- mLastChangedToTrueDimensions.insert(state);
- mSlicedState[primaryKey] = state;
- conditionCache[mIndex] = ConditionState::kTrue;
- conditionChangedCache[mIndex] = true;
- } else {
- conditionCache[mIndex] = ConditionState::kTrue;
- conditionChangedCache[mIndex] = false;
- }
-
- if (DEBUG) {
- dumpState();
- }
- return;
-}
-
-void StateTracker::isConditionMet(
- const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- const bool isPartialLink,
- vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
- if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
- // it has been evaluated.
- VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
- return;
- }
-
- const auto pair = conditionParameters.find(mConditionId);
- if (pair == conditionParameters.end()) {
- if (mSlicedState.size() > 0) {
- conditionCache[mIndex] = ConditionState::kTrue;
-
- for (const auto& state : mSlicedState) {
- dimensionsKeySet.insert(state.second);
- }
- } else {
- conditionCache[mIndex] = ConditionState::kUnknown;
- }
- return;
- }
-
- const auto& primaryKey = pair->second;
- conditionCache[mIndex] = mInitialValue;
- auto it = mSlicedState.find(primaryKey);
- if (it != mSlicedState.end()) {
- conditionCache[mIndex] = ConditionState::kTrue;
- dimensionsKeySet.insert(it->second);
- }
-}
-
-ConditionState StateTracker::getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
- if (mSlicedState.size() > 0) {
- for (const auto& state : mSlicedState) {
- dimensionsKeySet.insert(state.second);
- }
- return ConditionState::kTrue;
- }
-
- return mInitialValue;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateTracker.h
deleted file mode 100644
index 2bdf98c..0000000
--- a/cmds/statsd/src/condition/StateTracker.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma once
-
-#include <gtest/gtest_prod.h>
-#include "ConditionTracker.h"
-#include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StateTracker : public virtual ConditionTracker {
-public:
- StateTracker(const ConfigKey& key, const int64_t& id, const int index,
- const SimplePredicate& simplePredicate,
- const std::unordered_map<int64_t, int>& trackerNameIndexMap,
- const vector<Matcher> primaryKeys);
-
- ~StateTracker();
-
- 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;
-
- void evaluateCondition(const LogEvent& event,
- const std::vector<MatchingState>& eventMatcherValues,
- const std::vector<sp<ConditionTracker>>& mAllConditions,
- std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache) override;
-
- /**
- * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
- * must take the entire dimension fields from StateTracker. This is to make implementation
- * simple and efficient.
- *
- * For example: wakelock duration by uid process states:
- * dimension in condition must be {uid, process state}.
- */
- void isConditionMet(const ConditionKey& conditionParameters,
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- const bool isPartialLink,
- std::vector<ConditionState>& conditionCache,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
- /**
- * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
- * must take the entire dimension fields from StateTracker. This is to make implementation
- * simple and efficient.
- */
- ConditionState getMetConditionDimension(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensionFields,
- const bool isSubOutputDimensionFields,
- std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
-
- virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const {
- return &mLastChangedToTrueDimensions;
- }
-
- virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions) const {
- return &mLastChangedToFalseDimensions;
- }
-
- bool IsChangedDimensionTrackable() const override { return true; }
-
- bool IsSimpleCondition() const override { return true; }
-
- bool equalOutputDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- const vector<Matcher>& dimensions) const override {
- return equalDimensions(mOutputDimensions, dimensions);
- }
-
- void getTrueSlicedDimensions(
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::set<HashableDimensionKey>* dimensions) const override {
- for (const auto& itr : mSlicedState) {
- dimensions->insert(itr.second);
- }
- }
-
-private:
- const ConfigKey mConfigKey;
-
- // The index of the LogEventMatcher which defines the start.
- int mStartLogMatcherIndex;
-
- std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
- std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
-
- std::vector<Matcher> mOutputDimensions;
- std::vector<Matcher> mPrimaryKeys;
-
- ConditionState mInitialValue;
-
- int mDimensionTag;
-
- void dumpState();
-
- bool hitGuardRail(const HashableDimensionKey& newKey);
-
- // maps from [primary_key] to [primary_key, exclusive_state].
- std::unordered_map<HashableDimensionKey, HashableDimensionKey> mSlicedState;
-
- FRIEND_TEST(StateTrackerTest, TestStateChange);
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 13d251a..13020e0 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -42,7 +42,78 @@
using android::base::StringPrintf;
using std::unique_ptr;
-ConfigManager::ConfigManager() {
+struct ConfigReceiverDeathCookie {
+ ConfigReceiverDeathCookie(const wp<ConfigManager>& configManager, const ConfigKey& configKey,
+ const shared_ptr<IPendingIntentRef>& pir) :
+ mConfigManager(configManager), mConfigKey(configKey), mPir(pir) {
+ }
+
+ wp<ConfigManager> mConfigManager;
+ ConfigKey mConfigKey;
+ shared_ptr<IPendingIntentRef> mPir;
+};
+
+void ConfigManager::configReceiverDied(void* cookie) {
+ auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
+ sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+ if (!thiz) {
+ return;
+ }
+
+ ConfigKey& configKey = cookie_->mConfigKey;
+ shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
+
+ // Erase the mapping from the config key to the config receiver (pir) if the
+ // mapping still exists.
+ lock_guard<mutex> lock(thiz->mMutex);
+ auto it = thiz->mConfigReceivers.find(configKey);
+ if (it != thiz->mConfigReceivers.end() && it->second == pir) {
+ thiz->mConfigReceivers.erase(configKey);
+ }
+
+ // The death recipient corresponding to this specific pir can never be
+ // triggered again, so free up resources.
+ delete cookie_;
+}
+
+struct ActiveConfigChangedReceiverDeathCookie {
+ ActiveConfigChangedReceiverDeathCookie(const wp<ConfigManager>& configManager, const int uid,
+ const shared_ptr<IPendingIntentRef>& pir) :
+ mConfigManager(configManager), mUid(uid), mPir(pir) {
+ }
+
+ wp<ConfigManager> mConfigManager;
+ int mUid;
+ shared_ptr<IPendingIntentRef> mPir;
+};
+
+void ConfigManager::activeConfigChangedReceiverDied(void* cookie) {
+ auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
+ sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+ if (!thiz) {
+ return;
+ }
+
+ int uid = cookie_->mUid;
+ shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
+
+ // Erase the mapping from the config key to the active config changed
+ // receiver (pir) if the mapping still exists.
+ lock_guard<mutex> lock(thiz->mMutex);
+ auto it = thiz->mActiveConfigsChangedReceivers.find(uid);
+ if (it != thiz->mActiveConfigsChangedReceivers.end() && it->second == pir) {
+ thiz->mActiveConfigsChangedReceivers.erase(uid);
+ }
+
+ // The death recipient corresponding to this specific pir can never
+ // be triggered again, so free up resources.
+ delete cookie_;
+}
+
+ConfigManager::ConfigManager() :
+ mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)),
+ mActiveConfigChangedReceiverDeathRecipient(
+ AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) {
}
ConfigManager::~ConfigManager() {
@@ -114,9 +185,12 @@
}
}
-void ConfigManager::SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender) {
+void ConfigManager::SetConfigReceiver(const ConfigKey& key,
+ const shared_ptr<IPendingIntentRef>& pir) {
lock_guard<mutex> lock(mMutex);
- mConfigReceivers[key] = intentSender;
+ mConfigReceivers[key] = pir;
+ AIBinder_linkToDeath(pir->asBinder().get(), mConfigReceiverDeathRecipient.get(),
+ new ConfigReceiverDeathCookie(this, key, pir));
}
void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
@@ -125,9 +199,13 @@
}
void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
- const sp<IBinder>& intentSender) {
- lock_guard<mutex> lock(mMutex);
- mActiveConfigsChangedReceivers[uid] = intentSender;
+ const shared_ptr<IPendingIntentRef>& pir) {
+ {
+ lock_guard<mutex> lock(mMutex);
+ mActiveConfigsChangedReceivers[uid] = pir;
+ }
+ AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(),
+ new ActiveConfigChangedReceiverDeathCookie(this, uid, pir));
}
void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) {
@@ -146,25 +224,11 @@
// Remove from map
uidIt->second.erase(key);
- // No more configs for this uid, lets remove the active configs callback.
- if (uidIt->second.empty()) {
- auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid);
- if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) {
- mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver);
- }
- }
-
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
}
- auto itReceiver = mConfigReceivers.find(key);
- if (itReceiver != mConfigReceivers.end()) {
- // Remove from map
- mConfigReceivers.erase(itReceiver);
- }
-
// Remove from disk. There can still be a lingering file on disk so we check
// whether or not the config was on memory.
remove_saved_configs(key);
@@ -195,12 +259,6 @@
// Remove from map
remove_saved_configs(*it);
removed.push_back(*it);
- mConfigReceivers.erase(*it);
- }
-
- auto itActiveConfigsChangedReceiver = mActiveConfigsChangedReceivers.find(uid);
- if (itActiveConfigsChangedReceiver != mActiveConfigsChangedReceivers.end()) {
- mActiveConfigsChangedReceivers.erase(itActiveConfigsChangedReceiver);
}
mConfigs.erase(uidIt);
@@ -234,8 +292,6 @@
uidIt = mConfigs.erase(uidIt);
}
- mConfigReceivers.clear();
- mActiveConfigsChangedReceivers.clear();
for (const sp<ConfigListener>& listener : mListeners) {
broadcastList.push_back(listener);
}
@@ -262,7 +318,7 @@
return ret;
}
-const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
lock_guard<mutex> lock(mMutex);
auto it = mConfigReceivers.find(key);
@@ -273,7 +329,8 @@
}
}
-const sp<android::IBinder> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid)
+ const {
lock_guard<mutex> lock(mMutex);
auto it = mActiveConfigsChangedReceivers.find(uid);
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 1fdec31..bef057f 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -16,16 +16,18 @@
#pragma once
-#include "binder/IBinder.h"
#include "config/ConfigKey.h"
#include "config/ConfigListener.h"
-#include <map>
+#include <aidl/android/os/IPendingIntentRef.h>
#include <mutex>
#include <string>
#include <stdio.h>
+using aidl::android::os::IPendingIntentRef;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
@@ -63,12 +65,12 @@
/**
* Sets the broadcast receiver for a configuration key.
*/
- void SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender);
+ void SetConfigReceiver(const ConfigKey& key, const shared_ptr<IPendingIntentRef>& pir);
/**
* Returns the package name and class name representing the broadcast receiver for this config.
*/
- const sp<android::IBinder> GetConfigReceiver(const ConfigKey& key) const;
+ const shared_ptr<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
/**
* Returns all config keys registered.
@@ -84,13 +86,13 @@
* Sets the broadcast receiver that is notified whenever the list of active configs
* changes for this uid.
*/
- void SetActiveConfigsChangedReceiver(const int uid, const sp<IBinder>& intentSender);
+ void SetActiveConfigsChangedReceiver(const int uid, const shared_ptr<IPendingIntentRef>& pir);
/**
* Returns the broadcast receiver for active configs changed for this uid.
*/
- const sp<IBinder> GetActiveConfigsChangedReceiver(const int uid) const;
+ const shared_ptr<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
/**
* Erase any active configs changed broadcast receiver associated with this uid.
@@ -140,21 +142,38 @@
std::map<int, std::set<ConfigKey>> mConfigs;
/**
- * Each config key can be subscribed by up to one receiver, specified as IBinder from
- * PendingIntent.
+ * Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef.
*/
- std::map<ConfigKey, sp<android::IBinder>> mConfigReceivers;
+ std::map<ConfigKey, shared_ptr<IPendingIntentRef>> mConfigReceivers;
/**
* Each uid can be subscribed by up to one receiver to notify that the list of active configs
- * for this uid has changed. The receiver is specified as IBinder from PendingIntent.
+ * for this uid has changed. The receiver is specified as IPendingIntentRef.
*/
- std::map<int, sp<android::IBinder>> mActiveConfigsChangedReceivers;
+ std::map<int, shared_ptr<IPendingIntentRef>> mActiveConfigsChangedReceivers;
/**
* The ConfigListeners that will be told about changes.
*/
std::vector<sp<ConfigListener>> mListeners;
+
+ // Death recipients that are triggered when the host process holding an
+ // IPendingIntentRef dies.
+ ::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient;
+ ::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient;
+
+ /**
+ * Death recipient callback that is called when a config receiver dies.
+ * The cookie is a pointer to a ConfigReceiverDeathCookie.
+ */
+ static void configReceiverDied(void* cookie);
+
+ /**
+ * Death recipient callback that is called when an active config changed
+ * receiver dies. The cookie is a pointer to an
+ * ActiveConfigChangedReceiverDeathCookie.
+ */
+ static void activeConfigChangedReceiverDied(void* cookie);
};
} // namespace statsd
diff --git a/cmds/statsd/src/experiment_ids.proto b/cmds/statsd/src/experiment_ids.proto
new file mode 100644
index 0000000..c203631
--- /dev/null
+++ b/cmds/statsd/src/experiment_ids.proto
@@ -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.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd;
+
+option java_package = "com.android.internal.os";
+option java_outer_classname = "ExperimentIdsProto";
+
+// StatsLogProcessor uses the proto to parse experiment ids from
+// BinaryPushStateChanged atoms. This needs to be in sync with
+// TrainExperimentIds in atoms.proto.
+message ExperimentIds {
+ repeated int64 experiment_id = 1;
+}
diff --git a/cmds/statsd/src/external/CarStatsPuller.cpp b/cmds/statsd/src/external/CarStatsPuller.cpp
deleted file mode 100644
index 70c0456..0000000
--- a/cmds/statsd/src/external/CarStatsPuller.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false
-#include "Log.h"
-
-#include <binder/IServiceManager.h>
-#include <com/android/internal/car/ICarStatsService.h>
-
-#include "CarStatsPuller.h"
-#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-
-using android::binder::Status;
-using com::android::internal::car::ICarStatsService;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static std::mutex gCarStatsMutex;
-static sp<ICarStatsService> gCarStats = nullptr;
-
-class CarStatsDeathRecipient : public android::IBinder::DeathRecipient {
- public:
- CarStatsDeathRecipient() = default;
- ~CarStatsDeathRecipient() override = default;
-
- // android::IBinder::DeathRecipient override:
- void binderDied(const android::wp<android::IBinder>& /* who */) override {
- ALOGE("Car service has died");
- std::lock_guard<std::mutex> lock(gCarStatsMutex);
- if (gCarStats) {
- sp<IBinder> binder = IInterface::asBinder(gCarStats);
- binder->unlinkToDeath(this);
- gCarStats = nullptr;
- }
- }
-};
-
-static sp<CarStatsDeathRecipient> gDeathRecipient = new CarStatsDeathRecipient();
-
-static sp<ICarStatsService> getCarService() {
- std::lock_guard<std::mutex> lock(gCarStatsMutex);
- if (!gCarStats) {
- const sp<IBinder> binder = defaultServiceManager()->checkService(String16("car_stats"));
- if (!binder) {
- ALOGW("Car service is unavailable");
- return nullptr;
- }
- gCarStats = interface_cast<ICarStatsService>(binder);
- binder->linkToDeath(gDeathRecipient);
- }
- return gCarStats;
-}
-
-CarStatsPuller::CarStatsPuller(const int tagId) : StatsPuller(tagId) {
-}
-
-bool CarStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
- const sp<ICarStatsService> carService = getCarService();
- if (!carService) {
- return false;
- }
-
- vector<StatsLogEventWrapper> returned_value;
- Status status = carService->pullData(mTagId, &returned_value);
- if (!status.isOk()) {
- ALOGW("CarStatsPuller::pull failed for %d", mTagId);
- return false;
- }
-
- data->clear();
- for (const StatsLogEventWrapper& it : returned_value) {
- LogEvent::createLogEvents(it, *data);
- }
- VLOG("CarStatsPuller::pull succeeded for %d", mTagId);
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp
deleted file mode 100644
index 0d3aca0..0000000
--- a/cmds/statsd/src/external/GpuStatsPuller.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GpuStatsPuller.h"
-
-#include <binder/IServiceManager.h>
-#include <graphicsenv/GpuStatsInfo.h>
-#include <graphicsenv/IGpuService.h>
-
-#include "logd/LogEvent.h"
-
-#include "stats_log_util.h"
-#include "statslog.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using android::util::ProtoReader;
-
-GpuStatsPuller::GpuStatsPuller(const int tagId) : StatsPuller(tagId) {
-}
-
-static sp<IGpuService> getGpuService() {
- const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
- if (!binder) {
- ALOGE("Failed to get gpu service");
- return nullptr;
- }
-
- return interface_cast<IGpuService>(binder);
-}
-
-static bool pullGpuStatsGlobalInfo(const sp<IGpuService>& gpuService,
- std::vector<std::shared_ptr<LogEvent>>* data) {
- std::vector<GpuStatsGlobalInfo> stats;
- status_t status = gpuService->getGpuStatsGlobalInfo(&stats);
- if (status != OK) {
- return false;
- }
-
- data->clear();
- data->reserve(stats.size());
- for (const auto& info : stats) {
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(
- android::util::GPU_STATS_GLOBAL_INFO, getWallClockNs(), getElapsedRealtimeNs());
- if (!event->write(info.driverPackageName)) return false;
- if (!event->write(info.driverVersionName)) return false;
- if (!event->write((int64_t)info.driverVersionCode)) return false;
- if (!event->write(info.driverBuildTime)) return false;
- if (!event->write((int64_t)info.glLoadingCount)) return false;
- if (!event->write((int64_t)info.glLoadingFailureCount)) return false;
- if (!event->write((int64_t)info.vkLoadingCount)) return false;
- if (!event->write((int64_t)info.vkLoadingFailureCount)) return false;
- if (!event->write(info.vulkanVersion)) return false;
- if (!event->write(info.cpuVulkanVersion)) return false;
- if (!event->write(info.glesVersion)) return false;
- if (!event->write((int64_t)info.angleLoadingCount)) return false;
- if (!event->write((int64_t)info.angleLoadingFailureCount)) return false;
- event->init();
- data->emplace_back(event);
- }
-
- return true;
-}
-
-static bool pullGpuStatsAppInfo(const sp<IGpuService>& gpuService,
- std::vector<std::shared_ptr<LogEvent>>* data) {
- std::vector<GpuStatsAppInfo> stats;
- status_t status = gpuService->getGpuStatsAppInfo(&stats);
- if (status != OK) {
- return false;
- }
-
- data->clear();
- data->reserve(stats.size());
- for (const auto& info : stats) {
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(
- android::util::GPU_STATS_APP_INFO, getWallClockNs(), getElapsedRealtimeNs());
- if (!event->write(info.appPackageName)) return false;
- if (!event->write((int64_t)info.driverVersionCode)) return false;
- if (!event->write(int64VectorToProtoByteString(info.glDriverLoadingTime))) return false;
- if (!event->write(int64VectorToProtoByteString(info.vkDriverLoadingTime))) return false;
- if (!event->write(int64VectorToProtoByteString(info.angleDriverLoadingTime))) return false;
- if (!event->write(info.cpuVulkanInUse)) return false;
- event->init();
- data->emplace_back(event);
- }
-
- return true;
-}
-
-bool GpuStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) {
- const sp<IGpuService> gpuService = getGpuService();
- if (!gpuService) {
- return false;
- }
-
- switch (mTagId) {
- case android::util::GPU_STATS_GLOBAL_INFO:
- return pullGpuStatsGlobalInfo(gpuService, data);
- case android::util::GPU_STATS_APP_INFO:
- return pullGpuStatsAppInfo(gpuService, data);
- default:
- break;
- }
-
- return false;
-}
-
-static std::string protoOutputStreamToByteString(ProtoOutputStream& proto) {
- if (!proto.size()) return "";
-
- std::string byteString;
- sp<ProtoReader> reader = proto.data();
- while (reader->readBuffer() != nullptr) {
- const size_t toRead = reader->currentToRead();
- byteString.append((char*)reader->readBuffer(), toRead);
- reader->move(toRead);
- }
-
- if (byteString.size() != proto.size()) return "";
-
- return byteString;
-}
-
-std::string int64VectorToProtoByteString(const std::vector<int64_t>& value) {
- if (value.empty()) return "";
-
- ProtoOutputStream proto;
- for (const auto& ele : value) {
- proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
- 1 /* field id */,
- (long long)ele);
- }
-
- return protoOutputStreamToByteString(proto);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/GpuStatsPuller.h b/cmds/statsd/src/external/GpuStatsPuller.h
deleted file mode 100644
index 2da199c..0000000
--- a/cmds/statsd/src/external/GpuStatsPuller.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Pull GpuStats from GpuService.
- */
-class GpuStatsPuller : public StatsPuller {
-public:
- explicit GpuStatsPuller(const int tagId);
- bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
-};
-
-// convert a int64_t vector into a byte string for proto message like:
-// message RepeatedInt64Wrapper {
-// repeated int64 value = 1;
-// }
-std::string int64VectorToProtoByteString(const std::vector<int64_t>& value);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
index ab2c195..095782a 100644
--- a/cmds/statsd/src/external/Perfetto.h
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -16,10 +16,6 @@
#pragma once
-#include <android/os/StatsLogEventWrapper.h>
-
-using android::os::StatsLogEventWrapper;
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/external/PowerStatsPuller.cpp b/cmds/statsd/src/external/PowerStatsPuller.cpp
deleted file mode 100644
index dc69b78..0000000
--- a/cmds/statsd/src/external/PowerStatsPuller.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-
-#include <vector>
-
-#include "PowerStatsPuller.h"
-#include "statslog.h"
-#include "stats_log_util.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::power::stats::V1_0::IPowerStats;
-using android::hardware::power::stats::V1_0::EnergyData;
-using android::hardware::power::stats::V1_0::RailInfo;
-using android::hardware::power::stats::V1_0::Status;
-using android::hardware::Return;
-using android::hardware::Void;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
-static std::mutex gPowerStatsHalMutex;
-static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
-static std::vector<RailInfo> gRailInfo;
-
-struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override {
- // The HAL just died. Reset all handles to HAL services.
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
- gPowerStatsHal = nullptr;
- }
-};
-
-static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient();
-
-static bool getPowerStatsHalLocked() {
- if (gPowerStatsHal == nullptr && gPowerStatsExist) {
- gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService();
- if (gPowerStatsHal == nullptr) {
- ALOGW("Couldn't load power.stats HAL service");
- gPowerStatsExist = false;
- } else {
- // Link death recipient to power.stats service handle
- hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power.stats HAL death: %s",
- linked.description().c_str());
- gPowerStatsHal = nullptr;
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power.stats HAL death notifications");
- // We should still continue even though linking failed
- }
- }
- }
- return gPowerStatsHal != nullptr;
-}
-
-PowerStatsPuller::PowerStatsPuller() : StatsPuller(android::util::ON_DEVICE_POWER_MEASUREMENT) {
-}
-
-bool PowerStatsPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!getPowerStatsHalLocked()) {
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
- data->clear();
-
- // Pull getRailInfo if necessary
- if (gRailInfo.empty()) {
- bool resultSuccess = true;
- Return<void> ret = gPowerStatsHal->getRailInfo(
- [&resultSuccess](const hidl_vec<RailInfo> &list, Status status) {
- resultSuccess = (status == Status::SUCCESS || status == Status::NOT_SUPPORTED);
- if (status != Status::SUCCESS) return;
-
- gRailInfo.reserve(list.size());
- for (size_t i = 0; i < list.size(); ++i) {
- gRailInfo.push_back(list[i]);
- }
- });
- if (!resultSuccess || !ret.isOk()) {
- ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
- gPowerStatsHal = nullptr;
- return false;
- }
- // If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
- if (gRailInfo.empty()) {
- ALOGE("power.stats has no rail information");
- gPowerStatsExist = false; // No rail info, so never try again.
- gPowerStatsHal = nullptr;
- return false;
- }
- }
-
- // Pull getEnergyData and write the data out
- const hidl_vec<uint32_t> desiredRailIndices; // Empty vector indicates we want all.
- bool resultSuccess = true;
- Return<void> ret = gPowerStatsHal->getEnergyData(desiredRailIndices,
- [&data, wallClockTimestampNs, elapsedTimestampNs, &resultSuccess]
- (hidl_vec<EnergyData> energyDataList, Status status) {
- resultSuccess = (status == Status::SUCCESS);
- if (!resultSuccess) return;
-
- for (size_t i = 0; i < energyDataList.size(); i++) {
- const EnergyData& energyData = energyDataList[i];
-
- if (energyData.index >= gRailInfo.size()) {
- ALOGE("power.stats getEnergyData() returned an invalid rail index %u.",
- energyData.index);
- resultSuccess = false;
- return;
- }
- const RailInfo& rail = gRailInfo[energyData.index];
-
- auto ptr = make_shared<LogEvent>(android::util::ON_DEVICE_POWER_MEASUREMENT,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(rail.subsysName);
- ptr->write(rail.railName);
- ptr->write(energyData.timestamp);
- ptr->write(energyData.energy);
- ptr->init();
- data->push_back(ptr);
-
- VLOG("power.stat: %s.%s: %llu, %llu",
- rail.subsysName.c_str(),
- rail.railName.c_str(),
- (unsigned long long)energyData.timestamp,
- (unsigned long long)energyData.energy);
- }
- });
- if (!resultSuccess || !ret.isOk()) {
- ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
- gPowerStatsHal = nullptr;
- return false;
- }
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/PowerStatsPuller.h b/cmds/statsd/src/external/PowerStatsPuller.h
deleted file mode 100644
index 6f15bd6..0000000
--- a/cmds/statsd/src/external/PowerStatsPuller.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Reads hal for power.stats
- */
-class PowerStatsPuller : public StatsPuller {
-public:
- PowerStatsPuller();
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/PullResultReceiver.cpp b/cmds/statsd/src/external/PullResultReceiver.cpp
new file mode 100644
index 0000000..8aa4792
--- /dev/null
+++ b/cmds/statsd/src/external/PullResultReceiver.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PullResultReceiver.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+PullResultReceiver::PullResultReceiver(
+ std::function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCb)
+ : pullFinishCallback(std::move(pullFinishCb)) {
+}
+
+Status PullResultReceiver::pullFinished(int32_t atomTag, bool success,
+ const vector<StatsEventParcel>& output) {
+ pullFinishCallback(atomTag, success, output);
+ return Status::ok();
+}
+
+PullResultReceiver::~PullResultReceiver() {
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/PullResultReceiver.h b/cmds/statsd/src/external/PullResultReceiver.h
new file mode 100644
index 0000000..ceaae80
--- /dev/null
+++ b/cmds/statsd/src/external/PullResultReceiver.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/os/BnPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
+
+using namespace std;
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnPullAtomResultReceiver;
+using aidl::android::util::StatsEventParcel;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PullResultReceiver : public BnPullAtomResultReceiver {
+public:
+ PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)>
+ pullFinishCallback);
+ ~PullResultReceiver();
+
+ /**
+ * Binder call for finishing a pull.
+ */
+ Status pullFinished(int32_t atomTag, bool success,
+ const vector<StatsEventParcel>& output) override;
+
+private:
+ function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/CarStatsPuller.h b/cmds/statsd/src/external/PullUidProvider.h
similarity index 69%
rename from cmds/statsd/src/external/CarStatsPuller.h
rename to cmds/statsd/src/external/PullUidProvider.h
index ca0f1a9..2318c50 100644
--- a/cmds/statsd/src/external/CarStatsPuller.h
+++ b/cmds/statsd/src/external/PullUidProvider.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -13,22 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#pragma once
+#include <utils/RefBase.h>
+
#include "StatsPuller.h"
+#include "logd/LogEvent.h"
namespace android {
namespace os {
namespace statsd {
-/**
- * Pull atoms from CarService.
- */
-class CarStatsPuller : public StatsPuller {
+class PullUidProvider : virtual public RefBase {
public:
- explicit CarStatsPuller(const int tagId);
- bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override;
+ virtual ~PullUidProvider() {}
+
+ /**
+ * @param atomId The atom for which to get the uids.
+ */
+ virtual vector<int32_t> getPullAtomUids(int32_t atomId) = 0;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
deleted file mode 100644
index 75b63f4..0000000
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/health/2.0/IHealth.h>
-#include <healthhalutils/HealthHalUtils.h>
-#include "external/ResourceHealthManagerPuller.h"
-#include "external/StatsPuller.h"
-
-#include "ResourceHealthManagerPuller.h"
-#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-#include "statslog.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-using android::hardware::Void;
-using android::hardware::health::V2_0::get_health_service;
-using android::hardware::health::V2_0::HealthInfo;
-using android::hardware::health::V2_0::IHealth;
-using android::hardware::health::V2_0::Result;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-sp<android::hardware::health::V2_0::IHealth> gHealthHal = nullptr;
-
-bool getHealthHal() {
- if (gHealthHal == nullptr) {
- gHealthHal = get_health_service();
- }
- return gHealthHal != nullptr;
-}
-
-ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) {
-}
-
-// TODO(b/110565992): add other health atoms (eg. Temperature).
-bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- if (!getHealthHal()) {
- ALOGE("Health Hal not loaded");
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
- data->clear();
- bool result_success = true;
-
- // Get the data from the Health HAL (hardware/interfaces/health/1.0/types.hal).
- Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) {
- if (r != Result::SUCCESS) {
- result_success = false;
- return;
- }
- if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) {
- auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(v.legacy.batteryChargeCounter);
- ptr->init();
- data->push_back(ptr);
- } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) {
- auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(v.legacy.batteryFullCharge);
- ptr->init();
- data->push_back(ptr);
- } else if (mTagId == android::util::BATTERY_VOLTAGE) {
- auto ptr = make_shared<LogEvent>(android::util::BATTERY_VOLTAGE, wallClockTimestampNs,
- elapsedTimestampNs);
- ptr->write(v.legacy.batteryVoltage);
- ptr->init();
- data->push_back(ptr);
- } else if (mTagId == android::util::BATTERY_LEVEL) {
- auto ptr = make_shared<LogEvent>(android::util::BATTERY_LEVEL, wallClockTimestampNs,
- elapsedTimestampNs);
- ptr->write(v.legacy.batteryLevel);
- ptr->init();
- data->push_back(ptr);
- } else if (mTagId == android::util::BATTERY_CYCLE_COUNT) {
- auto ptr = make_shared<LogEvent>(android::util::BATTERY_CYCLE_COUNT,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(v.legacy.batteryCycleCount);
- ptr->init();
- data->push_back(ptr);
- } else {
- ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
- }
- });
- if (!result_success || !ret.isOk()) {
- ALOGE("getHealthHal() failed: health HAL service not available. Description: %s",
- ret.description().c_str());
- if (!ret.isOk() && ret.isDeadObject()) {
- gHealthHal = nullptr;
- }
- return false;
- }
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
deleted file mode 100644
index f650fcc..0000000
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/String16.h>
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Reads Ihealth.hal
- */
-class ResourceHealthManagerPuller : public StatsPuller {
-public:
- explicit ResourceHealthManagerPuller(int tagId);
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index d718273..78e6f09 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -17,43 +17,98 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include <android/os/IStatsPullerCallback.h>
-
#include "StatsCallbackPuller.h"
+#include "PullResultReceiver.h"
+#include "StatsPullerManager.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
-using namespace android::binder;
+#include <aidl/android/util/StatsEventParcel.h>
+
+using namespace std;
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
namespace android {
namespace os {
namespace statsd {
-StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IStatsPullerCallback>& callback) :
- StatsPuller(tagId), mCallback(callback) {
- VLOG("StatsCallbackPuller created for tag %d", tagId);
+StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
+ const int64_t coolDownNs, int64_t timeoutNs,
+ const vector<int> additiveFields)
+ : StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) {
+ VLOG("StatsCallbackPuller created for tag %d", tagId);
}
bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- VLOG("StatsCallbackPuller called for tag %d", mTagId)
+ VLOG("StatsCallbackPuller called for tag %d", mTagId);
if(mCallback == nullptr) {
ALOGW("No callback registered");
return false;
}
- int64_t wallClockTimeNs = getWallClockNs();
- int64_t elapsedTimeNs = getElapsedRealtimeNs();
- vector<StatsLogEventWrapper> returned_value;
- Status status = mCallback->pullData(mTagId, elapsedTimeNs, wallClockTimeNs, &returned_value);
+
+ // Shared variables needed in the result receiver.
+ shared_ptr<mutex> cv_mutex = make_shared<mutex>();
+ shared_ptr<condition_variable> cv = make_shared<condition_variable>();
+ shared_ptr<bool> pullFinish = make_shared<bool>(false);
+ shared_ptr<bool> pullSuccess = make_shared<bool>(false);
+ shared_ptr<vector<shared_ptr<LogEvent>>> sharedData =
+ make_shared<vector<shared_ptr<LogEvent>>>();
+
+ shared_ptr<PullResultReceiver> resultReceiver = SharedRefBase::make<PullResultReceiver>(
+ [cv_mutex, cv, pullFinish, pullSuccess, sharedData](
+ int32_t atomTag, bool success, const vector<StatsEventParcel>& output) {
+ // This is the result of the pull, executing in a statsd binder thread.
+ // The pull could have taken a long time, and we should only modify
+ // data (the output param) if the pointer is in scope and the pull did not time out.
+ {
+ lock_guard<mutex> lk(*cv_mutex);
+ for (const StatsEventParcel& parcel: output) {
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1);
+ bool valid = event->parseBuffer((uint8_t*)parcel.buffer.data(),
+ parcel.buffer.size());
+ if (valid) {
+ sharedData->push_back(event);
+ } else {
+ StatsdStats::getInstance().noteAtomError(event->GetTagId(),
+ /*pull=*/true);
+ }
+ }
+ *pullSuccess = success;
+ *pullFinish = true;
+ }
+ cv->notify_one();
+ });
+
+ // Initiate the pull. This is a oneway call to a different process, except
+ // in unit tests. In process calls are not oneway.
+ Status status = mCallback->onPullAtom(mTagId, resultReceiver);
if (!status.isOk()) {
- ALOGW("StatsCallbackPuller::pull failed for %d", mTagId);
+ StatsdStats::getInstance().notePullBinderCallFailed(mTagId);
return false;
}
- data->clear();
- for (const StatsLogEventWrapper& it: returned_value) {
- LogEvent::createLogEvents(it, *data);
+
+ {
+ unique_lock<mutex> unique_lk(*cv_mutex);
+ // Wait until the pull finishes, or until the pull timeout.
+ cv->wait_for(unique_lk, chrono::nanoseconds(mPullTimeoutNs),
+ [pullFinish] { return *pullFinish; });
+ if (!*pullFinish) {
+ // Note: The parent stats puller will also note that there was a timeout and that the
+ // cache should be cleared. Once we migrate all pullers to this callback, we could
+ // consolidate the logic.
+ return true;
+ } else {
+ // Only copy the data if we did not timeout and the pull was successful.
+ if (*pullSuccess) {
+ *data = std::move(*sharedData);
+ }
+ VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId);
+ return *pullSuccess;
+ }
}
- VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId);
- return true;
}
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h
index 03e78be..e82e8bb 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.h
+++ b/cmds/statsd/src/external/StatsCallbackPuller.h
@@ -16,21 +16,29 @@
#pragma once
-#include <android/os/IStatsPullerCallback.h>
-
+#include <aidl/android/os/IPullAtomCallback.h>
#include "StatsPuller.h"
+using aidl::android::os::IPullAtomCallback;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
class StatsCallbackPuller : public StatsPuller {
public:
- explicit StatsCallbackPuller(int tagId, const sp<IStatsPullerCallback>& callback);
+ explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
+ const int64_t coolDownNs, const int64_t timeoutNs,
+ const std::vector<int> additiveFields);
private:
- bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
- const sp<IStatsPullerCallback> mCallback;
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+ const shared_ptr<IPullAtomCallback> mCallback;
+
+ FRIEND_TEST(StatsCallbackPullerTest, PullFail);
+ FRIEND_TEST(StatsCallbackPullerTest, PullSuccess);
+ FRIEND_TEST(StatsCallbackPullerTest, PullTimeout);
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
deleted file mode 100644
index f37d2be..0000000
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false
-#include "Log.h"
-
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IPCThreadState.h>
-#include <private/android_filesystem_config.h>
-#include "../stats_log_util.h"
-#include "../statscompanion_util.h"
-#include "StatsCompanionServicePuller.h"
-
-using namespace android;
-using namespace android::base;
-using namespace android::binder;
-using namespace android::os;
-using std::make_shared;
-using std::shared_ptr;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
-// let StatsCompanionService handle that and send the data back.
-StatsCompanionServicePuller::StatsCompanionServicePuller(int tagId) : StatsPuller(tagId) {
-}
-
-void StatsCompanionServicePuller::SetStatsCompanionService(
- sp<IStatsCompanionService> statsCompanionService) {
- AutoMutex _l(mStatsCompanionServiceLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
- mStatsCompanionService = statsCompanionService;
-}
-
-bool StatsCompanionServicePuller::PullInternal(vector<shared_ptr<LogEvent> >* data) {
- sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
- if (statsCompanionServiceCopy != nullptr) {
- vector<StatsLogEventWrapper> returned_value;
- Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value);
- if (!status.isOk()) {
- ALOGW("StatsCompanionServicePuller::pull failed for %d", mTagId);
- StatsdStats::getInstance().noteStatsCompanionPullFailed(mTagId);
- if (status.exceptionCode() == Status::Exception::EX_TRANSACTION_FAILED) {
- StatsdStats::getInstance().noteStatsCompanionPullBinderTransactionFailed(mTagId);
- }
- return false;
- }
- data->clear();
- for (const StatsLogEventWrapper& it : returned_value) {
- LogEvent::createLogEvents(it, *data);
- }
- VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
- return true;
- } else {
- ALOGW("statsCompanion not found!");
- return false;
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.h b/cmds/statsd/src/external/StatsCompanionServicePuller.h
deleted file mode 100644
index 2e13320..0000000
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/String16.h>
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class StatsCompanionServicePuller : public StatsPuller {
-public:
- explicit StatsCompanionServicePuller(int tagId);
-
- void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) override;
-
-private:
- Mutex mStatsCompanionServiceLock;
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
- bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 9552c0a..bb5d0a6 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -32,50 +32,64 @@
sp<UidMap> StatsPuller::mUidMap = nullptr;
void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
-StatsPuller::StatsPuller(const int tagId)
- : mTagId(tagId), mLastPullTimeNs(0) {
+StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_t pullTimeoutNs,
+ const std::vector<int> additiveFields)
+ : mTagId(tagId),
+ mPullTimeoutNs(pullTimeoutNs),
+ mCoolDownNs(coolDownNs),
+ mAdditiveFields(additiveFields),
+ mLastPullTimeNs(0),
+ mLastEventTimeNs(0) {
}
-bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
+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 = elapsedTimeNs - mLastPullTimeNs <
- StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs;
+ const bool shouldUseCache =
+ (mLastEventTimeNs == eventTimeNs) || (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs);
if (shouldUseCache) {
if (mHasGoodData) {
(*data) = mCachedData;
StatsdStats::getInstance().notePullFromCache(mTagId);
+
}
return mHasGoodData;
}
-
if (mLastPullTimeNs > 0) {
StatsdStats::getInstance().updateMinPullIntervalSec(
mTagId, (elapsedTimeNs - mLastPullTimeNs) / NS_PER_SEC);
}
mCachedData.clear();
mLastPullTimeNs = elapsedTimeNs;
+ mLastEventTimeNs = eventTimeNs;
mHasGoodData = PullInternal(&mCachedData);
if (!mHasGoodData) {
return mHasGoodData;
}
- const int64_t pullDurationNs = getElapsedRealtimeNs() - elapsedTimeNs;
- StatsdStats::getInstance().notePullTime(mTagId, pullDurationNs);
- const bool pullTimeOut =
- pullDurationNs > StatsPullerManager::kAllPullAtomInfo.at(mTagId).pullTimeoutNs;
+ 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.
- clearCacheLocked();
+ 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;
}
if (mCachedData.size() > 0) {
- mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+ mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId, mAdditiveFields);
+ }
+
+ if (mCachedData.empty()) {
+ VLOG("Data pulled is empty");
+ StatsdStats::getInstance().noteEmptyData(mTagId);
}
(*data) = mCachedData;
@@ -95,12 +109,12 @@
int ret = mCachedData.size();
mCachedData.clear();
mLastPullTimeNs = 0;
+ mLastEventTimeNs = 0;
return ret;
}
int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
- if (timestampNs - mLastPullTimeNs >
- StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs) {
+ if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
return clearCache();
} else {
return 0;
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index c83c4f8..470d15e 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -16,7 +16,7 @@
#pragma once
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
#include <mutex>
#include <vector>
@@ -26,13 +26,19 @@
#include "logd/LogEvent.h"
#include "puller_util.h"
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
class StatsPuller : public virtual RefBase {
public:
- explicit StatsPuller(const int tagId);
+ explicit StatsPuller(const int tagId,
+ const int64_t coolDownNs = NS_PER_SEC,
+ const int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs,
+ const std::vector<int> additiveFields = std::vector<int>());
virtual ~StatsPuller() {}
@@ -45,7 +51,7 @@
// 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
// If a metric wants to make any change to the data, like timestamps, it
// should make a copy as this data may be shared with multiple metrics.
- bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+ bool Pull(const int64_t eventTimeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
int ForceClearCache();
@@ -55,11 +61,18 @@
static void SetUidMap(const sp<UidMap>& uidMap);
- virtual void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService){};
+ virtual void SetStatsCompanionService(
+ shared_ptr<IStatsCompanionService> statsCompanionService) {};
protected:
const int mTagId;
+ // Max time allowed to pull this atom.
+ // We cannot reliably kill a pull thread. So we don't terminate the puller.
+ // The data is discarded if the pull takes longer than this and mHasGoodData
+ // marked as false.
+ const int64_t mPullTimeoutNs = StatsdStats::kPullMaxDelayNs;
+
private:
mutable std::mutex mLock;
@@ -68,8 +81,24 @@
bool mHasGoodData = false;
+ // Minimum time before this puller does actual pull again.
+ // Pullers can cause significant impact to system health and battery.
+ // So that we don't pull too frequently.
+ // If a pull request comes before cooldown, a cached version from previous pull
+ // will be returned.
+ const int64_t mCoolDownNs = 1 * NS_PER_SEC;
+
+ // The field numbers of the fields that need to be summed when merging
+ // isolated uid with host uid.
+ const std::vector<int> mAdditiveFields;
+
int64_t mLastPullTimeNs;
+ // All pulls happen due to an event (app upgrade, bucket boundary, condition change, etc).
+ // If multiple pulls need to be done at the same event time, we will always use the cache after
+ // the first pull.
+ int64_t mLastEventTimeNs;
+
// Cache of data from last pull. If next request comes before cool down finishes,
// cached data will be returned.
// Cached data is cleared when
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 0b0e4f6..8a9ec74 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -17,26 +17,22 @@
#define DEBUG false
#include "Log.h"
-#include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsPullerCallback.h>
+#include "StatsPullerManager.h"
+
#include <cutils/log.h>
#include <math.h>
#include <stdint.h>
+
#include <algorithm>
+#include <iostream>
+
#include "../StatsService.h"
#include "../logd/LogEvent.h"
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
-#include "CarStatsPuller.h"
-#include "GpuStatsPuller.h"
-#include "PowerStatsPuller.h"
-#include "ResourceHealthManagerPuller.h"
#include "StatsCallbackPuller.h"
-#include "StatsCompanionServicePuller.h"
-#include "StatsPullerManager.h"
-#include "SubsystemSleepStatePuller.h"
#include "TrainInfoPuller.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
using std::shared_ptr;
using std::vector;
@@ -45,254 +41,132 @@
namespace os {
namespace statsd {
+// Stores the puller as a wp to avoid holding a reference in case it is unregistered and
+// pullAtomCallbackDied is never called.
+struct PullAtomCallbackDeathCookie {
+ PullAtomCallbackDeathCookie(const wp<StatsPullerManager>& pullerManager,
+ const PullerKey& pullerKey, const wp<StatsPuller>& puller) :
+ mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
+ }
+
+ wp<StatsPullerManager> mPullerManager;
+ PullerKey mPullerKey;
+ wp<StatsPuller> mPuller;
+};
+
+void StatsPullerManager::pullAtomCallbackDied(void* cookie) {
+ PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie);
+ sp<StatsPullerManager> thiz = cookie_->mPullerManager.promote();
+ if (!thiz) {
+ return;
+ }
+
+ const PullerKey& pullerKey = cookie_->mPullerKey;
+ wp<StatsPuller> puller = cookie_->mPuller;
+
+ // Erase the mapping from the puller key to the puller if the mapping still exists.
+ // Note that we are removing the StatsPuller object, which internally holds the binder
+ // IPullAtomCallback. However, each new registration creates a new StatsPuller, so this works.
+ lock_guard<mutex> lock(thiz->mLock);
+ const auto& it = thiz->kAllPullAtomInfo.find(pullerKey);
+ if (it != thiz->kAllPullAtomInfo.end() && puller != nullptr && puller == it->second) {
+ StatsdStats::getInstance().notePullerCallbackRegistrationChanged(pullerKey.atomTag,
+ /*registered=*/false);
+ thiz->kAllPullAtomInfo.erase(pullerKey);
+ }
+ // The death recipient corresponding to this specific IPullAtomCallback can never
+ // be triggered again, so free up resources.
+ delete cookie_;
+}
+
// Values smaller than this may require to update the alarm.
const int64_t NO_ALARM_UPDATE = INT64_MAX;
-std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
- // wifi_bytes_transfer
- {android::util::WIFI_BYTES_TRANSFER,
- {.additiveFields = {2, 3, 4, 5},
- .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
- // wifi_bytes_transfer_by_fg_bg
- {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
- {.additiveFields = {3, 4, 5, 6},
- .puller = new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
- // mobile_bytes_transfer
- {android::util::MOBILE_BYTES_TRANSFER,
- {.additiveFields = {2, 3, 4, 5},
- .puller = new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
- // mobile_bytes_transfer_by_fg_bg
- {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
- {.additiveFields = {3, 4, 5, 6},
- .puller =
- new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
- // bluetooth_bytes_transfer
- {android::util::BLUETOOTH_BYTES_TRANSFER,
- {.additiveFields = {2, 3},
- .puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
- // kernel_wakelock
- {android::util::KERNEL_WAKELOCK,
- {.puller = new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
- // subsystem_sleep_state
- {android::util::SUBSYSTEM_SLEEP_STATE, {.puller = new SubsystemSleepStatePuller()}},
- // on_device_power_measurement
- {android::util::ON_DEVICE_POWER_MEASUREMENT, {.puller = new PowerStatsPuller()}},
- // cpu_time_per_freq
- {android::util::CPU_TIME_PER_FREQ,
- {.additiveFields = {3},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
- // cpu_time_per_uid
- {android::util::CPU_TIME_PER_UID,
- {.additiveFields = {2, 3},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
- // cpu_time_per_uid_freq
- // the throttling is 3sec, handled in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- {android::util::CPU_TIME_PER_UID_FREQ,
- {.additiveFields = {4},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
- // cpu_active_time
- // the throttling is 3sec, handled in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- {android::util::CPU_ACTIVE_TIME,
- {.additiveFields = {2},
- .puller = new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
- // cpu_cluster_time
- // the throttling is 3sec, handled in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- {android::util::CPU_CLUSTER_TIME,
- {.additiveFields = {3},
- .puller = new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
- // wifi_activity_energy_info
- {android::util::WIFI_ACTIVITY_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
- // modem_activity_info
- {android::util::MODEM_ACTIVITY_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
- // bluetooth_activity_info
- {android::util::BLUETOOTH_ACTIVITY_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
- // system_elapsed_realtime
- {android::util::SYSTEM_ELAPSED_REALTIME,
- {.coolDownNs = NS_PER_SEC,
- .puller = new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME),
- .pullTimeoutNs = NS_PER_SEC / 2,
- }},
- // system_uptime
- {android::util::SYSTEM_UPTIME,
- {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
- // remaining_battery_capacity
- {android::util::REMAINING_BATTERY_CAPACITY,
- {.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
- // full_battery_capacity
- {android::util::FULL_BATTERY_CAPACITY,
- {.puller = new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
- // battery_voltage
- {android::util::BATTERY_VOLTAGE,
- {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
- // battery_level
- {android::util::BATTERY_LEVEL,
- {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
- // battery_cycle_count
- {android::util::BATTERY_CYCLE_COUNT,
- {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}},
- // process_memory_state
- {android::util::PROCESS_MEMORY_STATE,
- {.additiveFields = {4, 5, 6, 7, 8, 9},
- .puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
- // native_process_memory_state
- {android::util::NATIVE_PROCESS_MEMORY_STATE,
- {.additiveFields = {3, 4, 5, 6, 8},
- .puller = new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
- // process_memory_high_water_mark
- {android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
- {.additiveFields = {3},
- .puller =
- new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
- // system_ion_heap_size
- {android::util::SYSTEM_ION_HEAP_SIZE,
- {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}},
- // process_system_ion_heap_size
- {android::util::PROCESS_SYSTEM_ION_HEAP_SIZE,
- {.puller = new StatsCompanionServicePuller(android::util::PROCESS_SYSTEM_ION_HEAP_SIZE)}},
- // temperature
- {android::util::TEMPERATURE,
- {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}},
- // cooling_device
- {android::util::COOLING_DEVICE,
- {.puller = new StatsCompanionServicePuller(android::util::COOLING_DEVICE)}},
- // binder_calls
- {android::util::BINDER_CALLS,
- {.additiveFields = {4, 5, 6, 8, 12},
- .puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
- // binder_calls_exceptions
- {android::util::BINDER_CALLS_EXCEPTIONS,
- {.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
- // looper_stats
- {android::util::LOOPER_STATS,
- {.additiveFields = {5, 6, 7, 8, 9},
- .puller = new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
- // Disk Stats
- {android::util::DISK_STATS,
- {.puller = new StatsCompanionServicePuller(android::util::DISK_STATS)}},
- // Directory usage
- {android::util::DIRECTORY_USAGE,
- {.puller = new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
- // Size of app's code, data, and cache
- {android::util::APP_SIZE,
- {.puller = new StatsCompanionServicePuller(android::util::APP_SIZE)}},
- // Size of specific categories of files. Eg. Music.
- {android::util::CATEGORY_SIZE,
- {.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
- // Number of fingerprints enrolled for each user.
- {android::util::NUM_FINGERPRINTS_ENROLLED,
- {.puller = new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS_ENROLLED)}},
- // Number of faces enrolled for each user.
- {android::util::NUM_FACES_ENROLLED,
- {.puller = new StatsCompanionServicePuller(android::util::NUM_FACES_ENROLLED)}},
- // ProcStats.
- {android::util::PROC_STATS,
- {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}},
- // ProcStatsPkgProc.
- {android::util::PROC_STATS_PKG_PROC,
- {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
- // Disk I/O stats per uid.
- {android::util::DISK_IO,
- {.additiveFields = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
- .coolDownNs = 3 * NS_PER_SEC,
- .puller = new StatsCompanionServicePuller(android::util::DISK_IO)}},
- // PowerProfile constants for power model calculations.
- {android::util::POWER_PROFILE,
- {.puller = new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
- // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
- {android::util::PROCESS_CPU_TIME,
- {.coolDownNs = 5 * NS_PER_SEC /* min cool-down in seconds*/,
- .puller = new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
- {android::util::CPU_TIME_PER_THREAD_FREQ,
- {.additiveFields = {7, 9, 11, 13, 15, 17, 19, 21},
- .puller = new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
- // DeviceCalculatedPowerUse.
- {android::util::DEVICE_CALCULATED_POWER_USE,
- {.puller = new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
- // DeviceCalculatedPowerBlameUid.
- {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
- {.puller = new StatsCompanionServicePuller(
- android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
- // DeviceCalculatedPowerBlameOther.
- {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
- {.puller = new StatsCompanionServicePuller(
- android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
- // DebugElapsedClock.
- {android::util::DEBUG_ELAPSED_CLOCK,
- {.additiveFields = {1, 2, 3, 4},
- .puller = new StatsCompanionServicePuller(android::util::DEBUG_ELAPSED_CLOCK)}},
- // DebugFailingElapsedClock.
- {android::util::DEBUG_FAILING_ELAPSED_CLOCK,
- {.additiveFields = {1, 2, 3, 4},
- .puller = new StatsCompanionServicePuller(android::util::DEBUG_FAILING_ELAPSED_CLOCK)}},
- // BuildInformation.
- {android::util::BUILD_INFORMATION,
- {.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
- // RoleHolder.
- {android::util::ROLE_HOLDER,
- {.puller = new StatsCompanionServicePuller(android::util::ROLE_HOLDER)}},
- // PermissionState.
- {android::util::DANGEROUS_PERMISSION_STATE,
- {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}},
- // TrainInfo.
- {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}},
- // TimeZoneDataInfo.
- {android::util::TIME_ZONE_DATA_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}},
- // ExternalStorageInfo
- {android::util::EXTERNAL_STORAGE_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::EXTERNAL_STORAGE_INFO)}},
- // GpuStatsGlobalInfo
- {android::util::GPU_STATS_GLOBAL_INFO,
- {.puller = new GpuStatsPuller(android::util::GPU_STATS_GLOBAL_INFO)}},
- // GpuStatsAppInfo
- {android::util::GPU_STATS_APP_INFO,
- {.puller = new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)}},
- // AppsOnExternalStorageInfo
- {android::util::APPS_ON_EXTERNAL_STORAGE_INFO,
- {.puller = new StatsCompanionServicePuller(android::util::APPS_ON_EXTERNAL_STORAGE_INFO)}},
- // Face Settings
- {android::util::FACE_SETTINGS,
- {.puller = new StatsCompanionServicePuller(android::util::FACE_SETTINGS)}},
- // App ops
- {android::util::APP_OPS,
- {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}},
- // VmsClientStats
- {android::util::VMS_CLIENT_STATS,
- {.additiveFields = {5, 6, 7, 8, 9, 10},
- .puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}},
- // NotiifcationRemoteViews.
- {android::util::NOTIFICATION_REMOTE_VIEWS,
- {.puller = new StatsCompanionServicePuller(android::util::NOTIFICATION_REMOTE_VIEWS)}},
-};
-
-StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
+StatsPullerManager::StatsPullerManager()
+ : kAllPullAtomInfo({
+ // TrainInfo.
+ {{.atomTag = util::TRAIN_INFO, .uid = AID_STATSD}, new TrainInfoPuller()},
+ }),
+ mNextPullTimeNs(NO_ALARM_UPDATE),
+ mPullAtomCallbackDeathRecipient(AIBinder_DeathRecipient_new(pullAtomCallbackDied)) {
}
-bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
- VLOG("Initiating pulling %d", tagId);
+bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ vector<shared_ptr<LogEvent>>* data, bool useUids) {
+ std::lock_guard<std::mutex> _l(mLock);
+ return PullLocked(tagId, configKey, eventTimeNs, data, useUids);
+}
- if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
- bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
- VLOG("pulled %d items", (int)data->size());
- if (!ret) {
- StatsdStats::getInstance().notePullFailed(tagId);
+bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids) {
+ std::lock_guard<std::mutex> _l(mLock);
+ return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+}
+
+bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
+ bool useUids) {
+ vector<int32_t> uids;
+ if (useUids) {
+ auto uidProviderIt = mPullUidProviders.find(configKey);
+ if (uidProviderIt == mPullUidProviders.end()) {
+ ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
}
- return ret;
+ sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
+ if (pullUidProvider == nullptr) {
+ ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
+ configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+ return false;
+ }
+ uids = pullUidProvider->getPullAtomUids(tagId);
+ }
+ return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+}
+
+bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
+ const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
+ bool useUids) {
+ VLOG("Initiating pulling %d", tagId);
+ if (useUids) {
+ for (int32_t uid : uids) {
+ PullerKey key = {.atomTag = tagId, .uid = uid};
+ auto pullerIt = kAllPullAtomInfo.find(key);
+ if (pullerIt != kAllPullAtomInfo.end()) {
+ bool ret = pullerIt->second->Pull(eventTimeNs, data);
+ VLOG("pulled %zu items", data->size());
+ if (!ret) {
+ StatsdStats::getInstance().notePullFailed(tagId);
+ }
+ return ret;
+ }
+ }
+ StatsdStats::getInstance().notePullerNotFound(tagId);
+ ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
+ return false; // Return early since we don't know what to pull.
} else {
- VLOG("Unknown tagId %d", tagId);
+ PullerKey key = {.atomTag = tagId, .uid = -1};
+ auto pullerIt = kAllPullAtomInfo.find(key);
+ if (pullerIt != kAllPullAtomInfo.end()) {
+ bool ret = pullerIt->second->Pull(eventTimeNs, data);
+ VLOG("pulled %zu items", data->size());
+ if (!ret) {
+ StatsdStats::getInstance().notePullFailed(tagId);
+ }
+ return ret;
+ }
+ ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
}
}
bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
- // Vendor pulled atoms might be registered after we parse the config.
- return isVendorPulledAtom(tagId) || kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
+ // Pulled atoms might be registered after we parse the config, so just make sure the id is in
+ // an appropriate range.
+ return isVendorPulledAtom(tagId) || isPulledAtom(tagId);
}
void StatsPullerManager::updateAlarmLocked() {
@@ -301,9 +175,9 @@
return;
}
- sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
- if (statsCompanionServiceCopy != nullptr) {
- statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
+ // TODO(b/151045771): do not hold a lock while making a binder call
+ if (mStatsCompanionService != nullptr) {
+ mStatsCompanionService->setPullingAlarm(mNextPullTimeNs / 1000000);
} else {
VLOG("StatsCompanionService not available. Alarm not set.");
}
@@ -311,22 +185,23 @@
}
void StatsPullerManager::SetStatsCompanionService(
- sp<IStatsCompanionService> statsCompanionService) {
- AutoMutex _l(mLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+ shared_ptr<IStatsCompanionService> statsCompanionService) {
+ std::lock_guard<std::mutex> _l(mLock);
+ shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
for (const auto& pulledAtom : kAllPullAtomInfo) {
- pulledAtom.second.puller->SetStatsCompanionService(statsCompanionService);
+ pulledAtom.second->SetStatsCompanionService(statsCompanionService);
}
if (mStatsCompanionService != nullptr) {
updateAlarmLocked();
}
}
-void StatsPullerManager::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
- int64_t nextPullTimeNs, int64_t intervalNs) {
- AutoMutex _l(mLock);
- auto& receivers = mReceivers[tagId];
+void StatsPullerManager::RegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ int64_t intervalNs) {
+ std::lock_guard<std::mutex> _l(mLock);
+ auto& receivers = mReceivers[{.atomTag = tagId, .configKey = configKey}];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
if (it->receiver == receiver) {
VLOG("Receiver already registered of %d", (int)receivers.size());
@@ -358,13 +233,15 @@
VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
}
-void StatsPullerManager::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
- AutoMutex _l(mLock);
- if (mReceivers.find(tagId) == mReceivers.end()) {
+void StatsPullerManager::UnRegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver) {
+ std::lock_guard<std::mutex> _l(mLock);
+ auto receiversIt = mReceivers.find({.atomTag = tagId, .configKey = configKey});
+ if (receiversIt == mReceivers.end()) {
VLOG("Unknown pull code or no receivers: %d", tagId);
return;
}
- auto& receivers = mReceivers.find(tagId)->second;
+ std::list<ReceiverInfo>& receivers = receiversIt->second;
for (auto it = receivers.begin(); it != receivers.end(); it++) {
if (receiver == it->receiver) {
receivers.erase(it);
@@ -374,16 +251,30 @@
}
}
+void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey,
+ wp<PullUidProvider> provider) {
+ std::lock_guard<std::mutex> _l(mLock);
+ mPullUidProviders[configKey] = provider;
+}
+
+void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey,
+ wp<PullUidProvider> provider) {
+ std::lock_guard<std::mutex> _l(mLock);
+ const auto& it = mPullUidProviders.find(configKey);
+ if (it != mPullUidProviders.end() && it->second == provider) {
+ mPullUidProviders.erase(it);
+ }
+}
+
void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) {
- AutoMutex _l(mLock);
+ std::lock_guard<std::mutex> _l(mLock);
int64_t wallClockNs = getWallClockNs();
int64_t minNextPullTimeNs = NO_ALARM_UPDATE;
- vector<pair<int, vector<ReceiverInfo*>>> needToPull =
- vector<pair<int, vector<ReceiverInfo*>>>();
+ vector<pair<const ReceiverKey*, vector<ReceiverInfo*>>> needToPull;
for (auto& pair : mReceivers) {
- vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
+ vector<ReceiverInfo*> receivers;
if (pair.second.size() != 0) {
for (ReceiverInfo& receiverInfo : pair.second) {
if (receiverInfo.nextPullTimeNs <= elapsedTimeNs) {
@@ -395,18 +286,15 @@
}
}
if (receivers.size() > 0) {
- needToPull.push_back(make_pair(pair.first, receivers));
+ needToPull.push_back(make_pair(&pair.first, receivers));
}
}
}
-
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
- bool pullSuccess = Pull(pullInfo.first, &data);
- if (pullSuccess) {
- StatsdStats::getInstance().notePullDelay(
- pullInfo.first, getElapsedRealtimeNs() - elapsedTimeNs);
- } else {
+ bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey,
+ elapsedTimeNs, &data);
+ if (!pullSuccess) {
VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
}
@@ -448,7 +336,7 @@
int StatsPullerManager::ForceClearPullerCache() {
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
- totalCleared += pulledAtom.second.puller->ForceClearCache();
+ totalCleared += pulledAtom.second->ForceClearCache();
}
return totalCleared;
}
@@ -456,32 +344,45 @@
int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
int totalCleared = 0;
for (const auto& pulledAtom : kAllPullAtomInfo) {
- totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampNs);
+ totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
}
return totalCleared;
}
-void StatsPullerManager::RegisterPullerCallback(int32_t atomTag,
- const sp<IStatsPullerCallback>& callback) {
- AutoMutex _l(mLock);
- // Platform pullers cannot be changed.
- if (!isVendorPulledAtom(atomTag)) {
- VLOG("RegisterPullerCallback: atom tag %d is not vendor pulled", atomTag);
+void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
+ const int64_t coolDownNs, const int64_t timeoutNs,
+ const vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& callback,
+ bool useUid) {
+ std::lock_guard<std::mutex> _l(mLock);
+ VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
+
+ if (callback == nullptr) {
+ ALOGW("SetPullAtomCallback called with null callback for atom %d.", atomTag);
return;
}
- VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
+
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
- kAllPullAtomInfo[atomTag] = {.puller = new StatsCallbackPuller(atomTag, callback)};
+ int64_t actualCoolDownNs = coolDownNs < kMinCoolDownNs ? kMinCoolDownNs : coolDownNs;
+ int64_t actualTimeoutNs = timeoutNs > kMaxTimeoutNs ? kMaxTimeoutNs : timeoutNs;
+
+ sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs,
+ actualTimeoutNs, additiveFields);
+ PullerKey key = {.atomTag = atomTag, .uid = useUid ? uid : -1};
+ AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(),
+ new PullAtomCallbackDeathCookie(this, key, puller));
+ kAllPullAtomInfo[key] = puller;
}
-void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) {
- AutoMutex _l(mLock);
- // Platform pullers cannot be changed.
- if (!isVendorPulledAtom(atomTag)) {
- return;
+void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag,
+ bool useUids) {
+ std::lock_guard<std::mutex> _l(mLock);
+ PullerKey key = {.atomTag = atomTag, .uid = useUids ? uid : -1};
+ if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
+ StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag,
+ /*registered=*/false);
+ kAllPullAtomInfo.erase(key);
}
- StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false);
- kAllPullAtomInfo.erase(atomTag);
}
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 6791f66..194a0f5 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,40 +16,48 @@
#pragma once
-#include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsPullerCallback.h>
-#include <binder/IServiceManager.h>
+#include <aidl/android/os/IPullAtomCallback.h>
+#include <aidl/android/os/IStatsCompanionService.h>
#include <utils/RefBase.h>
-#include <utils/threads.h>
+
#include <list>
#include <vector>
+
#include "PullDataReceiver.h"
+#include "PullUidProvider.h"
#include "StatsPuller.h"
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
+#include "packages/UidMap.h"
+
+using aidl::android::os::IPullAtomCallback;
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
-typedef struct {
- // The field numbers of the fields that need to be summed when merging
- // isolated uid with host uid.
- std::vector<int> additiveFields;
- // Minimum time before this puller does actual pull again.
- // Pullers can cause significant impact to system health and battery.
- // So that we don't pull too frequently.
- // If a pull request comes before cooldown, a cached version from previous pull
- // will be returned.
- int64_t coolDownNs = 1 * NS_PER_SEC;
- // The actual puller
- sp<StatsPuller> puller;
- // Max time allowed to pull this atom.
- // We cannot reliably kill a pull thread. So we don't terminate the puller.
- // The data is discarded if the pull takes longer than this and mHasGoodData
- // marked as false.
- int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs;
-} PullAtomInfo;
+typedef struct PullerKey {
+ // The uid of the process that registers this puller.
+ const int uid = -1;
+ // The atom that this puller is for.
+ const int atomTag;
+
+ bool operator<(const PullerKey& that) const {
+ if (uid < that.uid) {
+ return true;
+ }
+ if (uid > that.uid) {
+ return false;
+ }
+ return atomTag < that.atomTag;
+ };
+
+ bool operator==(const PullerKey& that) const {
+ return uid == that.uid && atomTag == that.atomTag;
+ };
+} PullerKey;
class StatsPullerManager : public virtual RefBase {
public:
@@ -58,13 +66,24 @@
virtual ~StatsPullerManager() {
}
+
// Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
// and then every intervalNs thereafter.
- virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+ virtual void RegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
int64_t intervalNs);
// Stop listening on a tagId.
- virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
+ virtual void UnRegisterReceiver(int tagId, const ConfigKey& configKey,
+ wp<PullDataReceiver> receiver);
+
+ // Registers a pull uid provider for the config key. When pulling atoms, it will be used to
+ // determine which uids to pull from.
+ virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp<PullUidProvider> provider);
+
+ // Unregister a pull uid provider.
+ virtual void UnregisterPullUidProvider(const ConfigKey& configKey,
+ wp<PullUidProvider> provider);
// Verify if we know how to pull for this matcher
bool PullerForMatcherExists(int tagId) const;
@@ -78,9 +97,16 @@
// Returns false when
// 1) the pull fails
// 2) pull takes longer than mPullTimeoutNs (intrinsic to puller)
+ // 3) Either a PullUidProvider was not registered for the config, or the there was no puller
+ // registered for any of the uids for this atom.
// If the metric wants to make any change to the data, like timestamps, they
// should make a copy as this data may be shared with multiple metrics.
- virtual bool Pull(int tagId, vector<std::shared_ptr<LogEvent>>* data);
+ virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+
+ // Same as above, but directly specify the allowed uids to pull from.
+ virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
// Clear pull data cache immediately.
int ForceClearPullerCache();
@@ -88,17 +114,31 @@
// Clear pull data cache if it is beyond respective cool down time.
int ClearPullerCacheIfNecessary(int64_t timestampNs);
- void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+ void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
- void RegisterPullerCallback(int32_t atomTag,
- const sp<IStatsPullerCallback>& callback);
+ void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
+ const int64_t timeoutNs, const vector<int32_t>& additiveFields,
+ const shared_ptr<IPullAtomCallback>& callback,
+ bool useUid = true);
- void UnregisterPullerCallback(int32_t atomTag);
+ void UnregisterPullAtomCallback(const int uid, const int32_t atomTag, bool useUids = true);
- static std::map<int, PullAtomInfo> kAllPullAtomInfo;
+ std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
private:
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+ const static int64_t kMinCoolDownNs = NS_PER_SEC;
+ const static int64_t kMaxTimeoutNs = 10 * NS_PER_SEC;
+ shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+ // A struct containing an atom id and a Config Key
+ typedef struct ReceiverKey {
+ const int atomTag;
+ const ConfigKey configKey;
+
+ inline bool operator<(const ReceiverKey& that) const {
+ return atomTag == that.atomTag ? configKey < that.configKey : atomTag < that.atomTag;
+ }
+ } ReceiverKey;
typedef struct {
int64_t nextPullTimeNs;
@@ -106,16 +146,34 @@
wp<PullDataReceiver> receiver;
} ReceiverInfo;
- // mapping from simple matcher tagId to receivers
- std::map<int, std::list<ReceiverInfo>> mReceivers;
+ // mapping from Receiver Key to receivers
+ std::map<ReceiverKey, std::list<ReceiverInfo>> mReceivers;
+
+ // mapping from Config Key to the PullUidProvider for that config
+ std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
+
+ bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+
+ bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids);
// locks for data receiver and StatsCompanionService changes
- Mutex mLock;
+ std::mutex mLock;
void updateAlarmLocked();
int64_t mNextPullTimeNs;
+ // Death recipient that is triggered when the process holding the IPullAtomCallback has died.
+ ::ndk::ScopedAIBinder_DeathRecipient mPullAtomCallbackDeathRecipient;
+
+ /**
+ * Death recipient callback that is called when a pull atom callback dies.
+ * The cookie is a pointer to a PullAtomCallbackDeathCookie.
+ */
+ static void pullAtomCallbackDied(void* cookie);
+
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
@@ -123,6 +181,8 @@
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
+
+ FRIEND_TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate);
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
deleted file mode 100644
index f6a4aea..0000000
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/power/1.0/IPower.h>
-#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-
-#include <fcntl.h>
-#include <hardware/power.h>
-#include <hardware_legacy/power.h>
-#include <inttypes.h>
-#include <semaphore.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "external/SubsystemSleepStatePuller.h"
-#include "external/StatsPuller.h"
-
-#include "SubsystemSleepStatePuller.h"
-#include "logd/LogEvent.h"
-#include "statslog.h"
-#include "stats_log_util.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::power::V1_0::IPower;
-using android::hardware::power::V1_0::PowerStatePlatformSleepState;
-using android::hardware::power::V1_0::PowerStateVoter;
-using android::hardware::power::V1_1::PowerStateSubsystem;
-using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
-using android::hardware::power::stats::V1_0::PowerEntityInfo;
-using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
-using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
-
-using android::hardware::Return;
-using android::hardware::Void;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
-
-static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
-static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
-
-static std::unordered_map<uint32_t, std::string> gEntityNames = {};
-static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
-
-static std::mutex gPowerHalMutex;
-
-// The caller must be holding gPowerHalMutex.
-static void deinitPowerStatsLocked() {
- gPowerHalV1_0 = nullptr;
- gPowerHalV1_1 = nullptr;
- gPowerStatsHalV1_0 = nullptr;
-}
-
-struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override {
-
- // The HAL just died. Reset all handles to HAL services.
- std::lock_guard<std::mutex> lock(gPowerHalMutex);
- deinitPowerStatsLocked();
- }
-};
-
-static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient =
- new SubsystemSleepStatePullerDeathRecipient();
-
-SubsystemSleepStatePuller::SubsystemSleepStatePuller() :
- StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool checkResultLocked(const Return<void> &ret, const char* function) {
- if (!ret.isOk()) {
- ALOGE("%s failed: requested HAL service not available. Description: %s",
- function, ret.description().c_str());
- if (ret.isDeadObject()) {
- deinitPowerStatsLocked();
- }
- return false;
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-// gPowerStatsHalV1_0 must not be null
-static bool initializePowerStats() {
- using android::hardware::power::stats::V1_0::Status;
-
- // Clear out previous content if we are re-initializing
- gEntityNames.clear();
- gStateNames.clear();
-
- Return<void> ret;
- ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting power entity info");
- return;
- }
-
- // construct lookup table of powerEntityId to power entity name
- for (auto info : infos) {
- gEntityNames.emplace(info.powerEntityId, info.powerEntityName);
- }
- });
- if (!checkResultLocked(ret, __func__)) {
- return false;
- }
-
- ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting state info");
- return;
- }
-
- // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
- for (auto stateSpace : stateSpaces) {
- std::unordered_map<uint32_t, std::string> stateNames = {};
- for (auto state : stateSpace.states) {
- stateNames.emplace(state.powerEntityStateId,
- state.powerEntityStateName);
- }
- gStateNames.emplace(stateSpace.powerEntityId, stateNames);
- }
- });
- if (!checkResultLocked(ret, __func__)) {
- return false;
- }
-
- return (!gEntityNames.empty()) && (!gStateNames.empty());
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getPowerStatsHalLocked() {
- if(gPowerStatsHalV1_0 == nullptr) {
- gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
- if (gPowerStatsHalV1_0 == nullptr) {
- ALOGE("Unable to get power.stats HAL service.");
- return false;
- }
-
- // Link death recipient to power.stats service handle
- hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power.stats HAL death: %s",
- linked.description().c_str());
- deinitPowerStatsLocked();
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power.stats HAL death notifications");
- // We should still continue even though linking failed
- }
- return initializePowerStats();
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
- using android::hardware::power::stats::V1_0::Status;
-
- if(!getPowerStatsHalLocked()) {
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
- // Get power entity state residency data
- bool success = false;
- Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
- [&data, &success, wallClockTimestampNs, elapsedTimestampNs]
- (auto results, auto status) {
- if (status == Status::NOT_SUPPORTED) {
- ALOGW("getPowerEntityStateResidencyData is not supported");
- success = false;
- return;
- }
-
- for(auto result : results) {
- for(auto stateResidency : result.stateResidencyData) {
- auto statePtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- statePtr->write(gEntityNames.at(result.powerEntityId));
- statePtr->write(gStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId));
- statePtr->write(stateResidency.totalStateEntryCount);
- statePtr->write(stateResidency.totalTimeInStateMs);
- statePtr->init();
- data->emplace_back(statePtr);
- }
- }
- success = true;
- });
- // Intentionally not returning early here.
- // bool success determines if this succeeded or not.
- checkResultLocked(ret, __func__);
-
- return success;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getPowerHalLocked() {
- if(gPowerHalV1_0 == nullptr) {
- gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
- if(gPowerHalV1_0 == nullptr) {
- ALOGE("Unable to get power HAL service.");
- return false;
- }
- gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
-
- // Link death recipient to power service handle
- hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power HAL death: %s",
- linked.description().c_str());
- gPowerHalV1_0 = nullptr;
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power. death notifications");
- // We should still continue even though linking failed
- }
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
- using android::hardware::power::V1_0::Status;
-
- if(!getPowerHalLocked()) {
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
- Return<void> ret;
- ret = gPowerHalV1_0->getPlatformLowPowerStats(
- [&data, wallClockTimestampNs, elapsedTimestampNs]
- (hidl_vec<PowerStatePlatformSleepState> states, Status status) {
- if (status != Status::SUCCESS) return;
-
- for (size_t i = 0; i < states.size(); i++) {
- const PowerStatePlatformSleepState& state = states[i];
-
- auto statePtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- statePtr->write(state.name);
- statePtr->write("");
- statePtr->write(state.totalTransitions);
- statePtr->write(state.residencyInMsecSinceBoot);
- statePtr->init();
- data->push_back(statePtr);
- VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
- (long long)state.residencyInMsecSinceBoot,
- (long long)state.totalTransitions,
- state.supportedOnlyInSuspend ? 1 : 0);
- for (const auto& voter : state.voters) {
- auto voterPtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- voterPtr->write(state.name);
- voterPtr->write(voter.name);
- voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot);
- voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
- voterPtr->init();
- data->push_back(voterPtr);
- VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
- voter.name.c_str(),
- (long long)voter.totalTimeInMsecVotedForSinceBoot,
- (long long)voter.totalNumberOfTimesVotedSinceBoot);
- }
- }
- });
- if (!checkResultLocked(ret, __func__)) {
- return false;
- }
-
- // Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
- sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 =
- android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
- if (gPowerHal_1_1 != nullptr) {
- ret = gPowerHal_1_1->getSubsystemLowPowerStats(
- [&data, wallClockTimestampNs, elapsedTimestampNs]
- (hidl_vec<PowerStateSubsystem> subsystems, Status status) {
- if (status != Status::SUCCESS) return;
-
- if (subsystems.size() > 0) {
- for (size_t i = 0; i < subsystems.size(); i++) {
- const PowerStateSubsystem& subsystem = subsystems[i];
- for (size_t j = 0; j < subsystem.states.size(); j++) {
- const PowerStateSubsystemSleepState& state =
- subsystem.states[j];
- auto subsystemStatePtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- subsystemStatePtr->write(subsystem.name);
- subsystemStatePtr->write(state.name);
- subsystemStatePtr->write(state.totalTransitions);
- subsystemStatePtr->write(state.residencyInMsecSinceBoot);
- subsystemStatePtr->init();
- data->push_back(subsystemStatePtr);
- VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
- subsystem.name.c_str(), state.name.c_str(),
- (long long)state.residencyInMsecSinceBoot,
- (long long)state.totalTransitions,
- (long long)state.lastEntryTimestampMs);
- }
- }
- }
- });
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-std::function<bool(vector<shared_ptr<LogEvent>>* data)> getPullerLocked() {
- std::function<bool(vector<shared_ptr<LogEvent>>* data)> ret = {};
-
- // First see if power.stats HAL is available. Fall back to power HAL if
- // power.stats HAL is unavailable.
- if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
- ALOGI("Using power.stats HAL");
- ret = getIPowerStatsDataLocked;
- } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) {
- ALOGI("Using power HAL");
- ret = getIPowerDataLocked;
- }
-
- return ret;
-}
-
-bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> lock(gPowerHalMutex);
-
- if(!gPuller) {
- gPuller = getPullerLocked();
- }
-
- if(gPuller) {
- return gPuller(data);
- }
-
- ALOGE("Unable to load Power Hal or power.stats HAL");
- return false;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.h b/cmds/statsd/src/external/SubsystemSleepStatePuller.h
deleted file mode 100644
index 87f5f02..0000000
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/String16.h>
-#include "StatsPuller.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-/**
- * Reads hal for sleep states
- */
-class SubsystemSleepStatePuller : public StatsPuller {
-public:
- SubsystemSleepStatePuller();
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp
index 9d09242..3837f4a 100644
--- a/cmds/statsd/src/external/TrainInfoPuller.cpp
+++ b/cmds/statsd/src/external/TrainInfoPuller.cpp
@@ -22,7 +22,7 @@
#include "TrainInfoPuller.h"
#include "logd/LogEvent.h"
#include "stats_log_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
#include "storage/StorageManager.h"
using std::make_shared;
@@ -33,18 +33,20 @@
namespace statsd {
TrainInfoPuller::TrainInfoPuller() :
- StatsPuller(android::util::TRAIN_INFO) {
+ StatsPuller(util::TRAIN_INFO) {
}
bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- InstallTrainInfo trainInfo;
- bool ret = StorageManager::readTrainInfo(trainInfo);
- if (!ret) {
- ALOGW("Failed to read train info.");
- return false;
+ vector<InstallTrainInfo> trainInfoList =
+ StorageManager::readAllTrainInfo();
+ if (trainInfoList.empty()) {
+ ALOGW("Train info was empty.");
+ return true;
}
- auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
- data->push_back(event);
+ for (InstallTrainInfo& trainInfo : trainInfoList) {
+ auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
+ data->push_back(event);
+ }
return true;
}
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index ccfd666..aa99d00 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -17,20 +17,13 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include "StatsPullerManager.h"
-#include "atoms_info.h"
#include "puller_util.h"
namespace android {
namespace os {
namespace statsd {
-using std::list;
-using std::map;
-using std::set;
-using std::shared_ptr;
-using std::sort;
-using std::vector;
+using namespace std;
/**
* Process all data and merge isolated with host if necessary.
@@ -54,16 +47,14 @@
* All atoms should be of the same tagId. All fields should be present.
*/
void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
- int tagId) {
- if (StatsPullerManager::kAllPullAtomInfo.find(tagId) ==
- StatsPullerManager::kAllPullAtomInfo.end()) {
- VLOG("Unknown pull atom id %d", tagId);
- return;
- }
- if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) ==
- android::util::AtomsInfo::kAtomsWithAttributionChain.end()) &&
- (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
- android::util::AtomsInfo::kAtomsWithUidField.end())) {
+ int tagId, const vector<int>& additiveFieldsVec) {
+ // Check the first LogEvent for attribution chain or a uid field as either all atoms with this
+ // tagId have them or none of them do.
+ std::pair<int, int> attrIndexRange;
+ const bool hasAttributionChain = data[0]->hasAttributionChain(&attrIndexRange);
+ bool hasUidField = (data[0]->getUidFieldIndex() != -1);
+
+ if (!hasAttributionChain && !hasUidField) {
VLOG("No uid or attribution chain to merge, atom %d", tagId);
return;
}
@@ -74,31 +65,23 @@
ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId());
return;
}
- if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) !=
- android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
- for (auto& value : *(event->getMutableValues())) {
- if (value.mField.getPosAtDepth(0) > kAttributionField) {
- break;
- }
- if (isAttributionUidField(value)) {
- const int hostUid = uidMap->getHostUidOrSelf(value.mValue.int_value);
- value.mValue.setInt(hostUid);
+ if (hasAttributionChain) {
+ vector<FieldValue>* const fieldValues = event->getMutableValues();
+ for (int i = attrIndexRange.first; i <= attrIndexRange.second; i++) {
+ FieldValue& fieldValue = fieldValues->at(i);
+ if (isAttributionUidField(fieldValue)) {
+ const int hostUid = uidMap->getHostUidOrSelf(fieldValue.mValue.int_value);
+ fieldValue.mValue.setInt(hostUid);
}
}
} else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto,
- // starting from 1
- if (uidField > 0 && (int)event->getValues().size() >= uidField &&
- (event->getValues())[uidField - 1].mValue.getType() == INT) {
- Value& value = (*event->getMutableValues())[uidField - 1].mValue;
- const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
- value.setInt(hostUid);
- } else {
- ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
- return;
- }
+ int uidFieldIndex = event->getUidFieldIndex();
+ if (uidFieldIndex != -1) {
+ Value& value = (*event->getMutableValues())[uidFieldIndex].mValue;
+ const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
+ value.setInt(hostUid);
+ } else {
+ ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
}
}
}
@@ -120,8 +103,6 @@
});
vector<shared_ptr<LogEvent>> mergedData;
- const vector<int>& additiveFieldsVec =
- StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end());
bool needMerge = true;
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
index f703e6c..afcf68c 100644
--- a/cmds/statsd/src/external/puller_util.h
+++ b/cmds/statsd/src/external/puller_util.h
@@ -26,7 +26,8 @@
namespace statsd {
void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
- const sp<UidMap>& uidMap, int tagId);
+ const sp<UidMap>& uidMap, int tagId,
+ const vector<int>& additiveFieldsVec);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 3054b6d..6e89038 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -20,7 +20,7 @@
#include <android/util/ProtoOutputStream.h>
#include "../stats_log_util.h"
-#include "statslog.h"
+#include "statslog_statsd.h"
#include "storage/StorageManager.h"
namespace android {
@@ -38,6 +38,7 @@
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;
@@ -54,6 +55,7 @@
const int FIELD_ID_ATOM_STATS_TAG = 1;
const int FIELD_ID_ATOM_STATS_COUNT = 2;
+const int FIELD_ID_ATOM_STATS_ERROR_COUNT = 3;
const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
const int FIELD_ID_PERIODIC_ALARMS_REGISTERED = 1;
@@ -113,13 +115,13 @@
const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL_TIME = 2;
const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
- {android::util::BINDER_CALLS, {6000, 10000}},
- {android::util::LOOPER_STATS, {1500, 2500}},
- {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
+ {util::BINDER_CALLS, {6000, 10000}},
+ {util::LOOPER_STATS, {1500, 2500}},
+ {util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
};
StatsdStats::StatsdStats() {
- mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
+ mPushedAtomStats.resize(kMaxPushedAtomId + 1);
mStartTimeSec = getWallClockSec();
}
@@ -435,9 +437,18 @@
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) {
@@ -448,7 +459,7 @@
void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
lock_guard<std::mutex> lock(mLock);
- if (atomId <= android::util::kMaxPushedAtomId) {
+ if (atomId <= kMaxPushedAtomId) {
mPushedAtomStats[atomId]++;
} else {
if (mNonPlatformPushedAtomStats.size() < kMaxNonPlatformPushedAtoms) {
@@ -471,14 +482,19 @@
mPulledAtomStats[atomId].pullFailed++;
}
-void StatsdStats::noteStatsCompanionPullFailed(int atomId) {
+void StatsdStats::notePullUidProviderNotFound(int atomId) {
lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].statsCompanionPullFailed++;
+ mPulledAtomStats[atomId].pullUidProviderNotFound++;
}
-void StatsdStats::noteStatsCompanionPullBinderTransactionFailed(int atomId) {
+void StatsdStats::notePullerNotFound(int atomId) {
lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].statsCompanionPullBinderTransactionFailed++;
+ mPulledAtomStats[atomId].pullerNotFound++;
+}
+
+void StatsdStats::notePullBinderCallFailed(int atomId) {
+ lock_guard<std::mutex> lock(mLock);
+ mPulledAtomStats[atomId].binderCallFailCount++;
}
void StatsdStats::noteEmptyData(int atomId) {
@@ -549,6 +565,20 @@
std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs);
}
+void StatsdStats::noteAtomError(int atomTag, bool pull) {
+ lock_guard<std::mutex> lock(mLock);
+ if (pull) {
+ mPulledAtomStats[atomTag].atomErrorCount++;
+ return;
+ }
+
+ bool present = (mPushedAtomErrorStats.find(atomTag) != mPushedAtomErrorStats.end());
+ bool full = (mPushedAtomErrorStats.size() >= (size_t)kMaxPushedAtomErrorStatsSize);
+ if (!full || present) {
+ mPushedAtomErrorStats[atomTag]++;
+ }
+}
+
StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) {
auto atomMetricStatsIter = mAtomMetricStats.find(metricId);
if (atomMetricStatsIter != mAtomMetricStats.end()) {
@@ -593,6 +623,7 @@
for (auto& pullStats : mPulledAtomStats) {
pullStats.second.totalPull = 0;
pullStats.second.totalPullFromCache = 0;
+ pullStats.second.minPullIntervalSec = LONG_MAX;
pullStats.second.avgPullTimeNs = 0;
pullStats.second.maxPullTimeNs = 0;
pullStats.second.numPullTime = 0;
@@ -602,11 +633,18 @@
pullStats.second.dataError = 0;
pullStats.second.pullTimeout = 0;
pullStats.second.pullExceedMaxDelay = 0;
+ pullStats.second.pullFailed = 0;
+ pullStats.second.pullUidProviderNotFound = 0;
+ pullStats.second.pullerNotFound = 0;
pullStats.second.registeredCount = 0;
pullStats.second.unregisteredCount = 0;
+ pullStats.second.atomErrorCount = 0;
+ pullStats.second.binderCallFailCount = 0;
+ pullStats.second.pullTimeoutMetadata.clear();
}
mAtomMetricStats.clear();
mActivationBroadcastGuardrailStats.clear();
+ mPushedAtomErrorStats.clear();
}
string buildTimeString(int64_t timeSec) {
@@ -617,6 +655,15 @@
return string(timeBuffer);
}
+int StatsdStats::getPushedAtomErrors(int atomId) const {
+ const auto& it = mPushedAtomErrorStats.find(atomId);
+ if (it != mPushedAtomErrorStats.end()) {
+ return it->second;
+ } else {
+ return 0;
+ }
+}
+
void StatsdStats::dumpStats(int out) const {
lock_guard<std::mutex> lock(mLock);
time_t t = mStartTimeSec;
@@ -721,11 +768,13 @@
const size_t atomCounts = mPushedAtomStats.size();
for (size_t i = 2; i < atomCounts; i++) {
if (mPushedAtomStats[i] > 0) {
- dprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
+ dprintf(out, "Atom %zu->(total count)%d, (error count)%d\n", i, mPushedAtomStats[i],
+ getPushedAtomErrors((int)i));
}
}
for (const auto& pair : mNonPlatformPushedAtomStats) {
- dprintf(out, "Atom %lu->%d\n", (unsigned long)pair.first, pair.second);
+ dprintf(out, "Atom %d->(total count)%d, (error count)%d\n", pair.first, pair.second,
+ getPushedAtomErrors(pair.first));
}
dprintf(out, "********Pulled Atom stats***********\n");
@@ -736,14 +785,32 @@
" (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay "
"nanos)%lld, "
" (max pull delay nanos)%lld, (data error)%ld\n"
- " (pull timeout)%ld, (pull exceed max delay)%ld\n"
- " (registered count) %ld, (unregistered count) %ld\n",
+ " (pull timeout)%ld, (pull exceed max delay)%ld"
+ " (no uid provider count)%ld, (no puller found count)%ld\n"
+ " (registered count) %ld, (unregistered count) %ld"
+ " (atom error count) %d\n",
(int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
(long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec,
(long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs,
(long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs,
pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay,
- pair.second.registeredCount, pair.second.unregisteredCount);
+ 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) {
@@ -919,6 +986,10 @@
proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]);
+ int errors = getPushedAtomErrors(i);
+ if (errors > 0) {
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors);
+ }
proto.end(token);
}
}
@@ -928,6 +999,10 @@
proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, pair.first);
proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, pair.second);
+ int errors = getPushedAtomErrors(pair.first);
+ if (errors > 0) {
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors);
+ }
proto.end(token);
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 564b9ee..0050484 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -16,7 +16,6 @@
#pragma once
#include "config/ConfigKey.h"
-#include "atoms_info.h"
#include <gtest/gtest_prod.h>
#include <log/log_time.h>
@@ -102,7 +101,7 @@
// 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;
@@ -119,6 +118,8 @@
const static int kMaxLogSourceCount = 50;
+ const static int kMaxPullAtomPackages = 100;
+
// Max memory allowed for storing metrics per configuration. If this limit is exceeded, statsd
// drops the metrics data in memory.
static const size_t kMaxMetricsBytesPerConfig = 2 * 1024 * 1024;
@@ -159,13 +160,20 @@
static const long kPullerCacheClearIntervalSec = 1;
// Max time to do a pull.
- static const int64_t kPullMaxDelayNs = 10 * NS_PER_SEC;
+ static const int64_t kPullMaxDelayNs = 30 * NS_PER_SEC;
// Maximum number of pushed atoms statsd stats will track above kMaxPushedAtomId.
static const int kMaxNonPlatformPushedAtoms = 100;
- // Max platform atom tag number.
- static const int32_t kMaxPlatformAtomTag = 100000;
+ // Maximum atom id value that we consider a platform pushed atom.
+ // This should be updated once highest pushed atom id in atoms.proto approaches this value.
+ static const int kMaxPushedAtomId = 500;
+
+ // Atom id that is the start of the pulled atoms.
+ static const int kPullAtomStartTag = 10000;
+
+ // Atom id that is the start of vendor atoms.
+ static const int kVendorAtomStartTag = 100000;
// Vendor pulled atom start id.
static const int32_t kVendorPulledAtomStartTag = 150000;
@@ -181,6 +189,8 @@
static const int64_t kInt64Max = 0x7fffffffffffffffLL;
+ static const int32_t kMaxLoggedBucketDropEvents = 10;
+
/**
* Report a new config has been received and report the static stats about the config.
*
@@ -342,7 +352,7 @@
/*
* 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.
@@ -361,21 +371,30 @@
int32_t lastAtomTag, int32_t uid, int32_t pid);
/**
- * Records that the pull of an atom has failed
+ * Records that the pull of an atom has failed. Eg, if the client indicated the pull failed, if
+ * the pull timed out, or if the outgoing binder call failed.
+ * This count will only increment if the puller was actually invoked.
+ *
+ * It does not include a pull not occurring due to not finding the appropriate
+ * puller. These cases are covered in other counts.
*/
void notePullFailed(int atomId);
/**
- * Records that the pull of StatsCompanionService atom has failed
+ * Records that the pull of an atom has failed due to not having a uid provider.
*/
- void noteStatsCompanionPullFailed(int atomId);
+ void notePullUidProviderNotFound(int atomId);
/**
- * Records that the pull of a StatsCompanionService atom has failed due to a failed binder
- * transaction. This can happen when StatsCompanionService returns too
- * much data (the max Binder parcel size is 1MB)
+ * Records that the pull of an atom has failed due not finding a puller registered by a
+ * trusted uid.
*/
- void noteStatsCompanionPullBinderTransactionFailed(int atomId);
+ void notePullerNotFound(int atomId);
+
+ /**
+ * Records that the pull has failed due to the outgoing binder call failing.
+ */
+ void notePullBinderCallFailed(int atomId);
/**
* A pull with no data occurred
@@ -450,6 +469,16 @@
*/
void noteActivationBroadcastGuardrailHit(const int uid);
+ /**
+ * Reports that an atom is erroneous or cannot be parsed successfully by
+ * statsd. An atom tag of 0 indicates that the client did not supply the
+ * atom id within the encoding.
+ *
+ * For pushed atoms only, this call should be preceded by a call to
+ * noteAtomLogged.
+ */
+ void noteAtomError(int atomTag, bool pull=false);
+
/**
* Reset the historical stats. Including all stats in icebox, and the tracked stats about
* metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
@@ -469,6 +498,14 @@
*/
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;
@@ -483,11 +520,14 @@
long pullTimeout = 0;
long pullExceedMaxDelay = 0;
long pullFailed = 0;
- long statsCompanionPullFailed = 0;
- long statsCompanionPullBinderTransactionFailed = 0;
+ long pullUidProviderNotFound = 0;
+ long pullerNotFound = 0;
long emptyData = 0;
long registeredCount = 0;
long unregisteredCount = 0;
+ int32_t atomErrorCount = 0;
+ long binderCallFailCount = 0;
+ std::list<PullTimeoutMetadata> pullTimeoutMetadata;
} PulledAtomStats;
typedef struct {
@@ -535,6 +575,12 @@
// Maps PullAtomId to its stats. The size is capped by the puller atom counts.
std::map<int, PulledAtomStats> mPulledAtomStats;
+ // Stores the number of times a pushed atom was logged erroneously. The
+ // corresponding counts for pulled atoms are stored in PulledAtomStats.
+ // The max size of this map is kMaxAtomErrorsStatsSize.
+ std::map<int, int> mPushedAtomErrorStats;
+ int kMaxPushedAtomErrorStatsSize = 100;
+
// Maps metric ID to its stats. The size is capped by the number of metrics.
std::map<int64_t, AtomMetricStats> mAtomMetricStats;
@@ -602,6 +648,8 @@
void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats);
+ int getPushedAtomErrors(int atomId) const;
+
/**
* Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
* will live as long as `this`.
@@ -620,6 +668,9 @@
FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
+ FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
+
+ FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
};
} // namespace statsd
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1d51ab8..f56fa62 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -17,12 +17,14 @@
#define DEBUG false // STOPSHIP if true
#include "logd/LogEvent.h"
-#include "stats_log_util.h"
-#include "statslog.h"
-
-#include <binder/IPCThreadState.h>
+#include <android-base/stringprintf.h>
+#include <android/binder_ibinder.h>
#include <private/android_filesystem_config.h>
+#include "annotations.h"
+#include "stats_log_util.h"
+#include "statslog_statsd.h"
+
namespace android {
namespace os {
namespace statsd {
@@ -31,160 +33,41 @@
const int FIELD_ID_EXPERIMENT_ID = 1;
using namespace android::util;
+using android::base::StringPrintf;
using android::util::ProtoOutputStream;
using std::string;
using std::vector;
-LogEvent::LogEvent(log_msg& msg) {
- mContext =
- create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
- mLogdTimestampNs = msg.entry.sec * NS_PER_SEC + msg.entry.nsec;
- mLogUid = msg.entry.uid;
- init(mContext);
- if (mContext) {
- // android_log_destroy will set mContext to NULL
- android_log_destroy(&mContext);
- }
-}
+// stats_event.h socket types. Keep in sync.
+/* ERRORS */
+#define ERROR_NO_TIMESTAMP 0x1
+#define ERROR_NO_ATOM_ID 0x2
+#define ERROR_OVERFLOW 0x4
+#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
+#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
+#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
+#define ERROR_INVALID_ANNOTATION_ID 0x40
+#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
+#define ERROR_TOO_MANY_ANNOTATIONS 0x100
+#define ERROR_TOO_MANY_FIELDS 0x200
+#define ERROR_INVALID_VALUE_TYPE 0x400
+#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
-LogEvent::LogEvent(const LogEvent& event) {
- mTagId = event.mTagId;
- mLogUid = event.mLogUid;
- mElapsedTimestampNs = event.mElapsedTimestampNs;
- mLogdTimestampNs = event.mLogdTimestampNs;
- mValues = event.mValues;
-}
+/* TYPE IDS */
+#define INT32_TYPE 0x00
+#define INT64_TYPE 0x01
+#define STRING_TYPE 0x02
+#define LIST_TYPE 0x03
+#define FLOAT_TYPE 0x04
+#define BOOL_TYPE 0x05
+#define BYTE_ARRAY_TYPE 0x06
+#define OBJECT_TYPE 0x07
+#define KEY_VALUE_PAIRS_TYPE 0x08
+#define ATTRIBUTION_CHAIN_TYPE 0x09
+#define ERROR_TYPE 0x0F
-LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
- mTagId = statsLogEventWrapper.getTagId();
- mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
- mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
- mLogUid = 0;
- int workChainPosOffset = 0;
- if (workChainIndex != -1) {
- const WorkChain& wc = statsLogEventWrapper.getWorkChains()[workChainIndex];
- // chains are at field 1, level 2
- int depth = 2;
- for (int i = 0; i < (int)wc.uids.size(); i++) {
- int pos[] = {1, i + 1, 1};
- mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.uids[i])));
- pos[2]++;
- mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.tags[i])));
- mValues.back().mField.decorateLastPos(2);
- }
- mValues.back().mField.decorateLastPos(1);
- workChainPosOffset = 1;
- }
- for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
- Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1 + workChainPosOffset));
- switch (statsLogEventWrapper.getElements()[i].type) {
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
- mValues.push_back(
- FieldValue(field, Value(statsLogEventWrapper.getElements()[i].int_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::LONG:
- mValues.push_back(
- FieldValue(field, Value(statsLogEventWrapper.getElements()[i].long_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::FLOAT:
- mValues.push_back(FieldValue(
- field, Value(statsLogEventWrapper.getElements()[i].float_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::DOUBLE:
- mValues.push_back(FieldValue(
- field, Value(statsLogEventWrapper.getElements()[i].double_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STRING:
- mValues.push_back(
- FieldValue(field, Value(statsLogEventWrapper.getElements()[i].str_value)));
- break;
- case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::STORAGE:
- mValues.push_back(FieldValue(
- field, Value(statsLogEventWrapper.getElements()[i].storage_value)));
- break;
- default:
- break;
- }
- }
-}
-
-void LogEvent::createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
- std::vector<std::shared_ptr<LogEvent>>& logEvents) {
- if (statsLogEventWrapper.getWorkChains().size() == 0) {
- logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, -1));
- } else {
- for (size_t i = 0; i < statsLogEventWrapper.getWorkChains().size(); i++) {
- logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, i));
- }
- }
-}
-
-LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
- mLogdTimestampNs = wallClockTimestampNs;
- mElapsedTimestampNs = elapsedTimestampNs;
- mTagId = tagId;
- mLogUid = 0;
- mContext = create_android_logger(1937006964); // the event tag shared by all stats logs
- if (mContext) {
- android_log_write_int64(mContext, elapsedTimestampNs);
- android_log_write_int32(mContext, tagId);
- }
-}
-
-LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
- int32_t uid,
- const std::map<int32_t, int32_t>& int_map,
- const std::map<int32_t, int64_t>& long_map,
- const std::map<int32_t, std::string>& string_map,
- const std::map<int32_t, float>& float_map) {
- mLogdTimestampNs = wallClockTimestampNs;
- mElapsedTimestampNs = elapsedTimestampNs;
- mTagId = android::util::KEY_VALUE_PAIRS_ATOM;
- mLogUid = uid;
-
- int pos[] = {1, 1, 1};
-
- mValues.push_back(FieldValue(Field(mTagId, pos, 0 /* depth */), Value(uid)));
- pos[0]++;
- for (const auto&itr : int_map) {
- pos[2] = 1;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
- pos[2] = 2;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second)));
- mValues.back().mField.decorateLastPos(2);
- pos[1]++;
- }
-
- for (const auto&itr : long_map) {
- pos[2] = 1;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
- pos[2] = 3;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second)));
- mValues.back().mField.decorateLastPos(2);
- pos[1]++;
- }
-
- for (const auto&itr : string_map) {
- pos[2] = 1;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
- pos[2] = 4;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second)));
- mValues.back().mField.decorateLastPos(2);
- pos[1]++;
- }
-
- for (const auto&itr : float_map) {
- pos[2] = 1;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first)));
- pos[2] = 5;
- mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second)));
- mValues.back().mField.decorateLastPos(2);
- pos[1]++;
- }
- if (!mValues.empty()) {
- mValues.back().mField.decorateLastPos(1);
- mValues.at(mValues.size() - 2).mField.decorateLastPos(1);
- }
+LogEvent::LogEvent(int32_t uid, int32_t pid)
+ : mLogdTimestampNs(time(nullptr)), mLogUid(uid), mLogPid(pid) {
}
LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging,
@@ -192,8 +75,9 @@
const std::vector<uint8_t>& experimentIds, int32_t userId) {
mLogdTimestampNs = getWallClockNs();
mElapsedTimestampNs = getElapsedRealtimeNs();
- mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
- mLogUid = android::IPCThreadState::self()->getCallingUid();
+ mTagId = util::BINARY_PUSH_STATE_CHANGED;
+ mLogUid = AIBinder_getCallingUid();
+ mLogPid = AIBinder_getCallingPid();
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
@@ -210,7 +94,7 @@
const InstallTrainInfo& trainInfo) {
mLogdTimestampNs = wallClockTimestampNs;
mElapsedTimestampNs = elapsedTimestampNs;
- mTagId = android::util::TRAIN_INFO;
+ mTagId = util::TRAIN_INFO;
mValues.push_back(
FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
@@ -221,309 +105,320 @@
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
}
-LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, timestampNs) {
+void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int32_t value = readNextValue<int32_t>();
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) {
- mLogdTimestampNs = timestampNs;
- mTagId = tagId;
- mLogUid = uid;
- mContext = create_android_logger(1937006964); // the event tag shared by all stats logs
- if (mContext) {
- android_log_write_int64(mContext, timestampNs);
- android_log_write_int32(mContext, tagId);
+void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int64_t value = readNextValue<int64_t>();
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
+}
+
+void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int32_t numBytes = readNextValue<int32_t>();
+ if ((uint32_t)numBytes > mRemainingLen) {
+ mValid = false;
+ return;
}
+
+ string value = string((char*)mBuf, numBytes);
+ mBuf += numBytes;
+ mRemainingLen -= numBytes;
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-void LogEvent::init() {
- if (mContext) {
- const char* buffer;
- size_t len = android_log_write_list_buffer(mContext, &buffer);
- // turns to reader mode
- android_log_context contextForRead = create_android_log_parser(buffer, len);
- if (contextForRead) {
- init(contextForRead);
- // destroy the context to save memory.
- // android_log_destroy will set mContext to NULL
- android_log_destroy(&contextForRead);
- }
- android_log_destroy(&mContext);
+void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ float value = readNextValue<float>();
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
+}
+
+void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ // cast to int32_t because FieldValue does not support bools
+ int32_t value = (int32_t)readNextValue<uint8_t>();
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
+}
+
+void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int32_t numBytes = readNextValue<int32_t>();
+ if ((uint32_t)numBytes > mRemainingLen) {
+ mValid = false;
+ return;
}
+
+ vector<uint8_t> value(mBuf, mBuf + numBytes);
+ mBuf += numBytes;
+ mRemainingLen -= numBytes;
+ addToValues(pos, depth, value, last);
+ parseAnnotations(numAnnotations);
}
-LogEvent::~LogEvent() {
- if (mContext) {
- // This is for the case when LogEvent is created using the test interface
- // but init() isn't called.
- android_log_destroy(&mContext);
- }
-}
+void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
+ int32_t numPairs = readNextValue<uint8_t>();
-bool LogEvent::write(int32_t value) {
- if (mContext) {
- return android_log_write_int32(mContext, value) >= 0;
- }
- return false;
-}
+ for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) {
+ last[1] = (pos[1] == numPairs);
-bool LogEvent::write(uint32_t value) {
- if (mContext) {
- return android_log_write_int32(mContext, value) >= 0;
- }
- return false;
-}
+ // parse key
+ pos[2] = 1;
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
-bool LogEvent::write(int64_t value) {
- if (mContext) {
- return android_log_write_int64(mContext, value) >= 0;
- }
- return false;
-}
+ // parse value
+ last[2] = true;
-bool LogEvent::write(uint64_t value) {
- if (mContext) {
- return android_log_write_int64(mContext, value) >= 0;
- }
- return false;
-}
-
-bool LogEvent::write(const string& value) {
- if (mContext) {
- return android_log_write_string8_len(mContext, value.c_str(), value.length()) >= 0;
- }
- return false;
-}
-
-bool LogEvent::write(float value) {
- if (mContext) {
- return android_log_write_float32(mContext, value) >= 0;
- }
- return false;
-}
-
-bool LogEvent::writeKeyValuePairs(int32_t uid,
- const std::map<int32_t, int32_t>& int_map,
- const std::map<int32_t, int64_t>& long_map,
- const std::map<int32_t, std::string>& string_map,
- const std::map<int32_t, float>& float_map) {
- if (mContext) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(uid);
- for (const auto& itr : int_map) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(itr.first);
- write(itr.second);
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- }
-
- for (const auto& itr : long_map) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(itr.first);
- write(itr.second);
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- }
-
- for (const auto& itr : string_map) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(itr.first);
- write(itr.second.c_str());
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- }
-
- for (const auto& itr : float_map) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- write(itr.first);
- write(itr.second);
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- }
-
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- return true;
- }
- return false;
-}
-
-bool LogEvent::write(const std::vector<AttributionNodeInternal>& nodes) {
- if (mContext) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- for (size_t i = 0; i < nodes.size(); ++i) {
- if (!write(nodes[i])) {
- return false;
- }
- }
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- return true;
- }
- return false;
-}
-
-bool LogEvent::write(const AttributionNodeInternal& node) {
- if (mContext) {
- if (android_log_write_list_begin(mContext) < 0) {
- return false;
- }
- if (android_log_write_int32(mContext, node.uid()) < 0) {
- return false;
- }
- if (android_log_write_string8(mContext, node.tag().c_str()) < 0) {
- return false;
- }
- if (android_log_write_list_end(mContext) < 0) {
- return false;
- }
- return true;
- }
- return false;
-}
-
-/**
- * The elements of each log event are stored as a vector of android_log_list_elements.
- * The goal is to do as little preprocessing as possible, because we read a tiny fraction
- * of the elements that are written to the log.
- *
- * The idea here is to read through the log items once, we get as much information we need for
- * matching as possible. Because this log will be matched against lots of matchers.
- */
-void LogEvent::init(android_log_context context) {
- android_log_list_element elem;
- int i = 0;
- int depth = -1;
- int pos[] = {1, 1, 1};
- bool isKeyValuePairAtom = false;
- do {
- elem = android_log_read_next(context);
- switch ((int)elem.type) {
- case EVENT_TYPE_INT:
- // elem at [0] is EVENT_TYPE_LIST, [1] is the timestamp, [2] is tag id.
- if (i == 2) {
- mTagId = elem.data.int32;
- isKeyValuePairAtom = (mTagId == android::util::KEY_VALUE_PAIRS_ATOM);
- } else {
- if (depth < 0 || depth > 2) {
- return;
- }
-
- mValues.push_back(
- FieldValue(Field(mTagId, pos, depth), Value((int32_t)elem.data.int32)));
-
- pos[depth]++;
- }
+ uint8_t typeInfo = readNextValue<uint8_t>();
+ switch (getTypeId(typeInfo)) {
+ case INT32_TYPE:
+ pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
- case EVENT_TYPE_FLOAT: {
- if (depth < 0 || depth > 2) {
- ALOGE("Depth > 2. Not supported!");
- return;
- }
-
- // Handles the oneof field in KeyValuePair atom.
- if (isKeyValuePairAtom && depth == 2) {
- pos[depth] = 5;
- }
-
- mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(elem.data.float32)));
-
- pos[depth]++;
-
- } break;
- case EVENT_TYPE_STRING: {
- if (depth < 0 || depth > 2) {
- ALOGE("Depth > 2. Not supported!");
- return;
- }
-
- // Handles the oneof field in KeyValuePair atom.
- if (isKeyValuePairAtom && depth == 2) {
- pos[depth] = 4;
- }
- mValues.push_back(FieldValue(Field(mTagId, pos, depth),
- Value(string(elem.data.string, elem.len))));
-
- pos[depth]++;
-
- } break;
- case EVENT_TYPE_LONG: {
- if (i == 1) {
- mElapsedTimestampNs = elem.data.int64;
- } else {
- if (depth < 0 || depth > 2) {
- ALOGE("Depth > 2. Not supported!");
- return;
- }
- // Handles the oneof field in KeyValuePair atom.
- if (isKeyValuePairAtom && depth == 2) {
- pos[depth] = 3;
- }
- mValues.push_back(
- FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64)));
-
- pos[depth]++;
- }
- } break;
- case EVENT_TYPE_LIST:
- depth++;
- if (depth > 2) {
- ALOGE("Depth > 2. Not supported!");
- return;
- }
- pos[depth] = 1;
-
+ case INT64_TYPE:
+ pos[2] = 3;
+ parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
- case EVENT_TYPE_LIST_STOP: {
- int prevDepth = depth;
- depth--;
- if (depth >= 0 && depth < 2) {
- // Now go back to decorate the previous items that are last at prevDepth.
- // So that we can later easily match them with Position=Last matchers.
- pos[prevDepth]--;
- int path = getEncodedField(pos, prevDepth, false);
- for (auto it = mValues.rbegin(); it != mValues.rend(); ++it) {
- if (it->mField.getDepth() >= prevDepth &&
- it->mField.getPath(prevDepth) == path) {
- it->mField.decorateLastPos(prevDepth);
- } else {
- // Safe to break, because the items are in DFS order.
- break;
- }
- }
- pos[depth]++;
- }
+ case STRING_TYPE:
+ pos[2] = 4;
+ parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
- }
- case EVENT_TYPE_UNKNOWN:
+ case FLOAT_TYPE:
+ pos[2] = 5;
+ parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
default:
+ mValid = false;
+ }
+ }
+
+ parseAnnotations(numAnnotations);
+
+ pos[1] = pos[2] = 1;
+ last[1] = last[2] = false;
+}
+
+void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
+ uint8_t numAnnotations) {
+ const unsigned int firstUidInChainIndex = mValues.size();
+ const int32_t numNodes = readNextValue<uint8_t>();
+ for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
+ last[1] = (pos[1] == numNodes);
+
+ // parse uid
+ pos[2] = 1;
+ parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
+
+ // parse tag
+ pos[2] = 2;
+ last[2] = true;
+ parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
+ }
+ // Check if at least one node was successfully parsed.
+ if (mValues.size() - 1 > firstUidInChainIndex) {
+ mAttributionChainStartIndex = static_cast<int8_t>(firstUidInChainIndex);
+ mAttributionChainEndIndex = static_cast<int8_t>(mValues.size() - 1);
+ }
+
+ parseAnnotations(numAnnotations, firstUidInChainIndex);
+
+ pos[1] = pos[2] = 1;
+ last[1] = last[2] = false;
+}
+
+// Assumes that mValues is not empty
+bool LogEvent::checkPreviousValueType(Type expected) {
+ return mValues[mValues.size() - 1].mValue.getType() == expected;
+}
+
+void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
+ }
+
+ bool isUid = readNextValue<uint8_t>();
+ if (isUid) mUidFieldIndex = static_cast<int8_t>(mValues.size() - 1);
+ mValues[mValues.size() - 1].mAnnotations.setUidField(isUid);
+}
+
+void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) {
+ if (!mValues.empty() || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
+ }
+
+ mTruncateTimestamp = readNextValue<uint8_t>();
+}
+
+void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
+ }
+
+ const bool primaryField = readNextValue<uint8_t>();
+ mValues[mValues.size() - 1].mAnnotations.setPrimaryField(primaryField);
+}
+
+void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType,
+ int firstUidInChainIndex) {
+ if (mValues.empty() || annotationType != BOOL_TYPE || -1 == firstUidInChainIndex) {
+ mValid = false;
+ return;
+ }
+
+ const bool primaryField = readNextValue<uint8_t>();
+ mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField);
+}
+
+void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
+ }
+
+ const bool exclusiveState = readNextValue<uint8_t>();
+ mExclusiveStateFieldIndex = static_cast<int8_t>(mValues.size() - 1);
+ mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
+}
+
+void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || annotationType != INT32_TYPE) {
+ mValid = false;
+ return;
+ }
+
+ mResetState = readNextValue<int32_t>();
+}
+
+void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) {
+ if (mValues.empty() || annotationType != BOOL_TYPE) {
+ mValid = false;
+ return;
+ }
+
+ bool nested = readNextValue<uint8_t>();
+ mValues[mValues.size() - 1].mAnnotations.setNested(nested);
+}
+
+// firstUidInChainIndex is a default parameter that is only needed when parsing
+// annotations for attribution chains.
+void LogEvent::parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex) {
+ for (uint8_t i = 0; i < numAnnotations; i++) {
+ uint8_t annotationId = readNextValue<uint8_t>();
+ uint8_t annotationType = readNextValue<uint8_t>();
+
+ switch (annotationId) {
+ case ANNOTATION_ID_IS_UID:
+ parseIsUidAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_TRUNCATE_TIMESTAMP:
+ parseTruncateTimestampAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_PRIMARY_FIELD:
+ parsePrimaryFieldAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID:
+ parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex);
+ break;
+ case ANNOTATION_ID_EXCLUSIVE_STATE:
+ parseExclusiveStateAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_TRIGGER_STATE_RESET:
+ parseTriggerStateResetAnnotation(annotationType);
+ break;
+ case ANNOTATION_ID_STATE_NESTED:
+ parseStateNestedAnnotation(annotationType);
+ break;
+ default:
+ mValid = false;
+ return;
+ }
+ }
+}
+
+// This parsing logic is tied to the encoding scheme used in StatsEvent.java and
+// stats_event.c
+bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
+ mBuf = buf;
+ mRemainingLen = (uint32_t)len;
+
+ int32_t pos[] = {1, 1, 1};
+ bool last[] = {false, false, false};
+
+ // Beginning of buffer is OBJECT_TYPE | NUM_FIELDS | TIMESTAMP | ATOM_ID
+ uint8_t typeInfo = readNextValue<uint8_t>();
+ if (getTypeId(typeInfo) != OBJECT_TYPE) mValid = false;
+
+ uint8_t numElements = readNextValue<uint8_t>();
+ if (numElements < 2 || numElements > 127) mValid = false;
+
+ typeInfo = readNextValue<uint8_t>();
+ if (getTypeId(typeInfo) != INT64_TYPE) mValid = false;
+ mElapsedTimestampNs = readNextValue<int64_t>();
+ numElements--;
+
+ typeInfo = readNextValue<uint8_t>();
+ if (getTypeId(typeInfo) != INT32_TYPE) mValid = false;
+ mTagId = readNextValue<int32_t>();
+ numElements--;
+ parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
+
+ for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) {
+ last[0] = (pos[0] == numElements);
+
+ typeInfo = readNextValue<uint8_t>();
+ uint8_t typeId = getTypeId(typeInfo);
+
+ switch (typeId) {
+ case BOOL_TYPE:
+ parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case INT32_TYPE:
+ parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case INT64_TYPE:
+ parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case FLOAT_TYPE:
+ parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case BYTE_ARRAY_TYPE:
+ parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case STRING_TYPE:
+ parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case KEY_VALUE_PAIRS_TYPE:
+ parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case ATTRIBUTION_CHAIN_TYPE:
+ parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
+ break;
+ case ERROR_TYPE:
+ /* mErrorBitmask =*/ readNextValue<int32_t>();
+ mValid = false;
+ break;
+ default:
+ mValid = false;
break;
}
- i++;
- } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
- if (isKeyValuePairAtom && mValues.size() > 0) {
- mValues[0] = FieldValue(Field(android::util::KEY_VALUE_PAIRS_ATOM, getSimpleField(1)),
- Value((int32_t)mLogUid));
}
+
+ if (mRemainingLen != 0) mValid = false;
+ mBuf = nullptr;
+ return mValid;
+}
+
+uint8_t LogEvent::getTypeId(uint8_t typeInfo) {
+ return typeInfo & 0x0F; // type id in lower 4 bytes
+}
+
+uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) {
+ return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes
}
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
@@ -631,6 +526,26 @@
return 0.0;
}
+std::vector<uint8_t> LogEvent::GetStorage(size_t key, status_t* err) const {
+ int field = getSimpleField(key);
+ for (const auto& value : mValues) {
+ if (value.mField.getField() == field) {
+ if (value.mValue.getType() == STORAGE) {
+ return value.mValue.storage_value;
+ } else {
+ *err = BAD_TYPE;
+ return vector<uint8_t>();
+ }
+ }
+ if ((size_t)value.mField.getPosAtDepth(0) > key) {
+ break;
+ }
+ }
+
+ *err = BAD_INDEX;
+ return vector<uint8_t>();
+}
+
string LogEvent::ToString() const {
string result;
result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
@@ -647,6 +562,19 @@
writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
}
+bool LogEvent::hasAttributionChain(std::pair<int, int>* indexRange) const {
+ if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) {
+ return false;
+ }
+
+ if (nullptr != indexRange) {
+ indexRange->first = static_cast<int>(mAttributionChainStartIndex);
+ indexRange->second = static_cast<int>(mAttributionChainEndIndex);
+ }
+
+ return true;
+}
+
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds,
std::vector<uint8_t>* protoOut) {
ProtoOutputStream proto;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index e1b5a0b..a5f2460 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,9 +18,7 @@
#include "FieldValue.h"
-#include <android/os/StatsLogEventWrapper.h>
#include <android/util/ProtoOutputStream.h>
-#include <log/log_read.h>
#include <private/android_logger.h>
#include <string>
@@ -30,75 +28,39 @@
namespace os {
namespace statsd {
-struct AttributionNodeInternal {
- void set_uid(int32_t id) {
- mUid = id;
- }
-
- void set_tag(const std::string& value) {
- mTag = value;
- }
-
- int32_t uid() const {
- return mUid;
- }
-
- const std::string& tag() const {
- return mTag;
- }
-
- int32_t mUid;
- std::string mTag;
-};
-
struct InstallTrainInfo {
int64_t trainVersionCode;
std::string trainName;
int32_t status;
std::vector<int64_t> experimentIds;
+ bool requiresStaging;
+ bool rollbackEnabled;
+ bool requiresLowLatencyMonitor;
};
/**
- * Wrapper for the log_msg structure.
+ * This class decodes the structured, serialized encoding of an atom into a
+ * vector of FieldValues.
*/
class LogEvent {
public:
/**
- * Read a LogEvent from a log_msg.
+ * \param uid user id of the logging caller
+ * \param pid process id of the logging caller
*/
- explicit LogEvent(log_msg& msg);
+ explicit LogEvent(int32_t uid, int32_t pid);
/**
- * Creates LogEvent from StatsLogEventWrapper.
+ * Parses the atomId, timestamp, and vector of values from a buffer
+ * containing the StatsEvent/AStatsEvent encoding of an atom.
+ *
+ * \param buf a buffer that begins at the start of the serialized atom (it
+ * should not include the android_log_header_t or the StatsEventTag)
+ * \param len size of the buffer
+ *
+ * \return success of the initialization
*/
- static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
- std::vector<std::shared_ptr<LogEvent>>& logEvents);
-
- /**
- * Construct one LogEvent from a StatsLogEventWrapper with the i-th work chain. -1 if no chain.
- */
- explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex);
-
- /**
- * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
- */
- explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs);
-
- // For testing. The timestamp is used as both elapsed real time and logd timestamp.
- explicit LogEvent(int32_t tagId, int64_t timestampNs);
-
- // For testing. The timestamp is used as both elapsed real time and logd timestamp.
- explicit LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid);
-
- /**
- * Constructs a KeyValuePairsAtom LogEvent from value maps.
- */
- explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
- int32_t uid,
- const std::map<int32_t, int32_t>& int_map,
- const std::map<int32_t, int64_t>& long_map,
- const std::map<int32_t, std::string>& string_map,
- const std::map<int32_t, float>& float_map);
+ bool parseBuffer(uint8_t* buf, size_t len);
// Constructs a BinaryPushStateChanged LogEvent from API call.
explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging,
@@ -108,7 +70,7 @@
explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& installTrainInfo);
- ~LogEvent();
+ ~LogEvent() {}
/**
* Get the timestamp associated with this event.
@@ -121,9 +83,17 @@
*/
inline int GetTagId() const { return mTagId; }
- inline uint32_t GetUid() const {
- return mLogUid;
- }
+ /**
+ * Get the uid of the logging client.
+ * Returns -1 if the uid is unknown/has not been set.
+ */
+ inline int32_t GetUid() const { return mLogUid; }
+
+ /**
+ * Get the pid of the logging client.
+ * Returns -1 if the pid is unknown/has not been set.
+ */
+ inline int32_t GetPid() const { return mLogPid; }
/**
* Get the nth value, starting at 1.
@@ -136,24 +106,7 @@
const char* GetString(size_t key, status_t* err) const;
bool GetBool(size_t key, status_t* err) const;
float GetFloat(size_t key, status_t* err) const;
-
- /**
- * Write test data to the LogEvent. This can only be used when the LogEvent is constructed
- * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it.
- */
- bool write(uint32_t value);
- bool write(int32_t value);
- bool write(uint64_t value);
- bool write(int64_t value);
- bool write(const std::string& value);
- bool write(float value);
- bool write(const std::vector<AttributionNodeInternal>& nodes);
- bool write(const AttributionNodeInternal& node);
- bool writeKeyValuePairs(int32_t uid,
- const std::map<int32_t, int32_t>& int_map,
- const std::map<int32_t, int64_t>& long_map,
- const std::map<int32_t, std::string>& string_map,
- const std::map<int32_t, float>& float_map);
+ std::vector<uint8_t> GetStorage(size_t key, status_t* err) const;
/**
* Return a string representation of this event.
@@ -166,12 +119,6 @@
void ToProto(android::util::ProtoOutputStream& out) const;
/**
- * Used with the constructor where tag is passed in. Converts the log_event_list to read mode
- * and prepares the list for reading.
- */
- void init();
-
- /**
* Set elapsed timestamp if the original timestamp is missing.
*/
void setElapsedTimestampNs(int64_t timestampNs) {
@@ -197,39 +144,184 @@
return &mValues;
}
+ // Default value = false
+ inline bool shouldTruncateTimestamp() const {
+ return mTruncateTimestamp;
+ }
+
+ // Returns the index of the uid field within the FieldValues vector if the
+ // uid exists. If there is no uid field, returns -1.
+ //
+ // If the index within the atom definition is desired, do the following:
+ // int vectorIndex = LogEvent.getUidFieldIndex();
+ // if (vectorIndex != -1) {
+ // FieldValue& v = LogEvent.getValues()[vectorIndex];
+ // int atomIndex = v.mField.getPosAtDepth(0);
+ // }
+ // Note that atomIndex is 1-indexed.
+ inline int getUidFieldIndex() {
+ return static_cast<int>(mUidFieldIndex);
+ }
+
+ // Returns whether this LogEvent has an AttributionChain.
+ // If it does and indexRange is not a nullptr, populate indexRange with the start and end index
+ // of the AttributionChain within mValues.
+ bool hasAttributionChain(std::pair<int, int>* indexRange = nullptr) const;
+
+ // Returns the index of the exclusive state field within the FieldValues vector if
+ // an exclusive state exists. If there is no exclusive state field, returns -1.
+ //
+ // If the index within the atom definition is desired, do the following:
+ // int vectorIndex = LogEvent.getExclusiveStateFieldIndex();
+ // if (vectorIndex != -1) {
+ // FieldValue& v = LogEvent.getValues()[vectorIndex];
+ // int atomIndex = v.mField.getPosAtDepth(0);
+ // }
+ // Note that atomIndex is 1-indexed.
+ inline int getExclusiveStateFieldIndex() const {
+ return static_cast<int>(mExclusiveStateFieldIndex);
+ }
+
+ // If a reset state is not sent in the StatsEvent, returns -1. Note that a
+ // reset state is sent if and only if a reset should be triggered.
+ inline int getResetState() const {
+ return mResetState;
+ }
+
inline LogEvent makeCopy() {
return LogEvent(*this);
}
+ template <class T>
+ status_t updateValue(size_t key, T& value, Type type) {
+ int field = getSimpleField(key);
+ for (auto& fieldValue : mValues) {
+ if (fieldValue.mField.getField() == field) {
+ if (fieldValue.mValue.getType() == type) {
+ fieldValue.mValue = Value(value);
+ return OK;
+ } else {
+ return BAD_TYPE;
+ }
+ }
+ }
+ return BAD_INDEX;
+ }
+
+ bool isValid() const {
+ return mValid;
+ }
+
private:
/**
* Only use this if copy is absolutely needed.
*/
- LogEvent(const LogEvent&);
+ LogEvent(const LogEvent&) = default;
+
+ void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+ void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
+
+ void parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex = -1);
+ void parseIsUidAnnotation(uint8_t annotationType);
+ void parseTruncateTimestampAnnotation(uint8_t annotationType);
+ void parsePrimaryFieldAnnotation(uint8_t annotationType);
+ void parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, int firstUidInChainIndex);
+ void parseExclusiveStateAnnotation(uint8_t annotationType);
+ void parseTriggerStateResetAnnotation(uint8_t annotationType);
+ void parseStateNestedAnnotation(uint8_t annotationType);
+ bool checkPreviousValueType(Type expected);
/**
- * Parses a log_msg into a LogEvent object.
+ * The below two variables are only valid during the execution of
+ * parseBuffer. There are no guarantees about the state of these variables
+ * before/after.
*/
- void init(android_log_context context);
+ uint8_t* mBuf;
+ uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
+
+ bool mValid = true; // stores whether the event we received from the socket is valid
+
+ /**
+ * Side-effects:
+ * If there is enough space in buffer to read value of type T
+ * - move mBuf past the value that was just read
+ * - decrement mRemainingLen by size of T
+ * Else
+ * - set mValid to false
+ */
+ template <class T>
+ T readNextValue() {
+ T value;
+ if (mRemainingLen < sizeof(T)) {
+ mValid = false;
+ value = 0; // all primitive types can successfully cast 0
+ } else {
+ // When alignof(T) == 1, hopefully the compiler can optimize away
+ // this conditional as always true.
+ if ((reinterpret_cast<uintptr_t>(mBuf) % alignof(T)) == 0) {
+ // We're properly aligned, and can safely make this assignment.
+ value = *((T*)mBuf);
+ } else {
+ // We need to use memcpy. It's slower, but safe.
+ memcpy(&value, mBuf, sizeof(T));
+ }
+ mBuf += sizeof(T);
+ mRemainingLen -= sizeof(T);
+ }
+ return value;
+ }
+
+ template <class T>
+ void addToValues(int32_t* pos, int32_t depth, T& value, bool* last) {
+ Field f = Field(mTagId, pos, depth);
+ // do not decorate last position at depth 0
+ for (int i = 1; i < depth; i++) {
+ if (last[i]) f.decorateLastPos(i);
+ }
+
+ Value v = Value(value);
+ mValues.push_back(FieldValue(f, v));
+ }
+
+ uint8_t getTypeId(uint8_t typeInfo);
+ uint8_t getNumAnnotations(uint8_t typeInfo);
// The items are naturally sorted in DFS order as we read them. this allows us to do fast
// matching.
std::vector<FieldValue> mValues;
- // This field is used when statsD wants to create log event object and write fields to it. After
- // calling init() function, this object would be destroyed to save memory usage.
- // When the log event is created from log msg, this field is never initiated.
- android_log_context mContext = NULL;
-
// The timestamp set by the logd.
int64_t mLogdTimestampNs;
// The elapsed timestamp set by statsd log writer.
int64_t mElapsedTimestampNs;
- int mTagId;
+ // The atom tag of the event (defaults to 0 if client does not
+ // appropriately set the atom id).
+ int mTagId = 0;
- uint32_t mLogUid;
+ // The uid of the logging client (defaults to -1).
+ int32_t mLogUid = -1;
+
+ // The pid of the logging client (defaults to -1).
+ int32_t mLogPid = -1;
+
+ // Annotations
+ bool mTruncateTimestamp = false;
+ int mResetState = -1;
+
+ // Indexes within the FieldValue vector can be stored in 7 bits because
+ // that's the assumption enforced by the encoding used in FieldValue.
+ int8_t mUidFieldIndex = -1;
+ int8_t mAttributionChainStartIndex = -1;
+ int8_t mAttributionChainEndIndex = -1;
+ int8_t mExclusiveStateFieldIndex = -1;
};
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 739c597..cd9c4e5 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -20,10 +20,9 @@
#include "StatsService.h"
#include "socket/StatsSocketListener.h"
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <hidl/HidlTransportSupport.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_process.h>
+#include <android/binder_manager.h>
#include <utils/Looper.h>
#include <stdio.h>
@@ -33,31 +32,35 @@
using namespace android;
using namespace android::os::statsd;
+using ::ndk::SharedRefBase;
+using std::shared_ptr;
+using std::make_shared;
-/**
- * Thread function data.
- */
-struct log_reader_thread_data {
- sp<StatsService> service;
-};
+shared_ptr<StatsService> gStatsService = nullptr;
+sp<StatsSocketListener> gSocketListener = nullptr;
-
-sp<StatsService> gStatsService = nullptr;
-
-void sigHandler(int sig) {
- if (gStatsService != nullptr) {
- gStatsService->Terminate();
+void signalHandler(int sig) {
+ if (sig == SIGPIPE) {
+ // ShellSubscriber uses SIGPIPE as a signal to detect the end of the
+ // client process. Don't prematurely exit(1) here. Instead, ignore the
+ // signal and allow the write call to return EPIPE.
+ ALOGI("statsd received SIGPIPE. Ignoring signal.");
+ return;
}
+
+ if (gSocketListener != nullptr) gSocketListener->stopListener();
+ if (gStatsService != nullptr) gStatsService->Terminate();
ALOGW("statsd terminated on receiving signal %d.", sig);
exit(1);
}
-void registerSigHandler()
+void registerSignalHandlers()
{
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
- sa.sa_handler = sigHandler;
+ sa.sa_handler = signalHandler;
+ sigaction(SIGPIPE, &sa, nullptr);
sigaction(SIGHUP, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGQUIT, &sa, nullptr);
@@ -69,35 +72,33 @@
sp<Looper> looper(Looper::prepare(0 /* opts */));
// Set up the binder
- sp<ProcessState> ps(ProcessState::self());
- ps->setThreadPoolMaxThreadCount(9);
- ps->startThreadPool();
- ps->giveThreadPoolName();
- IPCThreadState::self()->disableBackgroundScheduling(true);
+ ABinderProcess_setThreadPoolMaxThreadCount(9);
+ ABinderProcess_startThreadPool();
std::shared_ptr<LogEventQueue> eventQueue =
std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
// Create the service
- gStatsService = new StatsService(looper, eventQueue);
- if (defaultServiceManager()->addService(String16("stats"), gStatsService, false,
- IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO)
- != 0) {
+ gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
+ // TODO(b/149582373): Set DUMP_FLAG_PROTO once libbinder_ndk supports
+ // setting dumpsys priorities.
+ binder_status_t status = AServiceManager_addService(gStatsService->asBinder().get(), "stats");
+ if (status != STATUS_OK) {
ALOGE("Failed to add service as AIDL service");
return -1;
}
- registerSigHandler();
+ registerSignalHandlers();
gStatsService->sayHiToStatsCompanion();
gStatsService->Startup();
- sp<StatsSocketListener> socketListener = new StatsSocketListener(eventQueue);
+ gSocketListener = new StatsSocketListener(eventQueue);
ALOGI("Statsd starts to listen to socket.");
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
- if (socketListener->startListener(600)) {
+ if (gSocketListener->startListener(600)) {
exit(1);
}
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 2cbe2e9..2b4c6a3 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -81,18 +81,17 @@
return matched;
}
-bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value,
- const string& str_match) {
- if (isAttributionUidField(field, value)) {
- int uid = value.int_value;
+bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) {
+ if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
+ int uid = fieldValue.mValue.int_value;
auto aidIt = UidMap::sAidToUidMapping.find(str_match);
if (aidIt != UidMap::sAidToUidMapping.end()) {
return ((int)aidIt->second) == uid;
}
std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
return packageNames.find(str_match) != packageNames.end();
- } else if (value.getType() == STRING) {
- return value.str_value == str_match;
+ } else if (fieldValue.mValue.getType() == STRING) {
+ return fieldValue.mValue.str_value == str_match;
}
return false;
}
@@ -228,8 +227,7 @@
}
case FieldValueMatcher::ValueMatcherCase::kEqString: {
for (int i = start; i < end; i++) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue,
- matcher.eq_string())) {
+ if (tryMatchString(uidMap, values[i], matcher.eq_string())) {
return true;
}
}
@@ -240,7 +238,7 @@
for (int i = start; i < end; i++) {
bool notEqAll = true;
for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ if (tryMatchString(uidMap, values[i], str)) {
notEqAll = false;
break;
}
@@ -255,7 +253,7 @@
const auto& str_list = matcher.eq_any_string();
for (int i = start; i < end; i++) {
for (const auto& str : str_list.str_value()) {
- if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) {
+ if (tryMatchString(uidMap, values[i], str)) {
return true;
}
}
@@ -357,9 +355,10 @@
bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
const LogEvent& event) {
- if (simpleMatcher.field_value_matcher_size() <= 0) {
- return event.GetTagId() == simpleMatcher.atom_id();
+ if (event.GetTagId() != simpleMatcher.atom_id()) {
+ return false;
}
+
for (const auto& matcher : simpleMatcher.field_value_matcher()) {
if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
return false;
diff --git a/cmds/statsd/src/metadata_util.cpp b/cmds/statsd/src/metadata_util.cpp
new file mode 100644
index 0000000..27ee59b
--- /dev/null
+++ b/cmds/statsd/src/metadata_util.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#include "FieldValue.h"
+#include "metadata_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using google::protobuf::RepeatedPtrField;
+
+void writeValueToProto(metadata::FieldValue* metadataFieldValue, const Value& value) {
+ std::string storage_value;
+ switch (value.getType()) {
+ case INT:
+ metadataFieldValue->set_value_int(value.int_value);
+ break;
+ case LONG:
+ metadataFieldValue->set_value_long(value.long_value);
+ break;
+ case FLOAT:
+ metadataFieldValue->set_value_float(value.float_value);
+ break;
+ case DOUBLE:
+ metadataFieldValue->set_value_double(value.double_value);
+ break;
+ case STRING:
+ metadataFieldValue->set_value_str(value.str_value.c_str());
+ break;
+ case STORAGE: // byte array
+ storage_value = ((char*) value.storage_value.data());
+ metadataFieldValue->set_value_storage(storage_value);
+ break;
+ default:
+ break;
+ }
+}
+
+void writeMetricDimensionKeyToMetadataDimensionKey(
+ const MetricDimensionKey& metricKey,
+ metadata::MetricDimensionKey* metadataMetricKey) {
+ for (const FieldValue& fieldValue : metricKey.getDimensionKeyInWhat().getValues()) {
+ metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_dimension_key_in_what();
+ metadata::Field* metadataField = metadataFieldValue->mutable_field();
+ metadataField->set_tag(fieldValue.mField.getTag());
+ metadataField->set_field(fieldValue.mField.getField());
+ writeValueToProto(metadataFieldValue, fieldValue.mValue);
+ }
+
+ for (const FieldValue& fieldValue : metricKey.getStateValuesKey().getValues()) {
+ metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_state_values_key();
+ metadata::Field* metadataField = metadataFieldValue->mutable_field();
+ metadataField->set_tag(fieldValue.mField.getTag());
+ metadataField->set_field(fieldValue.mField.getField());
+ writeValueToProto(metadataFieldValue, fieldValue.mValue);
+ }
+}
+
+void writeFieldValuesFromMetadata(
+ const RepeatedPtrField<metadata::FieldValue>& repeatedFieldValueList,
+ std::vector<FieldValue>* fieldValues) {
+ for (const metadata::FieldValue& metadataFieldValue : repeatedFieldValueList) {
+ Field field(metadataFieldValue.field().tag(), metadataFieldValue.field().field());
+ Value value;
+ switch (metadataFieldValue.value_case()) {
+ case metadata::FieldValue::ValueCase::kValueInt:
+ value = Value(metadataFieldValue.value_int());
+ break;
+ case metadata::FieldValue::ValueCase::kValueLong:
+ value = Value(metadataFieldValue.value_long());
+ break;
+ case metadata::FieldValue::ValueCase::kValueFloat:
+ value = Value(metadataFieldValue.value_float());
+ break;
+ case metadata::FieldValue::ValueCase::kValueDouble:
+ value = Value(metadataFieldValue.value_double());
+ break;
+ case metadata::FieldValue::ValueCase::kValueStr:
+ value = Value(metadataFieldValue.value_str());
+ break;
+ case metadata::FieldValue::ValueCase::kValueStorage:
+ value = Value(metadataFieldValue.value_storage());
+ break;
+ default:
+ break;
+ }
+ FieldValue fieldValue(field, value);
+ fieldValues->emplace_back(field, value);
+ }
+}
+
+MetricDimensionKey loadMetricDimensionKeyFromProto(
+ const metadata::MetricDimensionKey& metricDimensionKey) {
+ std::vector<FieldValue> dimKeyInWhatFieldValues;
+ writeFieldValuesFromMetadata(metricDimensionKey.dimension_key_in_what(),
+ &dimKeyInWhatFieldValues);
+ std::vector<FieldValue> stateValuesFieldValues;
+ writeFieldValuesFromMetadata(metricDimensionKey.state_values_key(), &stateValuesFieldValues);
+
+ HashableDimensionKey dimKeyInWhat(dimKeyInWhatFieldValues);
+ HashableDimensionKey stateValues(stateValuesFieldValues);
+ MetricDimensionKey metricKey(dimKeyInWhat, stateValues);
+ return metricKey;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metadata_util.h b/cmds/statsd/src/metadata_util.h
new file mode 100644
index 0000000..84a39ff
--- /dev/null
+++ b/cmds/statsd/src/metadata_util.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+#include "HashableDimensionKey.h"
+
+#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void writeMetricDimensionKeyToMetadataDimensionKey(const MetricDimensionKey& metricKey,
+ metadata::MetricDimensionKey* metadataMetricKey);
+
+MetricDimensionKey loadMetricDimensionKeyFromProto(
+ const metadata::MetricDimensionKey& metricDimensionKey);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index c023e6f..5739612 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -18,13 +18,15 @@
#include "Log.h"
#include "CountMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
+#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
+#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
@@ -37,6 +39,7 @@
using std::string;
using std::unordered_map;
using std::vector;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -48,28 +51,31 @@
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
const int FIELD_ID_IS_ACTIVE = 14;
// for CountMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for CountMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
+const int FIELD_ID_SLICE_BY_STATE = 6;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for CountBucketInfo
const int FIELD_ID_COUNT = 3;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
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)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard) {
+CountMetricProducer::CountMetricProducer(
+ const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
+ 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, initialConditionCache, wizard,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -82,12 +88,7 @@
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
- HasPositionALL(metric.dimensions_in_condition());
-
- if (metric.has_dimensions_in_condition()) {
- translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
- }
+ mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
@@ -100,7 +101,13 @@
mConditionSliced = true;
}
- mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
+ for (const auto& stateLink : metric.state_link()) {
+ Metric2State ms;
+ ms.stateAtomId = stateLink.state_atom_id();
+ translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
+ translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
+ mMetric2StateLinks.push_back(ms);
+ }
flushIfNeededLocked(startTimeNs);
// Adjust start for partial bucket
@@ -114,6 +121,14 @@
VLOG("~CountMetricProducer() called");
}
+void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) {
+ VLOG("CountMetric %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);
+}
+
void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (mCurrentSlicedCounter == nullptr ||
mCurrentSlicedCounter->size() == 0) {
@@ -124,10 +139,9 @@
(unsigned long)mCurrentSlicedCounter->size());
if (verbose) {
for (const auto& it : *mCurrentSlicedCounter) {
- fprintf(out, "\t(what)%s\t(condition)%s %lld\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getDimensionKeyInCondition().toString().c_str(),
- (unsigned long long)it.second);
+ fprintf(out, "\t(what)%s\t(state)%s %lld\n",
+ it.first.getDimensionKeyInWhat().toString().c_str(),
+ it.first.getStateValuesKey().toString().c_str(), (unsigned long long)it.second);
}
}
}
@@ -171,13 +185,6 @@
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
- if (!mDimensionsInCondition.empty()) {
- uint64_t dimenPathToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
- writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
- protoOutput->end(dimenPathToken);
- }
-
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
@@ -195,22 +202,16 @@
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
-
- if (dimensionKey.hasDimensionKeyInCondition()) {
- uint64_t dimensionInConditionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
- str_set, protoOutput);
- protoOutput->end(dimensionInConditionToken);
- }
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
- FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
- str_set, protoOutput);
- }
+ }
+ // Then fill slice_by_state.
+ for (auto state : dimensionKey.getStateValuesKey().getValues()) {
+ uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SLICE_BY_STATE);
+ writeStateToProto(state, protoOutput);
+ protoOutput->end(stateToken);
}
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
@@ -266,6 +267,7 @@
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("CountMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
@@ -275,12 +277,12 @@
void CountMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) {
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
int64_t eventTimeNs = event.GetElapsedTimestampNs();
flushIfNeededLocked(eventTimeNs);
- if (condition == false) {
+ if (!condition) {
return;
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index b4a910c..f05fb06 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -17,15 +17,16 @@
#ifndef COUNT_METRIC_PRODUCER_H
#define COUNT_METRIC_PRODUCER_H
-#include <unordered_map>
-
#include <android/util/ProtoOutputStream.h>
#include <gtest/gtest_prod.h>
-#include "../anomaly/AnomalyTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../matchers/matcher_util.h"
+
+#include <unordered_map>
+
#include "MetricProducer.h"
+#include "anomaly/AnomalyTracker.h"
+#include "condition/ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matchers/matcher_util.h"
#include "stats_util.h"
namespace android {
@@ -40,17 +41,27 @@
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);
+ CountMetricProducer(
+ const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
+ 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 = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~CountMetricProducer();
+ void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) override;
+
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
@@ -99,9 +110,11 @@
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
- FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
- FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
+ FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit);
+
+ FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket);
+ FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 96fbf7f..e9b0438 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -36,6 +36,7 @@
using std::string;
using std::unordered_map;
using std::vector;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -47,30 +48,32 @@
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
const int FIELD_ID_IS_ACTIVE = 14;
// for DurationMetricDataWrapper
const int FIELD_ID_DATA = 1;
// for DurationMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
+const int FIELD_ID_SLICE_BY_STATE = 6;
// for DurationBucketInfo
const int FIELD_ID_DURATION = 3;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
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)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+DurationMetricProducer::DurationMetricProducer(
+ const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
+ 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, initialConditionCache, wizard,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
@@ -100,12 +103,7 @@
ALOGE("Position ANY in dimension_in_what not supported.");
}
- if (metric.has_dimensions_in_condition()) {
- translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
- }
-
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
- HasPositionALL(metric.dimensions_in_condition());
+ mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
@@ -115,19 +113,23 @@
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
+ mConditionSliced = true;
}
- mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
mUnSlicedPartCondition = ConditionState::kUnknown;
+ for (const auto& stateLink : metric.state_link()) {
+ Metric2State ms;
+ ms.stateAtomId = stateLink.state_atom_id();
+ translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
+ translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
+ mMetric2StateLinks.push_back(ms);
+ }
+
mUseWhatDimensionAsInternalDimension = equalDimensions(mDimensionsInWhat, mInternalDimensions);
- if (mWizard != nullptr && mConditionTrackerIndex >= 0) {
- mSameConditionDimensionsInTracker =
- mWizard->equalOutputDimensions(mConditionTrackerIndex, mDimensionsInCondition);
- if (mMetric2ConditionLinks.size() == 1) {
- mHasLinksToAllConditionDimensionsInTracker =
- mWizard->equalOutputDimensions(mConditionTrackerIndex,
- mMetric2ConditionLinks.begin()->conditionFields);
- }
+ if (mWizard != nullptr && mConditionTrackerIndex >= 0 &&
+ mMetric2ConditionLinks.size() == 1) {
+ mHasLinksToAllConditionDimensionsInTracker = mWizard->equalOutputDimensions(
+ mConditionTrackerIndex, mMetric2ConditionLinks.begin()->conditionFields);
}
flushIfNeededLocked(startTimeNs);
// Adjust start for partial bucket
@@ -158,33 +160,58 @@
return anomalyTracker;
}
+void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState,
+ const FieldValue& newState) {
+ // Check if this metric has a StateMap. If so, map the new state value to
+ // the correct state group id.
+ FieldValue newStateCopy = newState;
+ mapStateValue(atomId, &newStateCopy);
+
+ flushIfNeededLocked(eventTimeNs);
+
+ // Each duration tracker is mapped to a different whatKey (a set of values from the
+ // dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
+ // state change event are a subset of the tracker's whatKey field values.
+ //
+ // Ex. For a duration metric dimensioned on uid and tag:
+ // DurationTracker1 whatKey = uid: 1001, tag: 1
+ // DurationTracker2 whatKey = uid: 1002, tag 1
+ //
+ // If the state change primaryKey = uid: 1001, we only notify DurationTracker1 of a state
+ // change.
+ for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ if (!containsLinkedStateValues(whatIt.first, primaryKey, mMetric2StateLinks, atomId)) {
+ continue;
+ }
+ whatIt.second->onStateChanged(eventTimeNs, atomId, newStateCopy);
+ }
+}
+
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
const MetricDimensionKey& eventKey) const {
switch (mAggregationType) {
case DurationMetric_AggregationType_SUM:
return make_unique<OringDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
- mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
- mTimeBaseNs, mBucketSizeNs, mConditionSliced,
- mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
+ mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
case DurationMetric_AggregationType_MAX_SPARSE:
return make_unique<MaxDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
- mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
- mTimeBaseNs, mBucketSizeNs, mConditionSliced,
- mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs,
+ mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
}
}
// SlicedConditionChange optimization case 1:
// 1. If combination condition, logical operation is AND, only one sliced child predicate.
-// 2. No condition in dimension
-// 3. The links covers all dimension fields in the sliced child condition predicate.
+// 2. The links covers all dimension fields in the sliced child condition predicate.
void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition,
const int64_t eventTime) {
if (mMetric2ConditionLinks.size() != 1 ||
- !mHasLinksToAllConditionDimensionsInTracker ||
- !mDimensionsInCondition.empty()) {
+ !mHasLinksToAllConditionDimensionsInTracker) {
return;
}
@@ -213,15 +240,11 @@
mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
HashableDimensionKey linkedConditionDimensionKey;
- getDimensionForCondition(whatIt.first.getValues(),
- mMetric2ConditionLinks[0],
+ getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
&linkedConditionDimensionKey);
if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
trueConditionDimensions.end()) {
- for (auto& condIt : whatIt.second) {
- condIt.second->onConditionChanged(
- currentUnSlicedPartCondition, eventTime);
- }
+ whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
}
}
} else {
@@ -229,109 +252,15 @@
if (currentUnSlicedPartCondition) {
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
HashableDimensionKey linkedConditionDimensionKey;
- getDimensionForCondition(whatIt.first.getValues(),
- mMetric2ConditionLinks[0],
+ getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
&linkedConditionDimensionKey);
if (dimensionsChangedToTrue->find(linkedConditionDimensionKey) !=
dimensionsChangedToTrue->end()) {
- for (auto& condIt : whatIt.second) {
- condIt.second->onConditionChanged(true, eventTime);
- }
+ whatIt.second->onConditionChanged(true, eventTime);
}
if (dimensionsChangedToFalse->find(linkedConditionDimensionKey) !=
dimensionsChangedToFalse->end()) {
- for (auto& condIt : whatIt.second) {
- condIt.second->onConditionChanged(false, eventTime);
- }
- }
- }
- }
- }
-}
-
-
-// SlicedConditionChange optimization case 2:
-// 1. If combination condition, logical operation is AND, only one sliced child predicate.
-// 2. Has dimensions_in_condition and it equals to the output dimensions of the sliced predicate.
-void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(bool condition,
- const int64_t eventTime) {
- if (mMetric2ConditionLinks.size() > 1 || !mSameConditionDimensionsInTracker) {
- return;
- }
-
- auto dimensionsChangedToTrue = mWizard->getChangedToTrueDimensions(mConditionTrackerIndex);
- auto dimensionsChangedToFalse = mWizard->getChangedToFalseDimensions(mConditionTrackerIndex);
-
- bool currentUnSlicedPartCondition = true;
- if (!mWizard->IsSimpleCondition(mConditionTrackerIndex)) {
- ConditionState unslicedPartState =
- mWizard->getUnSlicedPartConditionState(mConditionTrackerIndex);
- // When the unsliced part is still false, return directly.
- if (mUnSlicedPartCondition == ConditionState::kFalse &&
- unslicedPartState == ConditionState::kFalse) {
- return;
- }
- mUnSlicedPartCondition = unslicedPartState;
- currentUnSlicedPartCondition = mUnSlicedPartCondition > 0;
- }
-
- const std::set<HashableDimensionKey>* trueDimensionsToProcess = nullptr;
- const std::set<HashableDimensionKey>* falseDimensionsToProcess = nullptr;
-
- std::set<HashableDimensionKey> currentTrueConditionDimensions;
- if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
- (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
- mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, ¤tTrueConditionDimensions);
- trueDimensionsToProcess = ¤tTrueConditionDimensions;
- } else if (currentUnSlicedPartCondition) {
- // Handles the condition change from the sliced predicate. If the unsliced condition state
- // is not true, not need to do anything.
- trueDimensionsToProcess = dimensionsChangedToTrue;
- falseDimensionsToProcess = dimensionsChangedToFalse;
- }
-
- if (trueDimensionsToProcess == nullptr && falseDimensionsToProcess == nullptr) {
- return;
- }
-
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- if (falseDimensionsToProcess != nullptr) {
- for (const auto& changedDim : *falseDimensionsToProcess) {
- auto condIt = whatIt.second.find(changedDim);
- if (condIt != whatIt.second.end()) {
- condIt->second->onConditionChanged(false, eventTime);
- }
- }
- }
- if (trueDimensionsToProcess != nullptr) {
- HashableDimensionKey linkedConditionDimensionKey;
- if (!trueDimensionsToProcess->empty() && mMetric2ConditionLinks.size() == 1) {
- getDimensionForCondition(whatIt.first.getValues(),
- mMetric2ConditionLinks[0],
- &linkedConditionDimensionKey);
- }
- for (auto& trueDim : *trueDimensionsToProcess) {
- auto condIt = whatIt.second.find(trueDim);
- if (condIt != whatIt.second.end()) {
- condIt->second->onConditionChanged(
- currentUnSlicedPartCondition, eventTime);
- } else {
- if (mMetric2ConditionLinks.size() == 0 ||
- trueDim.contains(linkedConditionDimensionKey)) {
- if (!whatIt.second.empty()) {
- auto newEventKey = MetricDimensionKey(whatIt.first, trueDim);
- if (hitGuardRailLocked(newEventKey)) {
- continue;
- }
- unique_ptr<DurationTracker> newTracker =
- whatIt.second.begin()->second->clone(eventTime);
- if (newTracker != nullptr) {
- newTracker->setEventKey(newEventKey);
- newTracker->onConditionChanged(true, eventTime);
- whatIt.second[trueDim] = std::move(newTracker);
- }
- }
- }
+ whatIt.second->onConditionChanged(false, eventTime);
}
}
}
@@ -341,85 +270,14 @@
void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overallCondition,
const int64_t eventTimeNs) {
bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
- if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker &&
- mDimensionsInCondition.empty()) {
+ if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker) {
onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTimeNs);
return;
}
- if (changeDimTrackable && mSameConditionDimensionsInTracker &&
- mMetric2ConditionLinks.size() <= 1) {
- onSlicedConditionMayChangeLocked_opt2(overallCondition, eventTimeNs);
- return;
- }
-
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
- }
- }
-
- if (mDimensionsInCondition.empty()) {
- return;
- }
-
- if (mMetric2ConditionLinks.empty()) {
- std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
- mWizard->getMetConditionDimension(mConditionTrackerIndex, mDimensionsInCondition,
- !mSameConditionDimensionsInTracker,
- &conditionDimensionsKeySet);
- for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (const auto& pair : whatIt.second) {
- conditionDimensionsKeySet.erase(pair.first);
- }
- }
- for (const auto& conditionDimension : conditionDimensionsKeySet) {
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- if (!whatIt.second.empty()) {
- auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
- if (hitGuardRailLocked(newEventKey)) {
- continue;
- }
- unique_ptr<DurationTracker> newTracker =
- whatIt.second.begin()->second->clone(eventTimeNs);
- if (newTracker != nullptr) {
- newTracker->setEventKey(MetricDimensionKey(newEventKey));
- newTracker->onSlicedConditionMayChange(overallCondition, eventTimeNs);
- whatIt.second[conditionDimension] = std::move(newTracker);
- }
- }
- }
- }
- } else {
- for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- ConditionKey conditionKey;
- for (const auto& link : mMetric2ConditionLinks) {
- getDimensionForCondition(whatIt.first.getValues(), link,
- &conditionKey[link.conditionId]);
- }
- std::unordered_set<HashableDimensionKey> conditionDimensionsKeys;
- mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &conditionDimensionsKeys);
-
- for (const auto& conditionDimension : conditionDimensionsKeys) {
- if (!whatIt.second.empty() &&
- whatIt.second.find(conditionDimension) == whatIt.second.end()) {
- auto newEventKey = MetricDimensionKey(whatIt.first, conditionDimension);
- if (hitGuardRailLocked(newEventKey)) {
- continue;
- }
- auto newTracker = whatIt.second.begin()->second->clone(eventTimeNs);
- if (newTracker != nullptr) {
- newTracker->setEventKey(newEventKey);
- newTracker->onSlicedConditionMayChange(overallCondition, eventTimeNs);
- whatIt.second[conditionDimension] = std::move(newTracker);
- }
- }
- }
- }
+ whatIt.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
}
}
@@ -453,18 +311,14 @@
}
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onConditionChanged(mIsActive, eventTimeNs);
- }
+ whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
}
} else if (mIsActive) {
flushIfNeededLocked(eventTimeNs);
onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs);
} else { // mConditionSliced == true && !mIsActive
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onConditionChanged(mIsActive, eventTimeNs);
- }
+ whatIt.second->onConditionChanged(mIsActive, eventTimeNs);
}
}
}
@@ -480,9 +334,7 @@
flushIfNeededLocked(eventTime);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->onConditionChanged(conditionMet, eventTime);
- }
+ whatIt.second->onConditionChanged(conditionMet, eventTime);
}
}
@@ -526,12 +378,6 @@
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
- if (!mDimensionsInCondition.empty()) {
- uint64_t dimenPathToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
- writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
- protoOutput->end(dimenPathToken);
- }
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
@@ -551,22 +397,16 @@
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
-
- if (dimensionKey.hasDimensionKeyInCondition()) {
- uint64_t dimensionInConditionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
- str_set, protoOutput);
- protoOutput->end(dimensionInConditionToken);
- }
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
- FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
- str_set, protoOutput);
- }
+ }
+ // Then fill slice_by_state.
+ for (auto state : dimensionKey.getStateValuesKey().getValues()) {
+ uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SLICE_BY_STATE);
+ writeStateToProto(state, protoOutput);
+ protoOutput->end(stateToken);
}
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
@@ -614,19 +454,11 @@
const int64_t& nextBucketStartTimeNs) {
for (auto whatIt = mCurrentSlicedDurationTrackerMap.begin();
whatIt != mCurrentSlicedDurationTrackerMap.end();) {
- for (auto it = whatIt->second.begin(); it != whatIt->second.end();) {
- if (it->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
- VLOG("erase bucket for key %s %s", whatIt->first.toString().c_str(),
- it->first.toString().c_str());
- it = whatIt->second.erase(it);
- } else {
- ++it;
- }
- }
- if (whatIt->second.empty()) {
+ if (whatIt->second->flushCurrentBucket(eventTimeNs, &mPastBuckets)) {
+ VLOG("erase bucket for key %s", whatIt->first.toString().c_str());
whatIt = mCurrentSlicedDurationTrackerMap.erase(whatIt);
} else {
- whatIt++;
+ ++whatIt;
}
}
StatsdStats::getInstance().noteBucketCount(mMetricId);
@@ -642,34 +474,15 @@
(unsigned long)mCurrentSlicedDurationTrackerMap.size());
if (verbose) {
for (const auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (const auto& slice : whatIt.second) {
- fprintf(out, "\t(what)%s\t(condition)%s\n", whatIt.first.toString().c_str(),
- slice.first.toString().c_str());
- slice.second->dumpStates(out, verbose);
- }
+ fprintf(out, "\t(what)%s\n", whatIt.first.toString().c_str());
+ whatIt.second->dumpStates(out, verbose);
}
}
}
bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(newKey.getDimensionKeyInWhat());
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- auto condIt = whatIt->second.find(newKey.getDimensionKeyInCondition());
- if (condIt != whatIt->second.end()) {
- return false;
- }
- if (whatIt->second.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = whatIt->second.size() + 1;
- StatsdStats::getInstance().noteMetricDimensionInConditionSize(
- mConfigKey, mMetricId, newTupleCount);
- // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
- if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
- ALOGE("DurationMetric %lld dropping data for condition dimension key %s",
- (long long)mMetricId, newKey.getDimensionKeyInCondition().toString().c_str());
- return true;
- }
- }
- } else {
+ if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
@@ -679,6 +492,7 @@
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
ALOGE("DurationMetric %lld dropping data for what dimension key %s",
(long long)mMetricId, newKey.getDimensionKeyInWhat().toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
@@ -690,46 +504,36 @@
const ConditionKey& conditionKeys,
bool condition, const LogEvent& event) {
const auto& whatKey = eventKey.getDimensionKeyInWhat();
- const auto& condKey = eventKey.getDimensionKeyInCondition();
-
auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
if (hitGuardRailLocked(eventKey)) {
return;
}
- mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
- } else {
- if (whatIt->second.find(condKey) == whatIt->second.end()) {
- if (hitGuardRailLocked(eventKey)) {
- return;
- }
- mCurrentSlicedDurationTrackerMap[whatKey][condKey] = createDurationTracker(eventKey);
- }
+ mCurrentSlicedDurationTrackerMap[whatKey] = createDurationTracker(eventKey);
}
- auto it = mCurrentSlicedDurationTrackerMap.find(whatKey)->second.find(condKey);
+ auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
if (mUseWhatDimensionAsInternalDimension) {
- it->second->noteStart(whatKey, condition,
- event.GetElapsedTimestampNs(), conditionKeys);
+ it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
return;
}
if (mInternalDimensions.empty()) {
- it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
- event.GetElapsedTimestampNs(), conditionKeys);
+ it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(),
+ conditionKeys);
} else {
HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
- it->second->noteStart(
- dimensionKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
+ it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(),
+ conditionKeys);
}
}
void DurationMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKeys, bool condition,
- const LogEvent& event) {
+ const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
ALOGW("Not used in duration tracker.");
}
@@ -747,18 +551,49 @@
// Handles Stopall events.
if (matcherIndex == mStopAllIndex) {
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
- for (auto& pair : whatIt.second) {
- pair.second->noteStopAll(event.GetElapsedTimestampNs());
- }
+ whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
}
return;
}
- HashableDimensionKey dimensionInWhat;
+ HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
if (!mDimensionsInWhat.empty()) {
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
- } else {
- dimensionInWhat = DEFAULT_DIMENSION_KEY;
+ }
+
+ // Stores atom id to primary key pairs for each state atom that the metric is
+ // sliced by.
+ std::map<int, 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
+ // that will be used to query StateTracker for the correct state value.
+ for (const auto& stateLink : mMetric2StateLinks) {
+ getDimensionForState(event.getValues(), stateLink,
+ &statePrimaryKeys[stateLink.stateAtomId]);
+ }
+
+ // For each sliced state, query StateTracker for the state value using
+ // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
+ //
+ // Expected functionality: for any case where the MetricStateLinks are
+ // initialized incorrectly (ex. # of state links != # of primary fields, no
+ // links are provided for a state with primary fields, links are provided
+ // in the wrong order, etc.), StateTracker will simply return kStateUnknown
+ // when queried using an incorrect key.
+ HashableDimensionKey stateValuesKey = DEFAULT_DIMENSION_KEY;
+ for (auto atomId : mSlicedStateAtoms) {
+ FieldValue value;
+ if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
+ // found a primary key for this state, query using the key
+ queryStateValue(atomId, statePrimaryKeys[atomId], &value);
+ } else {
+ // if no MetricStateLinks exist for this state atom,
+ // query using the default dimension key (empty HashableDimensionKey)
+ queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
+ }
+ mapStateValue(atomId, &value);
+ stateValuesKey.addValue(value);
}
// Handles Stop events.
@@ -766,9 +601,7 @@
if (mUseWhatDimensionAsInternalDimension) {
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& condIt : whatIt->second) {
- condIt.second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
- }
+ whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
}
return;
}
@@ -780,62 +613,31 @@
auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& condIt : whatIt->second) {
- condIt.second->noteStop(
- internalDimensionKey, event.GetElapsedTimestampNs(), false);
- }
+ whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
}
return;
}
bool condition;
ConditionKey conditionKey;
- std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
if (mConditionSliced) {
for (const auto& link : mMetric2ConditionLinks) {
getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
}
auto conditionState =
- mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &dimensionKeysInCondition);
+ mWizard->query(mConditionTrackerIndex, conditionKey,
+ !mHasLinksToAllConditionDimensionsInTracker);
condition = conditionState == ConditionState::kTrue;
- if (mDimensionsInCondition.empty() && condition) {
- dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
- }
} else {
// TODO: The unknown condition state is not handled here, we should fix it.
condition = mCondition == ConditionState::kTrue;
- if (condition) {
- dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
- }
}
condition = condition && mIsActive;
- if (dimensionKeysInCondition.empty()) {
- handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY),
- conditionKey, condition, event);
- } else {
- auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
- // If the what dimension is already there, we should update all the trackers even
- // the condition is false.
- if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
- for (const auto& condIt : whatIt->second) {
- const bool cond = dimensionKeysInCondition.find(condIt.first) !=
- dimensionKeysInCondition.end() && condition;
- handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first),
- conditionKey, cond, event);
- dimensionKeysInCondition.erase(condIt.first);
- }
- }
- for (const auto& conditionDimension : dimensionKeysInCondition) {
- handleStartEvent(MetricDimensionKey(dimensionInWhat, conditionDimension), conditionKey,
- condition, event);
- }
- }
+ handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
+ event);
}
size_t DurationMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 56c9fd6..bfe1010 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -38,24 +38,33 @@
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);
+ DurationMetricProducer(
+ const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
+ 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 = {});
virtual ~DurationMetricProducer();
sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
+ void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) override;
+
protected:
void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKeys, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
@@ -127,13 +136,12 @@
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
// The duration trackers in the current bucket.
- std::unordered_map<HashableDimensionKey,
- std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>>
+ std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
mCurrentSlicedDurationTrackerMap;
// Helper function to create a duration tracker given the metric aggregation type.
std::unique_ptr<DurationTracker> createDurationTracker(
- const MetricDimensionKey& eventKey) const;
+ const MetricDimensionKey& eventKey) const;
// This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
@@ -146,12 +154,14 @@
FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState);
- FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade);
- FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket);
- FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
- FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket);
+
+ FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestSumDuration);
+ FRIEND_TEST(DurationMetricProducerTest_PartialBucket,
+ TestSumDurationWithSplitInFollowingBucket);
+ FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
+ FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 96133bd..dc0036a 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -36,6 +36,7 @@
using std::string;
using std::unordered_map;
using std::vector;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -51,11 +52,16 @@
const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1;
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)
- : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
+EventMetricProducer::EventMetricProducer(
+ const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
+ 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, initialConditionCache, wizard,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
@@ -138,16 +144,15 @@
void EventMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) {
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
if (!condition) {
return;
}
uint64_t wrapperToken =
mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
- const int64_t elapsedTimeNs = truncateTimestampIfNecessary(
- event.GetTagId(), event.GetElapsedTimestampNs());
+ const int64_t elapsedTimeNs = truncateTimestampIfNecessary(event);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long) elapsedTimeNs);
uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 74e6bc8..bfb2de3 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -33,17 +33,23 @@
class EventMetricProducer : public MetricProducer {
public:
- EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs);
+ EventMetricProducer(
+ const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
+ 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 = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~EventMetricProducer();
private:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
void onDumpReportLocked(const int64_t dumpTimeNs,
const bool include_current_partial_bucket,
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 4f437d1..020f4b6 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -46,19 +46,21 @@
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
const int FIELD_ID_IS_ACTIVE = 14;
// for GaugeMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
+// for SkippedBuckets
const int FIELD_ID_SKIPPED_START_MILLIS = 3;
const int FIELD_ID_SKIPPED_END_MILLIS = 4;
+const int FIELD_ID_SKIPPED_DROP_EVENT = 5;
+// for DumpEvent Proto
+const int FIELD_ID_BUCKET_DROP_REASON = 1;
+const int FIELD_ID_DROP_TIME = 2;
// for GaugeMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for GaugeBucketInfo
const int FIELD_ID_ATOM = 3;
const int FIELD_ID_ELAPSED_ATOM_TIMESTAMP = 4;
@@ -68,11 +70,15 @@
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)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+ 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, initialConditionCache, wizard,
+ eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
+ /*stateGroupMap=*/{}),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
@@ -113,10 +119,6 @@
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
}
- if (metric.has_dimensions_in_condition()) {
- translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
- }
-
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
@@ -125,19 +127,18 @@
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
+ mConditionSliced = true;
}
- mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
- HasPositionALL(metric.dimensions_in_condition());
+ mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+ mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(),
mBucketSizeNs);
}
- // Adjust start for partial bucket
+ // Adjust start for partial first bucket and then pull if needed
mCurrentBucketStartTimeNs = startTimeNs;
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
@@ -148,7 +149,7 @@
GaugeMetricProducer::~GaugeMetricProducer() {
VLOG("~GaugeMetricProducer() called");
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
- mPullerManager->UnRegisterReceiver(mPullTagId, this);
+ mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this);
}
}
@@ -162,10 +163,9 @@
(unsigned long)mCurrentSlicedBucket->size());
if (verbose) {
for (const auto& it : *mCurrentSlicedBucket) {
- fprintf(out, "\t(what)%s\t(condition)%s %d atoms\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getDimensionKeyInCondition().toString().c_str(),
- (int)it.second.size());
+ fprintf(out, "\t(what)%s\t(states)%s %d atoms\n",
+ it.first.getDimensionKeyInWhat().toString().c_str(),
+ it.first.getStateValuesKey().toString().c_str(), (int)it.second.size());
}
}
}
@@ -192,7 +192,7 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_IS_ACTIVE, isActiveLocked());
- if (mPastBuckets.empty()) {
+ if (mPastBuckets.empty() && mSkippedBuckets.empty()) {
return;
}
@@ -207,23 +207,25 @@
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
- if (!mDimensionsInCondition.empty()) {
- uint64_t dimenPathToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
- writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
- protoOutput->end(dimenPathToken);
- }
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
- for (const auto& pair : mSkippedBuckets) {
+ for (const auto& skippedBucket : mSkippedBuckets) {
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
- (long long)(NanoToMillis(pair.first)));
+ (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs)));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
- (long long)(NanoToMillis(pair.second)));
+ (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs)));
+
+ for (const auto& dropEvent : skippedBucket.dropEvents) {
+ uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SKIPPED_DROP_EVENT);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME, (long long) (NanoToMillis(dropEvent.dropTimeNs)));
+ protoOutput->end(dropEventToken);
+ }
protoOutput->end(wrapperToken);
}
@@ -240,22 +242,9 @@
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
-
- if (dimensionKey.hasDimensionKeyInCondition()) {
- uint64_t dimensionInConditionToken = protoOutput->start(
- FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(),
- str_set, protoOutput);
- protoOutput->end(dimensionInConditionToken);
- }
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
- FIELD_ID_DIMENSION_LEAF_IN_CONDITION,
- str_set, protoOutput);
- }
}
// Then fill bucket_info (GaugeBucketInfo).
@@ -282,11 +271,9 @@
protoOutput->end(atomsToken);
}
for (const auto& atom : bucket.mGaugeAtoms) {
- const int64_t elapsedTimestampNs =
- truncateTimestampIfNecessary(mAtomId, atom.mElapsedTimestamps);
- protoOutput->write(
- FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
- (long long)elapsedTimestampNs);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
+ FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
+ (long long)atom.mElapsedTimestampNs);
}
}
protoOutput->end(bucketInfoToken);
@@ -335,18 +322,17 @@
return;
}
vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, &allData)) {
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
return;
}
const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (pullDelayNs > mMaxPullDelayNs) {
ALOGE("Pull finish too late for atom %d", mPullTagId);
StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
return;
}
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
localCopy.setElapsedTimestampNs(timestampNs);
@@ -429,6 +415,13 @@
if (!pullSuccess || allData.size() == 0) {
return;
}
+ const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs;
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+ if (pullDelayNs > mMaxPullDelayNs) {
+ ALOGE("Pull finish too late for atom %d", mPullTagId);
+ StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+ return;
+ }
for (const auto& data : allData) {
if (mEventMatcherWizard->matchLogEvent(
*data, mWhatMatcherIndex) == MatchingState::kMatched) {
@@ -449,6 +442,7 @@
if (newTupleCount > mDimensionHardLimit) {
ALOGE("GaugeMetric %lld dropping data for dimension key %s",
(long long)mMetricId, newKey.toString().c_str());
+ StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId);
return true;
}
}
@@ -458,8 +452,8 @@
void GaugeMetricProducer::onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) {
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
if (condition == false) {
return;
}
@@ -488,7 +482,9 @@
if ((*mCurrentSlicedBucket)[eventKey].size() >= mGaugeAtomsPerDimensionLimit) {
return;
}
- GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs);
+
+ const int64_t truncatedElapsedTimestampNs = truncateTimestampIfNecessary(event);
+ GaugeAtom gaugeAtom(getGaugeFields(event), truncatedElapsedTimestampNs);
(*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
// Anomaly detection on gauge metric only works when there is one numeric
// field specified.
@@ -558,16 +554,16 @@
void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
+ int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
GaugeBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
- if (eventTimeNs < fullBucketEndTimeNs) {
- info.mBucketEndNs = eventTimeNs;
- } else {
- info.mBucketEndNs = fullBucketEndTimeNs;
- }
+ info.mBucketEndNs = bucketEndTime;
- if (info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs) {
+ // Add bucket to mPastBuckets if bucket is large enough.
+ // Otherwise, drop the bucket data and add bucket metadata to mSkippedBuckets.
+ bool isBucketLargeEnough = info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
+ if (isBucketLargeEnough) {
for (const auto& slice : *mCurrentSlicedBucket) {
info.mGaugeAtoms = slice.second;
auto& bucketList = mPastBuckets[slice.first];
@@ -576,7 +572,13 @@
slice.first.toString().c_str());
}
} else {
- mSkippedBuckets.emplace_back(info.mBucketStartNs, info.mBucketEndNs);
+ mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
+ mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
+ if (!maxDropEventsReached()) {
+ mCurrentSkippedBucket.dropEvents.emplace_back(
+ buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL));
+ }
+ mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
}
// If we have anomaly trackers, we need to update the partial bucket values.
@@ -595,6 +597,7 @@
StatsdStats::getInstance().noteBucketCount(mMetricId);
mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
+ mCurrentSkippedBucket.reset();
}
size_t GaugeMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index a612adf..2fc772b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -35,10 +35,10 @@
struct GaugeAtom {
GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
- : mFields(fields), mElapsedTimestamps(elapsedTimeNs) {
+ : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) {
}
std::shared_ptr<vector<FieldValue>> mFields;
- int64_t mElapsedTimestamps;
+ int64_t mElapsedTimestampNs;
};
struct GaugeBucket {
@@ -56,13 +56,16 @@
// producer always reports the guage at the earliest time of the bucket when the condition is met.
class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
- GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
- const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
- 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);
+ 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,
+ const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {});
virtual ~GaugeMetricProducer();
@@ -71,18 +74,23 @@
bool pullSuccess, int64_t originalPullTimeNs) override;
// GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
- void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) override {
+ void notifyAppUpgrade(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
if (!mSplitBucketForAppUpgrade) {
return;
}
- if (eventTimeNs > getCurrentBucketEndTimeNs()) {
- // Flush full buckets on the normal path up to the latest bucket boundary.
- flushIfNeededLocked(eventTimeNs);
+ flushLocked(eventTimeNs);
+ if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ pullAndMatchEventsLocked(eventTimeNs);
}
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
+ };
+
+ // GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
+ void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ flushLocked(eventTimeNs);
if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
pullAndMatchEventsLocked(eventTimeNs);
}
@@ -91,8 +99,8 @@
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
void onDumpReportLocked(const int64_t dumpTimeNs,
@@ -156,9 +164,6 @@
// this slice (ie, for partial buckets, we use the last partial bucket in this full bucket).
std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly;
- // Pairs of (elapsed start, elapsed end) denoting buckets that were skipped.
- std::list<std::pair<int64_t, int64_t>> mSkippedBuckets;
-
const int64_t mMinBucketSizeNs;
// Translate Atom based bucket to single numeric value bucket for anomaly and updates the map
@@ -191,13 +196,14 @@
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
- FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
- FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger);
FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput);
+
+ FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
+ FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 36434eb..fe143e4 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -16,8 +16,12 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
+
#include "MetricProducer.h"
+#include "../guardrail/StatsdStats.h"
+#include "state/StateTracker.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_ENUM;
using android::util::FIELD_TYPE_INT32;
@@ -39,6 +43,34 @@
const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_REMAINING_TTL_NANOS = 2;
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 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,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : mMetricId(metricId),
+ mConfigKey(key),
+ mTimeBaseNs(timeBaseNs),
+ mCurrentBucketStartTimeNs(timeBaseNs),
+ mCurrentBucketNum(0),
+ mCondition(initialCondition(conditionIndex, initialConditionCache)),
+ mConditionTrackerIndex(conditionIndex),
+ mConditionSliced(false),
+ mWizard(wizard),
+ mContainANYPositionInDimensionsInWhat(false),
+ mSliceByPositionALL(false),
+ mHasLinksToAllConditionDimensionsInTracker(false),
+ mEventActivationMap(eventActivationMap),
+ mEventDeactivationMap(eventDeactivationMap),
+ mIsActive(mEventActivationMap.empty()),
+ mSlicedStateAtoms(slicedStateAtoms),
+ mStateGroupMap(stateGroupMap) {
+}
+
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
if (!mIsActive) {
return;
@@ -51,38 +83,59 @@
bool condition;
ConditionKey conditionKey;
- std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
if (mConditionSliced) {
for (const auto& link : mMetric2ConditionLinks) {
getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
}
auto conditionState =
- mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &dimensionKeysInCondition);
+ mWizard->query(mConditionTrackerIndex, conditionKey,
+ !mHasLinksToAllConditionDimensionsInTracker);
condition = (conditionState == ConditionState::kTrue);
} else {
// TODO: The unknown condition state is not handled here, we should fix it.
condition = mCondition == ConditionState::kTrue;
}
- if (mDimensionsInCondition.empty() && condition) {
- dimensionKeysInCondition.insert(DEFAULT_DIMENSION_KEY);
+ // Stores atom id to primary key pairs for each state atom that the metric is
+ // sliced by.
+ 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
+ // that will be used to query StateTracker for the correct state value.
+ for (const auto& stateLink : mMetric2StateLinks) {
+ getDimensionForState(event.getValues(), stateLink,
+ &statePrimaryKeys[stateLink.stateAtomId]);
+ }
+
+ // For each sliced state, query StateTracker for the state value using
+ // either the primary key from the previous step or the DEFAULT_DIMENSION_KEY.
+ //
+ // Expected functionality: for any case where the MetricStateLinks are
+ // initialized incorrectly (ex. # of state links != # of primary fields, no
+ // links are provided for a state with primary fields, links are provided
+ // in the wrong order, etc.), StateTracker will simply return kStateUnknown
+ // when queried using an incorrect key.
+ HashableDimensionKey stateValuesKey;
+ for (auto atomId : mSlicedStateAtoms) {
+ FieldValue value;
+ if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) {
+ // found a primary key for this state, query using the key
+ queryStateValue(atomId, statePrimaryKeys[atomId], &value);
+ } else {
+ // if no MetricStateLinks exist for this state atom,
+ // query using the default dimension key (empty HashableDimensionKey)
+ queryStateValue(atomId, DEFAULT_DIMENSION_KEY, &value);
+ }
+ mapStateValue(atomId, &value);
+ stateValuesKey.addValue(value);
}
HashableDimensionKey dimensionInWhat;
filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
- MetricDimensionKey metricKey(dimensionInWhat, DEFAULT_DIMENSION_KEY);
- for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
- metricKey.setDimensionKeyInCondition(conditionDimensionKey);
- onMatchedLogEventInternalLocked(
- matcherIndex, metricKey, conditionKey, condition, event);
- }
- if (dimensionKeysInCondition.empty()) {
- onMatchedLogEventInternalLocked(
- matcherIndex, metricKey, conditionKey, condition, event);
- }
+ MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
+ onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
+ statePrimaryKeys);
}
bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
@@ -110,24 +163,6 @@
}
}
-void MetricProducer::addActivation(int activationTrackerIndex, const ActivationType& activationType,
- int64_t ttl_seconds, int deactivationTrackerIndex) {
- std::lock_guard<std::mutex> lock(mMutex);
- // When a metric producer does not depend on any activation, its mIsActive is true.
- // Therefore, if this is the 1st activation, mIsActive will turn to false. Otherwise it does not
- // change.
- if (mEventActivationMap.empty()) {
- mIsActive = false;
- }
- std::shared_ptr<Activation> activation =
- std::make_shared<Activation>(activationType, ttl_seconds * NS_PER_SEC);
- mEventActivationMap.emplace(activationTrackerIndex, activation);
- if (-1 != deactivationTrackerIndex) {
- auto& deactivationList = mEventDeactivationMap[deactivationTrackerIndex];
- deactivationList.push_back(activation);
- }
-}
-
void MetricProducer::activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs) {
auto it = mEventActivationMap.find(activationTrackerIndex);
if (it == mEventActivationMap.end()) {
@@ -231,6 +266,56 @@
}
}
+void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+ FieldValue* value) {
+ if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
+ value->mValue = Value(StateTracker::kStateUnknown);
+ value->mField.setTag(atomId);
+ ALOGW("StateTracker not found for state atom %d", atomId);
+ return;
+ }
+}
+
+void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
+ // check if there is a state map for this atom
+ auto atomIt = mStateGroupMap.find(atomId);
+ if (atomIt == mStateGroupMap.end()) {
+ return;
+ }
+ auto valueIt = atomIt->second.find(value->mValue.int_value);
+ if (valueIt == atomIt->second.end()) {
+ // state map exists, but value was not put in a state group
+ // so set mValue to kStateUnknown
+ // TODO(tsaichristine): handle incomplete state maps
+ value->mValue.setInt(StateTracker::kStateUnknown);
+ } else {
+ // set mValue to group_id
+ value->mValue.setLong(valueIt->second);
+ }
+}
+
+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;
+ event.dropTimeNs = dropTimeNs;
+ return event;
+}
+
+bool MetricProducer::maxDropEventsReached() {
+ return mCurrentSkippedBucket.dropEvents.size() >= StatsdStats::kMaxLoggedBucketDropEvents;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index c77bc01..be4cd67 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -28,6 +28,8 @@
#include "config/ConfigKey.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
+#include "state/StateListener.h"
+#include "state/StateManager.h"
namespace android {
namespace os {
@@ -67,61 +69,101 @@
NO_TIME_CONSTRAINTS = 2
};
+// Keep this in sync with BucketDropReason enum in stats_log.proto
+enum BucketDropReason {
+ // For ValueMetric, a bucket is dropped during a dump report request iff
+ // current bucket should be included, a pull is needed (pulled metric and
+ // condition is true), and we are under fast time constraints.
+ DUMP_REPORT_REQUESTED = 1,
+ EVENT_IN_WRONG_BUCKET = 2,
+ CONDITION_UNKNOWN = 3,
+ PULL_FAILED = 4,
+ PULL_DELAYED = 5,
+ DIMENSION_GUARDRAIL_REACHED = 6,
+ MULTIPLE_BUCKETS_SKIPPED = 7,
+ // Not an invalid bucket case, but the bucket is dropped.
+ BUCKET_TOO_SMALL = 8,
+ // Not an invalid bucket case, but the bucket is skipped.
+ NO_DATA = 9
+};
+
+struct Activation {
+ Activation(const ActivationType& activationType, const int64_t ttlNs)
+ : ttl_ns(ttlNs),
+ start_ns(0),
+ state(ActivationState::kNotActive),
+ activationType(activationType) {}
+
+ const int64_t ttl_ns;
+ int64_t start_ns;
+ ActivationState state;
+ const ActivationType activationType;
+};
+
+struct DropEvent {
+ // Reason for dropping the bucket and/or marking the bucket invalid.
+ BucketDropReason reason;
+ // The timestamp of the drop event.
+ int64_t dropTimeNs;
+};
+
+struct SkippedBucket {
+ // Start time of the dropped bucket.
+ int64_t bucketStartTimeNs;
+ // End time of the dropped bucket.
+ int64_t bucketEndTimeNs;
+ // List of events that invalidated this bucket.
+ std::vector<DropEvent> dropEvents;
+
+ void reset() {
+ bucketStartTimeNs = 0;
+ bucketEndTimeNs = 0;
+ dropEvents.clear();
+ }
+};
+
// A MetricProducer is responsible for compute one single metrics, creating stats log report, and
// writing the report to dropbox. MetricProducers should respond to package changes as required in
// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
// be a no-op.
-class MetricProducer : public virtual android::RefBase {
+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)
- : mMetricId(metricId),
- mConfigKey(key),
- mTimeBaseNs(timeBaseNs),
- mCurrentBucketStartTimeNs(timeBaseNs),
- mCurrentBucketNum(0),
- mCondition(initialCondition(conditionIndex)),
- mConditionSliced(false),
- mWizard(wizard),
- mConditionTrackerIndex(conditionIndex),
- mContainANYPositionInDimensionsInWhat(false),
- mSliceByPositionALL(false),
- mSameConditionDimensionsInTracker(false),
- mHasLinksToAllConditionDimensionsInTracker(false),
- mIsActive(true) {
- }
+ 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,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap);
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;
}
/**
- * Forces this metric to split into a partial bucket right now. If we're past a full bucket, we
- * first call the standard flushing code to flush up to the latest full bucket. Then we call
- * the flush again when the end timestamp is forced to be now, and then after flushing, update
- * the start timestamp to be now.
+ * Force a partial bucket split on app upgrade
*/
- virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) {
+ virtual void notifyAppUpgrade(const int64_t& eventTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
-
- if (eventTimeNs > getCurrentBucketEndTimeNs()) {
- // Flush full buckets on the normal path up to the latest bucket boundary.
- flushIfNeededLocked(eventTimeNs);
- }
- // Now flush a partial bucket.
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
- // Don't update the current bucket number so that the anomaly tracker knows this bucket
- // is a partial bucket and can merge it with the previous bucket.
+ flushLocked(eventTimeNs);
};
- void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) {
+ void notifyAppRemoved(const int64_t& eventTimeNs) {
// Force buckets to split on removal also.
- notifyAppUpgrade(eventTimeNs, apk, uid, 0);
+ notifyAppUpgrade(eventTimeNs);
};
+ /**
+ * Force a partial bucket split on boot complete.
+ */
+ virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ flushLocked(eventTimeNs);
+ }
// Consume the parsed stats log entry that already matched the "what" of the metric.
void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
@@ -143,6 +185,10 @@
return mConditionSliced;
};
+ void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState){};
+
// Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
// This method clears all the past buckets.
void onDumpReport(const int64_t dumpTimeNs,
@@ -161,9 +207,9 @@
return clearPastBucketsLocked(dumpTimeNs);
}
- void dumpStates(FILE* out, bool verbose) const {
+ void prepareFirstBucket() {
std::lock_guard<std::mutex> lock(mMutex);
- dumpStatesLocked(out, verbose);
+ prepareFirstBucketLocked();
}
// Returns the memory in bytes currently used to store this metric's data. Does not change
@@ -173,34 +219,9 @@
return byteSizeLocked();
}
- /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
- virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
- const sp<AlarmMonitor>& anomalyAlarmMonitor) {
+ void dumpStates(FILE* out, bool verbose) const {
std::lock_guard<std::mutex> lock(mMutex);
- sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
- if (anomalyTracker != nullptr) {
- mAnomalyTrackers.push_back(anomalyTracker);
- }
- return anomalyTracker;
- }
-
- int64_t getBuckeSizeInNs() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mBucketSizeNs;
- }
-
- // Only needed for unit-testing to override guardrail.
- void setBucketSize(int64_t bucketSize) {
- mBucketSizeNs = bucketSize;
- }
-
- inline const int64_t& getMetricId() const {
- return mMetricId;
- }
-
- void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
- std::lock_guard<std::mutex> lock(mMutex);
- loadActiveMetricLocked(activeMetric, currentTimeNs);
+ dumpStatesLocked(out, verbose);
}
// Let MetricProducer drop in-memory data to save memory.
@@ -212,9 +233,9 @@
dropDataLocked(dropTimeNs);
}
- // For test only.
- inline int64_t getCurrentBucketNum() const {
- return mCurrentBucketNum;
+ void loadActiveMetric(const ActiveMetric& activeMetric, int64_t currentTimeNs) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ loadActiveMetricLocked(activeMetric, currentTimeNs);
}
void activate(int activationTrackerIndex, int64_t elapsedTimestampNs) {
@@ -232,59 +253,49 @@
return isActiveLocked();
}
- void addActivation(int activationTrackerIndex, const ActivationType& activationType,
- int64_t ttl_seconds, int deactivationTrackerIndex = -1);
-
- void prepareFirstBucket() {
- std::lock_guard<std::mutex> lock(mMutex);
- prepareFirstBucketLocked();
- }
-
void flushIfExpire(int64_t elapsedTimestampNs);
void writeActiveMetricToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
-protected:
- virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
- virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) = 0;
- virtual void onDumpReportLocked(const int64_t dumpTimeNs,
- const bool include_current_partial_bucket,
- const bool erase_data,
- const DumpLatency dumpLatency,
- std::set<string> *str_set,
- android::util::ProtoOutputStream* protoOutput) = 0;
- virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
- virtual size_t byteSizeLocked() const = 0;
- virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
- bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
-
- void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
- void cancelEventActivationLocked(int deactivationTrackerIndex);
-
- inline bool isActiveLocked() const {
- return mIsActive;
+ // Start: getters/setters
+ inline const int64_t& getMetricId() const {
+ return mMetricId;
}
- void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
+ // For test only.
+ inline int64_t getCurrentBucketNum() const {
+ return mCurrentBucketNum;
+ }
- virtual void prepareFirstBucketLocked() {};
+ int64_t getBucketSizeInNs() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mBucketSizeNs;
+ }
+
+ inline const std::vector<int> getSlicedStateAtoms() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mSlicedStateAtoms;
+ }
+
+ /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
+ virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
+ const sp<AlarmMonitor>& anomalyAlarmMonitor) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
+ if (anomalyTracker != nullptr) {
+ mAnomalyTrackers.push_back(anomalyTracker);
+ }
+ return anomalyTracker;
+ }
+ // End: getters/setters
+protected:
/**
- * Flushes the current bucket if the eventTime is after the current bucket's end time. This will
- also flush the current partial bucket in memory.
+ * Flushes the current bucket if the eventTime is after the current bucket's end time.
*/
virtual void flushIfNeededLocked(const int64_t& eventTime){};
/**
- * Flushes all the data including the current partial bucket.
- */
- virtual void flushLocked(const int64_t& eventTimeNs) {
- flushIfNeededLocked(eventTimeNs);
- flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
- };
-
- /**
* For metrics that aggregate (ie, every metric producer except for EventMetricProducer),
* we need to be able to flush the current buckets on demand (ie, end the current bucket and
* start new bucket). If this function is called when eventTimeNs is greater than the current
@@ -297,12 +308,66 @@
virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {};
+ /**
+ * Flushes all the data including the current partial bucket.
+ */
+ virtual void flushLocked(const int64_t& eventTimeNs) {
+ flushIfNeededLocked(eventTimeNs);
+ flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
+ };
+
+ /*
+ * Individual metrics can implement their own business logic here. All pre-processing is done.
+ *
+ * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
+ * DurationMetric, because it has start/stop/stop_all 3 matchers.
+ * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
+ * dimensions, it will be DEFAULT_DIMENSION_KEY
+ * [conditionKey]: the keys of conditions which should be used to query the condition for this
+ * target event (from MetricConditionLink). This is passed to individual metrics
+ * because DurationMetric needs it to be cached.
+ * [condition]: whether condition is met. If condition is sliced, this is the result coming from
+ * query with ConditionWizard; If condition is not sliced, this is the
+ * nonSlicedCondition.
+ * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
+ */
+ virtual void onMatchedLogEventInternalLocked(
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) = 0;
+
+ // Consume the parsed stats log entry that already matched the "what" of the metric.
+ virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
+ virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
+ virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
+ const int64_t eventTime) = 0;
+ virtual void onDumpReportLocked(const int64_t dumpTimeNs,
+ const bool include_current_partial_bucket,
+ const bool erase_data,
+ const DumpLatency dumpLatency,
+ std::set<string> *str_set,
+ android::util::ProtoOutputStream* protoOutput) = 0;
+ virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0;
+ virtual void prepareFirstBucketLocked(){};
+ virtual size_t byteSizeLocked() const = 0;
+ virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
+ virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
+ void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs);
+ void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs);
+ void cancelEventActivationLocked(int deactivationTrackerIndex);
+
+ bool evaluateActiveStateLocked(int64_t elapsedTimestampNs);
+
virtual void onActiveStateChangedLocked(const int64_t& eventTimeNs) {
if (!mIsActive) {
flushLocked(eventTimeNs);
}
}
+ inline bool isActiveLocked() const {
+ return mIsActive;
+ }
+
// Convenience to compute the current bucket's end time, which is always aligned with the
// start time of the metric.
int64_t getCurrentBucketEndTimeNs() const {
@@ -313,7 +378,25 @@
return (endNs - mTimeBaseNs) / mBucketSizeNs - 1;
}
- virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
+ // Query StateManager for original state value using the queryKey.
+ // The field and value are output.
+ void queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+ FieldValue* value);
+
+ // If a state map exists for the given atom, replace the original state
+ // value with the group id mapped to the value.
+ // 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
+ // exceeded the maximum number allowed, which is currently capped at 10.
+ bool maxDropEventsReached();
const int64_t mMetricId;
@@ -335,21 +418,17 @@
ConditionState mCondition;
+ int mConditionTrackerIndex;
+
bool mConditionSliced;
sp<ConditionWizard> mWizard;
- int mConditionTrackerIndex;
-
- vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
- vector<Matcher> mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config
-
bool mContainANYPositionInDimensionsInWhat;
+
bool mSliceByPositionALL;
- // True iff the condition dimensions equal to the sliced dimensions in the simple condition
- // tracker. This field is always false for combinational condition trackers.
- bool mSameConditionDimensionsInTracker;
+ vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
// True iff the metric to condition links cover all dimension fields in the condition tracker.
// This field is always false for combinational condition trackers.
@@ -359,43 +438,8 @@
std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
- /*
- * Individual metrics can implement their own business logic here. All pre-processing is done.
- *
- * [matcherIndex]: the index of the matcher which matched this event. This is interesting to
- * DurationMetric, because it has start/stop/stop_all 3 matchers.
- * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
- * dimensions, it will be DEFAULT_DIMENSION_KEY
- * [conditionKey]: the keys of conditions which should be used to query the condition for this
- * target event (from MetricConditionLink). This is passed to individual metrics
- * because DurationMetric needs it to be cached.
- * [condition]: whether condition is met. If condition is sliced, this is the result coming from
- * query with ConditionWizard; If condition is not sliced, this is the
- * nonSlicedCondition.
- * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
- */
- virtual void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) = 0;
-
- // Consume the parsed stats log entry that already matched the "what" of the metric.
- virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
-
mutable std::mutex mMutex;
- struct Activation {
- Activation(const ActivationType& activationType, const int64_t ttlNs)
- : ttl_ns(ttlNs),
- start_ns(0),
- state(ActivationState::kNotActive),
- activationType(activationType) {}
-
- const int64_t ttl_ns;
- int64_t start_ns;
- ActivationState state;
- const ActivationType activationType;
- };
// When the metric producer has multiple activations, these activations are ORed to determine
// whether the metric producer is ready to generate metrics.
std::unordered_map<int, std::shared_ptr<Activation>> mEventActivationMap;
@@ -405,12 +449,37 @@
bool mIsActive;
+ // The slice_by_state atom ids defined in statsd_config.
+ const std::vector<int32_t> mSlicedStateAtoms;
+
+ // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
+ const std::unordered_map<int32_t, std::unordered_map<int, int64_t>> mStateGroupMap;
+
+ // MetricStateLinks defined in statsd_config that link fields in the state
+ // atom to fields in the "what" atom.
+ std::vector<Metric2State> mMetric2StateLinks;
+
+ SkippedBucket mCurrentSkippedBucket;
+ // Buckets that were invalidated and had their data dropped.
+ std::vector<SkippedBucket> mSkippedBuckets;
+
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
+ FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
+ FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
+ FRIEND_TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
@@ -424,6 +493,13 @@
FRIEND_TEST(StatsLogProcessorTest,
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
+
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
+ 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 1fb2b1c..60de1a2 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -21,18 +21,16 @@
#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"
#include "matchers/CombinationLogMatchingTracker.h"
#include "matchers/SimpleLogMatchingTracker.h"
#include "metrics_manager_util.h"
-#include "stats_util.h"
+#include "state/StateManager.h"
#include "stats_log_util.h"
-#include "statslog.h"
-
-#include <private/android_filesystem_config.h>
+#include "stats_util.h"
+#include "statslog_statsd.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_INT32;
@@ -71,6 +69,9 @@
mTtlEndNs(-1),
mLastReportTimeNs(currentTimeNs),
mLastReportWallClockNs(getWallClockNs()),
+ mPullerManager(pullerManager),
+ mWhitelistedAtomIds(config.whitelisted_atom_ids().begin(),
+ config.whitelisted_atom_ids().end()),
mShouldPersistHistory(config.persist_locally()) {
// Init the ttl end timestamp.
refreshTtl(timeBaseNs);
@@ -81,12 +82,13 @@
mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
- mMetricIndexesWithActivation, mNoReportMetricIds);
+ mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds);
mHashStringsInReport = config.hash_strings_in_metric_report();
mVersionStringsInReport = config.version_strings_in_metric_report();
mInstallerInReport = config.installer_in_metric_report();
+ // Init allowed pushed atom uids.
if (config.allowed_log_source_size() == 0) {
mConfigValid = false;
ALOGE("Log source whitelist is empty! This config won't get any data. Suggest adding at "
@@ -109,6 +111,40 @@
}
}
+ // Init default allowed pull atom uids.
+ int numPullPackages = 0;
+ for (const string& pullSource : config.default_pull_packages()) {
+ auto it = UidMap::sAidToUidMapping.find(pullSource);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ numPullPackages++;
+ mDefaultPullUids.insert(it->second);
+ } else {
+ ALOGE("Default pull atom packages must be in sAidToUidMapping");
+ mConfigValid = false;
+ }
+ }
+ // Init per-atom pull atom packages.
+ for (const PullAtomPackages& pullAtomPackages : config.pull_atom_packages()) {
+ int32_t atomId = pullAtomPackages.atom_id();
+ for (const string& pullPackage : pullAtomPackages.packages()) {
+ numPullPackages++;
+ auto it = UidMap::sAidToUidMapping.find(pullPackage);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ mPullAtomUids[atomId].insert(it->second);
+ } else {
+ mPullAtomPackages[atomId].insert(pullPackage);
+ }
+ }
+ }
+ if (numPullPackages > StatsdStats::kMaxPullAtomPackages) {
+ ALOGE("Too many sources in default_pull_packages and pull_atom_packages. This is likely to "
+ "be an error in the config");
+ mConfigValid = false;
+ } else {
+ initPullAtomSources();
+ }
+ mPullerManager->RegisterPullUidProvider(mConfigKey, this);
+
// Store the sub-configs used.
for (const auto& annotation : config.annotation()) {
mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
@@ -149,6 +185,13 @@
}
MetricsManager::~MetricsManager() {
+ for (auto it : mAllMetricProducers) {
+ for (int atomId : it->getSlicedStateAtoms()) {
+ StateManager::getInstance().unregisterListener(atomId, it);
+ }
+ }
+ mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
+
VLOG("~MetricsManager()");
}
@@ -168,6 +211,20 @@
}
}
+void MetricsManager::initPullAtomSources() {
+ std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+ mCombinedPullAtomUids.clear();
+ for (const auto& [atomId, uids] : mPullAtomUids) {
+ mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
+ }
+ for (const auto& [atomId, packages] : mPullAtomPackages) {
+ for (const string& pkg : packages) {
+ set<int32_t> uids = mUidMap->getAppUid(pkg);
+ mCombinedPullAtomUids[atomId].insert(uids.begin(), uids.end());
+ }
+ }
+}
+
bool MetricsManager::isConfigValid() const {
return mConfigValid;
}
@@ -175,43 +232,81 @@
void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
const int64_t version) {
// Inform all metric producers.
- for (auto it : mAllMetricProducers) {
- it->notifyAppUpgrade(eventTimeNs, apk, uid, version);
+ for (const auto& it : mAllMetricProducers) {
+ it->notifyAppUpgrade(eventTimeNs);
}
// check if we care this package
- if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
- return;
+ if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
+ // We will re-initialize the whole list because we don't want to keep the multi mapping of
+ // UID<->pkg inside MetricsManager to reduce the memory usage.
+ initLogSourceWhiteList();
}
- // We will re-initialize the whole list because we don't want to keep the multi mapping of
- // UID<->pkg inside MetricsManager to reduce the memory usage.
- initLogSourceWhiteList();
+
+ for (const auto& it : mPullAtomPackages) {
+ if (it.second.find(apk) != it.second.end()) {
+ initPullAtomSources();
+ return;
+ }
+ }
}
void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
const int uid) {
// Inform all metric producers.
- for (auto it : mAllMetricProducers) {
- it->notifyAppRemoved(eventTimeNs, apk, uid);
+ for (const auto& it : mAllMetricProducers) {
+ it->notifyAppRemoved(eventTimeNs);
}
// check if we care this package
- if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
- return;
+ if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
+ // We will re-initialize the whole list because we don't want to keep the multi mapping of
+ // UID<->pkg inside MetricsManager to reduce the memory usage.
+ initLogSourceWhiteList();
}
- // We will re-initialize the whole list because we don't want to keep the multi mapping of
- // UID<->pkg inside MetricsManager to reduce the memory usage.
- initLogSourceWhiteList();
+
+ for (const auto& it : mPullAtomPackages) {
+ if (it.second.find(apk) != it.second.end()) {
+ initPullAtomSources();
+ return;
+ }
+ }
}
void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
// Purposefully don't inform metric producers on a new snapshot
// because we don't need to flush partial buckets.
// This occurs if a new user is added/removed or statsd crashes.
+ initPullAtomSources();
+
if (mAllowedPkg.size() == 0) {
return;
}
initLogSourceWhiteList();
}
+void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
+ // Inform all metric producers.
+ for (const auto& it : mAllMetricProducers) {
+ it->onStatsdInitCompleted(eventTimeNs);
+ }
+}
+
+void MetricsManager::init() {
+ for (const auto& producer : mAllMetricProducers) {
+ producer->prepareFirstBucket();
+ }
+}
+
+vector<int32_t> MetricsManager::getPullAtomUids(int32_t atomId) {
+ std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
+ vector<int32_t> uids;
+ const auto& it = mCombinedPullAtomUids.find(atomId);
+ if (it != mCombinedPullAtomUids.end()) {
+ uids.insert(uids.end(), it->second.begin(), it->second.end());
+ }
+ uids.insert(uids.end(), mDefaultPullUids.begin(), mDefaultPullUids.end());
+ return uids;
+}
+
void MetricsManager::dumpStates(FILE* out, bool verbose) {
fprintf(out, "ConfigKey %s, allowed source:", mConfigKey.ToString().c_str());
{
@@ -265,16 +360,18 @@
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) {
- if (android::util::AtomsInfo::kWhitelistedAtoms.find(event.GetTagId()) !=
- android::util::AtomsInfo::kWhitelistedAtoms.end())
- {
+ if (mWhitelistedAtomIds.find(event.GetTagId()) != mWhitelistedAtomIds.end()) {
return true;
}
std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
@@ -286,18 +383,21 @@
}
bool MetricsManager::eventSanityCheck(const LogEvent& event) {
- if (event.GetTagId() == android::util::APP_BREADCRUMB_REPORTED) {
+ if (event.GetTagId() == util::APP_BREADCRUMB_REPORTED) {
// Check that app breadcrumb reported fields are valid.
status_t err = NO_ERROR;
// Uid is 3rd from last field and must match the caller's uid,
// unless that caller is statsd itself (statsd is allowed to spoof uids).
long appHookUid = event.GetLong(event.size()-2, &err);
- if (err != NO_ERROR ) {
+ if (err != NO_ERROR) {
VLOG("APP_BREADCRUMB_REPORTED had error when parsing the uid");
return false;
}
- int32_t loggerUid = event.GetUid();
+
+ // Because the uid within the LogEvent may have been mapped from
+ // isolated to host, map the loggerUid similarly before comparing.
+ int32_t loggerUid = mUidMap->getHostUidOrSelf(event.GetUid());
if (loggerUid != appHookUid && loggerUid != AID_STATSD) {
VLOG("APP_BREADCRUMB_REPORTED has invalid uid: claimed %ld but caller is %d",
appHookUid, loggerUid);
@@ -306,21 +406,21 @@
// The state must be from 0,3. This part of code must be manually updated.
long appHookState = event.GetLong(event.size(), &err);
- if (err != NO_ERROR ) {
+ if (err != NO_ERROR) {
VLOG("APP_BREADCRUMB_REPORTED had error when parsing the state field");
return false;
} else if (appHookState < 0 || appHookState > 3) {
VLOG("APP_BREADCRUMB_REPORTED does not have valid state %ld", appHookState);
return false;
}
- } else if (event.GetTagId() == android::util::DAVEY_OCCURRED) {
+ } else if (event.GetTagId() == util::DAVEY_OCCURRED) {
// Daveys can be logged from any app since they are logged in libs/hwui/JankTracker.cpp.
// Check that the davey duration is reasonable. Max length check is for privacy.
status_t err = NO_ERROR;
// Uid is the first field provided.
long jankUid = event.GetLong(1, &err);
- if (err != NO_ERROR ) {
+ if (err != NO_ERROR) {
VLOG("Davey occurred had error when parsing the uid");
return false;
}
@@ -332,7 +432,7 @@
}
long duration = event.GetLong(event.size(), &err);
- if (err != NO_ERROR ) {
+ if (err != NO_ERROR) {
VLOG("Davey occurred had error when parsing the duration");
return false;
} else if (duration > 100000) {
@@ -555,8 +655,40 @@
}
}
+bool MetricsManager::writeMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadata* statsMetadata) {
+ bool metadataWritten = false;
+ metadata::ConfigKey* configKey = statsMetadata->mutable_config_key();
+ configKey->set_config_id(mConfigKey.GetId());
+ configKey->set_uid(mConfigKey.GetUid());
+ for (const auto& anomalyTracker : mAllAnomalyTrackers) {
+ metadata::AlertMetadata* alertMetadata = statsMetadata->add_alert_metadata();
+ bool alertWritten = anomalyTracker->writeAlertMetadataToProto(currentWallClockTimeNs,
+ systemElapsedTimeNs, alertMetadata);
+ if (!alertWritten) {
+ statsMetadata->mutable_alert_metadata()->RemoveLast();
+ }
+ metadataWritten |= alertWritten;
+ }
+ return metadataWritten;
+}
-
+void MetricsManager::loadMetadata(const metadata::StatsMetadata& metadata,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs) {
+ for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) {
+ int64_t alertId = alertMetadata.alert_id();
+ auto it = mAlertTrackerMap.find(alertId);
+ if (it == mAlertTrackerMap.end()) {
+ ALOGE("No anomalyTracker found for alertId %lld", (long long) alertId);
+ continue;
+ }
+ mAllAnomalyTrackers[it->second]->loadAlertMetadata(alertMetadata,
+ currentWallClockTimeNs,
+ systemElapsedTimeNs);
+ }
+}
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 34d47d4..ad30a88 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -23,6 +23,7 @@
#include "config/ConfigKey.h"
#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h"
#include "logd/LogEvent.h"
#include "matchers/LogMatchingTracker.h"
#include "metrics/MetricProducer.h"
@@ -35,7 +36,7 @@
namespace statsd {
// A MetricsManager is responsible for managing metrics from one single config source.
-class MetricsManager : public virtual android::RefBase {
+class MetricsManager : public virtual android::RefBase, public virtual PullUidProvider {
public:
MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<UidMap>& uidMap,
@@ -69,6 +70,12 @@
void onUidMapReceived(const int64_t& eventTimeNs);
+ void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
+
+ void init();
+
+ vector<int32_t> getPullAtomUids(int32_t atomId) override;
+
bool shouldWriteToDisk() const {
return mNoReportMetricIds.size() != mAllMetricProducers.size();
}
@@ -139,6 +146,14 @@
void writeActiveConfigToProtoOutputStream(
int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
+ // Returns true if at least one piece of metadata is written.
+ bool writeMetadataToProto(int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs,
+ metadata::StatsMetadata* statsMetadata);
+
+ void loadMetadata(const metadata::StatsMetadata& metadata,
+ int64_t currentWallClockTimeNs,
+ int64_t systemElapsedTimeNs);
private:
// For test only.
inline int64_t getTtlEndNs() const { return mTtlEndNs; }
@@ -159,6 +174,8 @@
int64_t mLastReportTimeNs;
int64_t mLastReportWallClockNs;
+ sp<StatsPullerManager> mPullerManager;
+
// The uid log sources from StatsdConfig.
std::vector<int32_t> mAllowedUid;
@@ -169,13 +186,29 @@
// Logs from uids that are not in the list will be ignored to avoid spamming.
std::set<int32_t> mAllowedLogSources;
+ // To guard access to mAllowedLogSources
+ mutable std::mutex mAllowedLogSourcesMutex;
+
+ const std::set<int32_t> mWhitelistedAtomIds;
+
+ // We can pull any atom from these uids.
+ std::set<int32_t> mDefaultPullUids;
+
+ // Uids that specific atoms can pull from.
+ // This is a map<atom id, set<uids>>
+ std::map<int32_t, std::set<int32_t>> mPullAtomUids;
+
+ // Packages that specific atoms can be pulled from.
+ std::map<int32_t, std::set<std::string>> mPullAtomPackages;
+
+ // All uids to pull for this atom. NOTE: Does not include the default uids for memory.
+ std::map<int32_t, std::set<int32_t>> mCombinedPullAtomUids;
+
// Contains the annotations passed in with StatsdConfig.
std::list<std::pair<const int64_t, const int32_t>> mAnnotations;
const bool mShouldPersistHistory;
- // To guard access to mAllowedLogSources
- mutable std::mutex mAllowedLogSourcesMutex;
// All event tags that are interesting to my metrics.
std::set<int> mTagIds;
@@ -230,10 +263,16 @@
// Maps deactivation triggering event to MetricProducers.
std::unordered_map<int, std::vector<int>> mDeactivationAtomTrackerToMetricMap;
+ // Maps AlertIds to the index of the corresponding AnomalyTracker stored in mAllAnomalyTrackers.
+ // The map is used in LoadMetadata to more efficiently lookup AnomalyTrackers from an AlertId.
+ std::unordered_map<int64_t, int> mAlertTrackerMap;
+
std::vector<int> mMetricIndexesWithActivation;
void initLogSourceWhiteList();
+ void initPullAtomSources();
+
// The metrics that don't need to be uploaded or even reported.
std::set<int64_t> mNoReportMetricIds;
@@ -253,23 +292,12 @@
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition);
FRIEND_TEST(GaugeMetricE2eTest, TestConditionChangeToTrueSamplePulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
- FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition);
-
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition);
- FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk);
+ FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
@@ -282,6 +310,8 @@
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+ FRIEND_TEST(MetricsManagerTest, TestLogSources);
+
FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations);
@@ -289,12 +319,31 @@
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
+ FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
+ FRIEND_TEST(CountMetricE2eTest, TestMultipleSlicedStates);
+ FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStateMapped);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSuperset);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset);
+
+ FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
+ FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
+ FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index fa310dc..5987a72 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -46,19 +46,22 @@
const int FIELD_ID_TIME_BASE = 9;
const int FIELD_ID_BUCKET_SIZE = 10;
const int FIELD_ID_DIMENSION_PATH_IN_WHAT = 11;
-const int FIELD_ID_DIMENSION_PATH_IN_CONDITION = 12;
const int FIELD_ID_IS_ACTIVE = 14;
// for ValueMetricDataWrapper
const int FIELD_ID_DATA = 1;
const int FIELD_ID_SKIPPED = 2;
+// for SkippedBuckets
const int FIELD_ID_SKIPPED_START_MILLIS = 3;
const int FIELD_ID_SKIPPED_END_MILLIS = 4;
+const int FIELD_ID_SKIPPED_DROP_EVENT = 5;
+// for DumpEvent Proto
+const int FIELD_ID_BUCKET_DROP_REASON = 1;
+const int FIELD_ID_DROP_TIME = 2;
// for ValueMetricData
const int FIELD_ID_DIMENSION_IN_WHAT = 1;
-const int FIELD_ID_DIMENSION_IN_CONDITION = 2;
const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
-const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
+const int FIELD_ID_SLICE_BY_STATE = 6;
// for ValueBucketInfo
const int FIELD_ID_VALUE_INDEX = 1;
const int FIELD_ID_VALUE_LONG = 2;
@@ -75,10 +78,17 @@
// 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)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard),
+ 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,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache,
+ conditionWizard, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+ stateGroupMap),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
@@ -100,11 +110,11 @@
mSkipZeroDiffOutput(metric.skip_zero_diff_output()),
mUseZeroDefaultBase(metric.use_zero_default_base()),
mHasGlobalBase(false),
- mCurrentBucketIsInvalid(false),
+ mCurrentBucketIsSkipped(false),
mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
: StatsdStats::kPullMaxDelayNs),
mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()),
- // Condition timer will be set in prepareFirstBucketLocked.
+ // Condition timer will be set later within the constructor after pulling events
mConditionTimer(false, timeBaseNs) {
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
@@ -120,10 +130,7 @@
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
- }
-
- if (metric.has_dimensions_in_condition()) {
- translateFieldMatcher(metric.dimensions_in_condition(), &mDimensionsInCondition);
+ mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
}
if (metric.links().size() > 0) {
@@ -134,11 +141,16 @@
translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
mMetric2ConditionLinks.push_back(mc);
}
+ mConditionSliced = true;
}
- mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
- mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
- HasPositionALL(metric.dimensions_in_condition());
+ for (const auto& stateLink : metric.state_link()) {
+ Metric2State ms;
+ ms.stateAtomId = stateLink.state_atom_id();
+ translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
+ translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
+ mMetric2StateLinks.push_back(ms);
+ }
int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs);
mCurrentBucketNum += numBucketsForward;
@@ -146,7 +158,7 @@
flushIfNeededLocked(startTimeNs);
if (mIsPulled) {
- mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+ mPullerManager->RegisterReceiver(mPullTagId, mConfigKey, this, getCurrentBucketEndTimeNs(),
mBucketSizeNs);
}
@@ -155,6 +167,11 @@
// Adjust start for partial bucket
mCurrentBucketStartTimeNs = startTimeNs;
mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs);
+
+ // Now that activations are processed, start the condition timer if needed.
+ mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
+ mCurrentBucketStartTimeNs);
+
VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
@@ -162,18 +179,48 @@
ValueMetricProducer::~ValueMetricProducer() {
VLOG("~ValueMetricProducer() called");
if (mIsPulled) {
- mPullerManager->UnRegisterReceiver(mPullTagId, this);
+ mPullerManager->UnRegisterReceiver(mPullTagId, mConfigKey, this);
}
}
-void ValueMetricProducer::prepareFirstBucketLocked() {
- // Kicks off the puller immediately if condition is true and diff based.
- if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
- pullAndMatchEventsLocked(mCurrentBucketStartTimeNs, mCondition);
+void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) {
+ 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 old and new states are in the same StateGroup, then we do not need to
+ // pull for this state change.
+ FieldValue oldStateCopy = oldState;
+ FieldValue newStateCopy = newState;
+ mapStateValue(atomId, &oldStateCopy);
+ mapStateValue(atomId, &newStateCopy);
+ if (oldStateCopy == newStateCopy) {
+ return;
}
- // Now that activations are processed, start the condition timer if needed.
- mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue,
- mCurrentBucketStartTimeNs);
+
+ // 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,
+ (long long)mCurrentBucketStartTimeNs);
+ invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
+ return;
+ }
+ mStateChangePrimaryKey.first = atomId;
+ mStateChangePrimaryKey.second = primaryKey;
+ if (mIsPulled) {
+ pullAndMatchEventsLocked(eventTimeNs);
+ }
+ mStateChangePrimaryKey.first = 0;
+ mStateChangePrimaryKey.second = DEFAULT_DIMENSION_KEY;
+ flushIfNeededLocked(eventTimeNs);
}
void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -183,11 +230,9 @@
void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
StatsdStats::getInstance().noteBucketDropped(mMetricId);
- // We are going to flush the data without doing a pull first so we need to invalidte the data.
- bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue;
- if (pullNeeded) {
- invalidateCurrentBucket();
- }
+
+ // The current partial bucket is not flushed and does not require a pull,
+ // so the data is still valid.
flushIfNeededLocked(dropTimeNs);
clearPastBucketsLocked(dropTimeNs);
}
@@ -213,10 +258,10 @@
if (pullNeeded) {
switch (dumpLatency) {
case FAST:
- invalidateCurrentBucket();
+ invalidateCurrentBucket(dumpTimeNs, BucketDropReason::DUMP_REPORT_REQUESTED);
break;
case NO_TIME_CONSTRAINTS:
- pullAndMatchEventsLocked(dumpTimeNs, mCondition);
+ pullAndMatchEventsLocked(dumpTimeNs);
break;
}
}
@@ -238,23 +283,26 @@
writeDimensionPathToProto(mDimensionsInWhat, protoOutput);
protoOutput->end(dimenPathToken);
}
- if (!mDimensionsInCondition.empty()) {
- uint64_t dimenPathToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_PATH_IN_CONDITION);
- writeDimensionPathToProto(mDimensionsInCondition, protoOutput);
- protoOutput->end(dimenPathToken);
- }
}
uint64_t protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
- for (const auto& pair : mSkippedBuckets) {
+ for (const auto& skippedBucket : mSkippedBuckets) {
uint64_t wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SKIPPED);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_START_MILLIS,
- (long long)(NanoToMillis(pair.first)));
+ (long long)(NanoToMillis(skippedBucket.bucketStartTimeNs)));
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_END_MILLIS,
- (long long)(NanoToMillis(pair.second)));
+ (long long)(NanoToMillis(skippedBucket.bucketEndTimeNs)));
+ for (const auto& dropEvent : skippedBucket.dropEvents) {
+ uint64_t dropEventToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SKIPPED_DROP_EVENT);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME,
+ (long long)(NanoToMillis(dropEvent.dropTimeNs)));
+ ;
+ protoOutput->end(dropEventToken);
+ }
protoOutput->end(wrapperToken);
}
@@ -270,21 +318,17 @@
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), str_set, protoOutput);
protoOutput->end(dimensionToken);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- uint64_t dimensionInConditionToken =
- protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), str_set,
- protoOutput);
- protoOutput->end(dimensionInConditionToken);
- }
} else {
writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInWhat(),
FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
- if (dimensionKey.hasDimensionKeyInCondition()) {
- writeDimensionLeafNodesToProto(dimensionKey.getDimensionKeyInCondition(),
- FIELD_ID_DIMENSION_LEAF_IN_CONDITION, str_set,
- protoOutput);
- }
+ }
+
+ // Then fill slice_by_state.
+ for (auto state : dimensionKey.getStateValuesKey().getValues()) {
+ uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+ FIELD_ID_SLICE_BY_STATE);
+ writeStateToProto(state, protoOutput);
+ protoOutput->end(stateToken);
}
// Then fill bucket_info (ValueBucketInfo).
@@ -306,7 +350,7 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
(long long)bucket.mConditionTrueNs);
}
- for (int i = 0; i < (int)bucket.valueIndex.size(); i ++) {
+ for (int i = 0; i < (int)bucket.valueIndex.size(); i++) {
int index = bucket.valueIndex[i];
const Value& value = bucket.values[i];
uint64_t valueToken = protoOutput->start(
@@ -341,23 +385,34 @@
}
}
-void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase() {
- if (!mCurrentBucketIsInvalid) {
- // Only report once per invalid bucket.
+void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs,
+ const BucketDropReason reason) {
+ if (!mCurrentBucketIsSkipped) {
+ // Only report to StatsdStats once per invalid bucket.
StatsdStats::getInstance().noteInvalidatedBucket(mMetricId);
}
- mCurrentBucketIsInvalid = true;
+
+ skipCurrentBucket(dropTimeNs, reason);
}
-void ValueMetricProducer::invalidateCurrentBucket() {
- invalidateCurrentBucketWithoutResetBase();
+void ValueMetricProducer::invalidateCurrentBucket(const int64_t dropTimeNs,
+ const BucketDropReason reason) {
+ invalidateCurrentBucketWithoutResetBase(dropTimeNs, reason);
resetBase();
}
+void ValueMetricProducer::skipCurrentBucket(const int64_t dropTimeNs,
+ const BucketDropReason reason) {
+ if (!maxDropEventsReached()) {
+ mCurrentSkippedBucket.dropEvents.emplace_back(buildDropEvent(dropTimeNs, reason));
+ }
+ mCurrentBucketIsSkipped = true;
+}
+
void ValueMetricProducer::resetBase() {
- for (auto& slice : mCurrentSlicedBucket) {
- for (auto& interval : slice.second) {
- interval.hasBase = false;
+ for (auto& slice : mCurrentBaseInfo) {
+ for (auto& baseInfo : slice.second) {
+ baseInfo.hasBase = false;
}
}
mHasGlobalBase = false;
@@ -369,9 +424,10 @@
// - ConditionTimer tracks changes based on AND of condition and active state.
void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
- if (ConditionState::kTrue == mCondition && isEventTooLate) {
+ if (isEventTooLate) {
// Drop bucket because event arrived too late, ie. we are missing data for this bucket.
- invalidateCurrentBucket();
+ StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
+ invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
}
// Call parent method once we've verified the validity of current bucket.
@@ -384,7 +440,7 @@
// Pull on active state changes.
if (!isEventTooLate) {
if (mIsPulled) {
- pullAndMatchEventsLocked(eventTimeNs, mCondition);
+ pullAndMatchEventsLocked(eventTimeNs);
}
// When active state changes from true to false, clear diff base but don't
// reset other counters as we may accumulate more value in the bucket.
@@ -404,65 +460,80 @@
ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
- if (mIsActive) {
- if (isEventTooLate) {
- VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
- (long long)mCurrentBucketStartTimeNs);
- StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
- invalidateCurrentBucket();
- } else {
- if (mCondition == ConditionState::kUnknown) {
- // If the condition was unknown, we mark the bucket as invalid since the bucket will
- // contain partial data. For instance, the condition change might happen close to
- // the end of the bucket and we might miss lots of data.
- //
- // We still want to pull to set the base.
- invalidateCurrentBucket();
- }
-
- // Pull on condition changes.
- bool conditionChanged =
- (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)
- || (mCondition == ConditionState::kFalse &&
- newCondition == ConditionState::kTrue);
- // We do not need to pull when we go from unknown to false.
- //
- // We also pull if the condition was already true in order to be able to flush the
- // bucket at the end if needed.
- //
- // onConditionChangedLocked might happen on bucket boundaries if this is called before
- // #onDataPulled.
- if (mIsPulled && (conditionChanged || condition)) {
- pullAndMatchEventsLocked(eventTimeNs, newCondition);
- }
-
- // When condition change from true to false, clear diff base but don't
- // reset other counters as we may accumulate more value in the bucket.
- if (mUseDiff && mCondition == ConditionState::kTrue
- && newCondition == ConditionState::kFalse) {
- resetBase();
- }
- }
- }
-
- mCondition = isEventTooLate ? initialCondition(mConditionTrackerIndex) : newCondition;
-
- if (mIsActive) {
- flushIfNeededLocked(eventTimeNs);
- mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
- }
-}
-
-void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs,
- ConditionState condition) {
- vector<std::shared_ptr<LogEvent>> allData;
- if (!mPullerManager->Pull(mPullTagId, &allData)) {
- ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
- invalidateCurrentBucket();
+ // If the config is not active, skip the event.
+ if (!mIsActive) {
+ mCondition = isEventTooLate ? ConditionState::kUnknown : newCondition;
return;
}
- accumulateEvents(allData, timestampNs, timestampNs, condition);
+ // If the event arrived late, mark the bucket as invalid and skip the event.
+ if (isEventTooLate) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+ (long long)mCurrentBucketStartTimeNs);
+ StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
+ StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
+ invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
+ mCondition = ConditionState::kUnknown;
+ mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+ return;
+ }
+
+ // If the previous condition was unknown, mark the bucket as invalid
+ // because the bucket will contain partial data. For example, the condition
+ // change might happen close to the end of the bucket and we might miss a
+ // lot of data.
+ //
+ // We still want to pull to set the base.
+ if (mCondition == ConditionState::kUnknown) {
+ invalidateCurrentBucket(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
+ }
+
+ // Pull and match for the following condition change cases:
+ // unknown/false -> true - condition changed
+ // true -> false - condition changed
+ // true -> true - old condition was true so we can flush the bucket at the
+ // end if needed.
+ //
+ // We don’t need to pull for unknown -> false or false -> false.
+ //
+ // onConditionChangedLocked might happen on bucket boundaries if this is
+ // called before #onDataPulled.
+ if (mIsPulled &&
+ (newCondition == ConditionState::kTrue || mCondition == ConditionState::kTrue)) {
+ pullAndMatchEventsLocked(eventTimeNs);
+ }
+
+ // For metrics that use diff, when condition changes from true to false,
+ // clear diff base but don't reset other counts because we may accumulate
+ // more value in the bucket.
+ if (mUseDiff &&
+ (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)) {
+ resetBase();
+ }
+
+ // Update condition state after pulling.
+ mCondition = newCondition;
+
+ flushIfNeededLocked(eventTimeNs);
+ mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+}
+
+void ValueMetricProducer::prepareFirstBucketLocked() {
+ // Kicks off the puller immediately if condition is true and diff based.
+ if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) {
+ pullAndMatchEventsLocked(mCurrentBucketStartTimeNs);
+ }
+}
+
+void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
+ vector<std::shared_ptr<LogEvent>> allData;
+ if (!mPullerManager->Pull(mPullTagId, mConfigKey, timestampNs, &allData)) {
+ ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
+ invalidateCurrentBucket(timestampNs, BucketDropReason::PULL_FAILED);
+ return;
+ }
+
+ accumulateEvents(allData, timestampNs, timestampNs);
}
int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) {
@@ -475,33 +546,33 @@
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData,
bool pullSuccess, int64_t originalPullTimeNs) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mCondition == ConditionState::kTrue) {
- // If the pull failed, we won't be able to compute a diff.
- if (!pullSuccess) {
- invalidateCurrentBucket();
+ if (mCondition == ConditionState::kTrue) {
+ // If the pull failed, we won't be able to compute a diff.
+ if (!pullSuccess) {
+ invalidateCurrentBucket(originalPullTimeNs, BucketDropReason::PULL_FAILED);
+ } else {
+ bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs();
+ if (isEventLate) {
+ // If the event is late, we are in the middle of a bucket. Just
+ // process the data without trying to snap the data to the nearest bucket.
+ accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs);
} else {
- bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs();
- if (isEventLate) {
- // If the event is late, we are in the middle of a bucket. Just
- // process the data without trying to snap the data to the nearest bucket.
- accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs, mCondition);
- } else {
- // For scheduled pulled data, the effective event time is snap to the nearest
- // bucket end. In the case of waking up from a deep sleep state, we will
- // attribute to the previous bucket end. If the sleep was long but not very
- // long, we will be in the immediate next bucket. Previous bucket may get a
- // larger number as we pull at a later time than real bucket end.
- //
- // If the sleep was very long, we skip more than one bucket before sleep. In
- // this case, if the diff base will be cleared and this new data will serve as
- // new diff base.
- int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
- StatsdStats::getInstance().noteBucketBoundaryDelayNs(
- mMetricId, originalPullTimeNs - bucketEndTime);
- accumulateEvents(allData, originalPullTimeNs, bucketEndTime, mCondition);
- }
+ // For scheduled pulled data, the effective event time is snap to the nearest
+ // bucket end. In the case of waking up from a deep sleep state, we will
+ // attribute to the previous bucket end. If the sleep was long but not very
+ // long, we will be in the immediate next bucket. Previous bucket may get a
+ // larger number as we pull at a later time than real bucket end.
+ //
+ // If the sleep was very long, we skip more than one bucket before sleep. In
+ // this case, if the diff base will be cleared and this new data will serve as
+ // new diff base.
+ int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1;
+ StatsdStats::getInstance().noteBucketBoundaryDelayNs(
+ mMetricId, originalPullTimeNs - bucketEndTime);
+ accumulateEvents(allData, originalPullTimeNs, bucketEndTime);
}
}
+ }
// We can probably flush the bucket. Since we used bucketEndTime when calling
// #onMatchedLogEventInternalLocked, the current bucket will not have been flushed.
@@ -509,18 +580,18 @@
}
void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
- int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
- ConditionState condition) {
+ int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) {
bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs;
if (isEventLate) {
VLOG("Skip bucket end pull due to late arrival: %lld vs %lld",
(long long)eventElapsedTimeNs, (long long)mCurrentBucketStartTimeNs);
StatsdStats::getInstance().noteLateLogEventSkipped(mMetricId);
- invalidateCurrentBucket();
+ invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
return;
}
- const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs;
+ const int64_t elapsedRealtimeNs = getElapsedRealtimeNs();
+ const int64_t pullDelayNs = elapsedRealtimeNs - originalPullTimeNs;
StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (pullDelayNs > mMaxPullDelayNs) {
ALOGE("Pull finish too late for atom %d, longer than %lld", mPullTagId,
@@ -528,15 +599,10 @@
StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
// We are missing one pull from the bucket which means we will not have a complete view of
// what's going on.
- invalidateCurrentBucket();
+ invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::PULL_DELAYED);
return;
}
- if (allData.size() == 0) {
- VLOG("Data pulled is empty");
- StatsdStats::getInstance().noteEmptyData(mPullTagId);
- }
-
mMatchedMetricDimensionKeys.clear();
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
@@ -546,14 +612,19 @@
onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
}
}
- // If the new pulled data does not contains some keys we track in our intervals, we need to
- // reset the base.
+ // If a key that is:
+ // 1. Tracked in mCurrentSlicedBucket and
+ // 2. A superset of the current mStateChangePrimaryKey
+ // was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
+ // then we need to reset the base.
for (auto& slice : mCurrentSlicedBucket) {
- bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first)
- != mMatchedMetricDimensionKeys.end();
- if (!presentInPulledData) {
- for (auto& interval : slice.second) {
- interval.hasBase = false;
+ const auto& whatKey = slice.first.getDimensionKeyInWhat();
+ bool presentInPulledData =
+ mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
+ if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
+ auto it = mCurrentBaseInfo.find(whatKey);
+ for (auto& baseInfo : it->second) {
+ baseInfo.hasBase = false;
}
}
}
@@ -567,7 +638,7 @@
// incorrectly compute the diff when mUseZeroDefaultBase is true since an existing key
// might be missing from mCurrentSlicedBucket.
if (hasReachedGuardRailLimit()) {
- invalidateCurrentBucket();
+ invalidateCurrentBucket(eventElapsedTimeNs, BucketDropReason::DIMENSION_GUARDRAIL_REACHED);
mCurrentSlicedBucket.clear();
}
}
@@ -582,10 +653,10 @@
if (verbose) {
for (const auto& it : mCurrentSlicedBucket) {
for (const auto& interval : it.second) {
- fprintf(out, "\t(what)%s\t(condition)%s (value)%s\n",
- it.first.getDimensionKeyInWhat().toString().c_str(),
- it.first.getDimensionKeyInCondition().toString().c_str(),
- interval.value.toString().c_str());
+ fprintf(out, "\t(what)%s\t(states)%s (value)%s\n",
+ it.first.getDimensionKeyInWhat().toString().c_str(),
+ it.first.getStateValuesKey().toString().c_str(),
+ interval.value.toString().c_str());
}
}
}
@@ -653,6 +724,7 @@
ret.setDouble(value.mValue.double_value);
break;
default:
+ return false;
break;
}
return true;
@@ -661,17 +733,30 @@
return false;
}
-void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIndex,
- const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey,
- bool condition, const LogEvent& event) {
+void ValueMetricProducer::onMatchedLogEventInternalLocked(
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const map<int, HashableDimensionKey>& statePrimaryKeys) {
+ auto whatKey = eventKey.getDimensionKeyInWhat();
+ auto stateKey = eventKey.getStateValuesKey();
+
+ // Skip this event if a state changed occurred for a different primary key.
+ auto it = statePrimaryKeys.find(mStateChangePrimaryKey.first);
+ // Check that both the atom id and the primary key are equal.
+ if (it != statePrimaryKeys.end() && it->second != mStateChangePrimaryKey.second) {
+ VLOG("ValueMetric skip event with primary key %s because state change primary key "
+ "is %s",
+ it->second.toString().c_str(), mStateChangePrimaryKey.second.toString().c_str());
+ return;
+ }
+
int64_t eventTimeNs = event.GetElapsedTimestampNs();
if (eventTimeNs < mCurrentBucketStartTimeNs) {
VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
(long long)mCurrentBucketStartTimeNs);
return;
}
- mMatchedMetricDimensionKeys.insert(eventKey);
+ mMatchedMetricDimensionKeys.insert(whatKey);
if (!mIsPulled) {
// We cannot flush without doing a pull first.
@@ -689,17 +774,35 @@
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<Interval>& multiIntervals = mCurrentSlicedBucket[eventKey];
- if (multiIntervals.size() < mFieldMatchers.size()) {
+
+ vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey];
+ if (baseInfos.size() < mFieldMatchers.size()) {
VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
- multiIntervals.resize(mFieldMatchers.size());
+ baseInfos.resize(mFieldMatchers.size());
+ }
+
+ for (BaseInfo& baseInfo : baseInfos) {
+ if (!baseInfo.hasCurrentState) {
+ baseInfo.currentState = getUnknownStateKey();
+ baseInfo.hasCurrentState = true;
+ }
+ }
+
+ // We need to get the intervals stored with the previous state key so we can
+ // close these value intervals.
+ const auto oldStateKey = baseInfos[0].currentState;
+ vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)];
+ if (intervals.size() < mFieldMatchers.size()) {
+ VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
+ intervals.resize(mFieldMatchers.size());
}
// We only use anomaly detection under certain cases.
@@ -712,9 +815,12 @@
for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
const Matcher& matcher = mFieldMatchers[i];
- Interval& interval = multiIntervals[i];
+ BaseInfo& baseInfo = baseInfos[i];
+ 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);
@@ -723,60 +829,61 @@
interval.seenNewData = true;
if (mUseDiff) {
- if (!interval.hasBase) {
+ if (!baseInfo.hasBase) {
if (mHasGlobalBase && mUseZeroDefaultBase) {
// The bucket has global base. This key does not.
// Optionally use zero as base.
- interval.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
- interval.hasBase = true;
+ baseInfo.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
+ baseInfo.hasBase = true;
} else {
// no base. just update base and return.
- interval.base = value;
- interval.hasBase = true;
+ baseInfo.base = value;
+ baseInfo.hasBase = true;
// If we're missing a base, do not use anomaly detection on incomplete data
useAnomalyDetection = false;
- // Continue (instead of return) here in order to set interval.base and
- // interval.hasBase for other intervals
+ // Continue (instead of return) here in order to set baseInfo.base and
+ // baseInfo.hasBase for other baseInfos
continue;
}
}
+
Value diff;
switch (mValueDirection) {
case ValueMetric::INCREASING:
- if (value >= interval.base) {
- diff = value - interval.base;
+ if (value >= baseInfo.base) {
+ diff = value - baseInfo.base;
} else if (mUseAbsoluteValueOnReset) {
diff = value;
} else {
VLOG("Unexpected decreasing value");
StatsdStats::getInstance().notePullDataError(mPullTagId);
- interval.base = value;
+ baseInfo.base = value;
// If we've got bad data, do not use anomaly detection
useAnomalyDetection = false;
continue;
}
break;
case ValueMetric::DECREASING:
- if (interval.base >= value) {
- diff = interval.base - value;
+ if (baseInfo.base >= value) {
+ diff = baseInfo.base - value;
} else if (mUseAbsoluteValueOnReset) {
diff = value;
} else {
VLOG("Unexpected increasing value");
StatsdStats::getInstance().notePullDataError(mPullTagId);
- interval.base = value;
+ baseInfo.base = value;
// If we've got bad data, do not use anomaly detection
useAnomalyDetection = false;
continue;
}
break;
case ValueMetric::ANY:
- diff = value - interval.base;
+ diff = value - baseInfo.base;
break;
default:
break;
}
- interval.base = value;
+ baseInfo.base = value;
value = diff;
}
@@ -806,7 +913,7 @@
// Only trigger the tracker if all intervals are correct
if (useAnomalyDetection) {
// TODO: propgate proper values down stream when anomaly support doubles
- long wholeBucketVal = multiIntervals[0].value.long_value;
+ long wholeBucketVal = intervals[0].value.long_value;
auto prev = mCurrentFullBucket.find(eventKey);
if (prev != mCurrentFullBucket.end()) {
wholeBucketVal += prev->second;
@@ -823,7 +930,7 @@
void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
if (eventTimeNs < currentBucketEndTimeNs) {
- VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
+ VLOG("eventTime is %lld, less than current bucket end time %lld", (long long)eventTimeNs,
(long long)(currentBucketEndTimeNs));
return;
}
@@ -844,25 +951,39 @@
const int64_t& nextBucketStartTimeNs) {
if (mCondition == ConditionState::kUnknown) {
StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
- }
-
- int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
- if (numBucketsForward > 1) {
- 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();
+ 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 = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
+ int64_t bucketEndTime = fullBucketEndTimeNs;
+ int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
+
+ // 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;
+ }
+
// Close the current bucket.
int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime);
bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs;
- if (isBucketLargeEnough && !mCurrentBucketIsInvalid) {
+ if (!isBucketLargeEnough) {
+ skipCurrentBucket(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL);
+ }
+ if (!mCurrentBucketIsSkipped) {
+ bool bucketHasData = false;
// The current bucket is large enough to keep.
for (const auto& slice : mCurrentSlicedBucket) {
ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
@@ -871,13 +992,34 @@
if (bucket.valueIndex.size() > 0) {
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(bucket);
+ bucketHasData = true;
}
}
- } else {
- mSkippedBuckets.emplace_back(mCurrentBucketStartTimeNs, bucketEndTime);
+ if (!bucketHasData) {
+ skipCurrentBucket(eventTimeNs, BucketDropReason::NO_DATA);
+ }
}
- appendToFullBucket(eventTimeNs, fullBucketEndTimeNs);
+ if (mCurrentBucketIsSkipped) {
+ mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs;
+ mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime;
+ mSkippedBuckets.emplace_back(mCurrentSkippedBucket);
+ }
+
+ // This means that the current bucket was not flushed before a forced bucket split.
+ // 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;
+ bucketInGap.dropEvents.emplace_back(
+ buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA));
+ mSkippedBuckets.emplace_back(bucketInGap);
+ }
+
+ appendToFullBucket(eventTimeNs > fullBucketEndTimeNs);
initCurrentSlicedBucket(nextBucketStartTimeNs);
// Update the condition timer again, in case we skipped buckets.
mConditionTimer.newBucketStart(nextBucketStartTimeNs);
@@ -927,22 +1069,24 @@
} else {
it++;
}
+ // TODO(b/157655103): remove mCurrentBaseInfo entries when obsolete
}
- mCurrentBucketIsInvalid = false;
+ mCurrentBucketIsSkipped = false;
+ mCurrentSkippedBucket.reset();
+
// If we do not have a global base when the condition is true,
// we will have incomplete bucket for the next bucket.
if (mUseDiff && !mHasGlobalBase && mCondition) {
- mCurrentBucketIsInvalid = false;
+ mCurrentBucketIsSkipped = false;
}
mCurrentBucketStartTimeNs = nextBucketStartTimeNs;
VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
(long long)mCurrentBucketStartTimeNs);
}
-void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) {
- bool isFullBucketReached = eventTimeNs > fullBucketEndTimeNs;
- if (mCurrentBucketIsInvalid) {
+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.
mCurrentFullBucket.clear();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 784ac64..b359af7 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -50,12 +50,18 @@
// - an alarm set to the end of the bucket
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
- ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
- const int conditionIndex, 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);
+ 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,
+ const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~ValueMetricProducer();
@@ -64,23 +70,34 @@
bool pullSuccess, int64_t originalPullTimeNs) override;
// ValueMetric needs special logic if it's a pulled atom.
- void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) override {
+ void notifyAppUpgrade(const int64_t& eventTimeNs) override {
std::lock_guard<std::mutex> lock(mMutex);
if (!mSplitBucketForAppUpgrade) {
return;
}
- if (mIsPulled && mCondition) {
- pullAndMatchEventsLocked(eventTimeNs, mCondition);
+ if (mIsPulled && mCondition == ConditionState::kTrue) {
+ pullAndMatchEventsLocked(eventTimeNs);
}
flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
};
+ // 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 == ConditionState::kTrue) {
+ pullAndMatchEventsLocked(eventTimeNs);
+ }
+ flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
+ };
+
+ void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) override;
+
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
- const ConditionKey& conditionKey, bool condition,
- const LogEvent& event) override;
+ const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+ const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
private:
void onDumpReportLocked(const int64_t dumpTimeNs,
@@ -125,8 +142,15 @@
int64_t calcBucketsForwardCount(const int64_t& eventTimeNs) const;
// Mark the data as invalid.
- void invalidateCurrentBucket();
- void invalidateCurrentBucketWithoutResetBase();
+ 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.
+ void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
const int mWhatMatcherIndex;
@@ -138,7 +162,10 @@
std::vector<Matcher> mFieldMatchers;
// Value fields for matching.
- std::set<MetricDimensionKey> mMatchedMetricDimensionKeys;
+ std::set<HashableDimensionKey> mMatchedMetricDimensionKeys;
+
+ // Holds the atom id, primary key pair from a state change.
+ pair<int32_t, HashableDimensionKey> mStateChangePrimaryKey;
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
@@ -150,10 +177,6 @@
typedef struct {
// Index in multi value aggregation.
int valueIndex;
- // Holds current base value of the dimension. Take diff and update if necessary.
- Value base;
- // Whether there is a base to diff to.
- bool hasBase;
// Current value, depending on the aggregation type.
Value value;
// Number of samples collected.
@@ -165,34 +188,46 @@
bool seenNewData = false;
} Interval;
+ typedef struct {
+ // Holds current base value of the dimension. Take diff and update if necessary.
+ Value base;
+ // Whether there is a base to diff to.
+ bool hasBase;
+ // Last seen state value(s).
+ HashableDimensionKey currentState;
+ // Whether this dimensions in what key has a current state key.
+ bool hasCurrentState;
+ } BaseInfo;
+
std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
+ std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo;
+
std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
- // Pairs of (elapsed start, elapsed end) denoting buckets that were skipped.
- std::list<std::pair<int64_t, int64_t>> mSkippedBuckets;
-
const int64_t mMinBucketSizeNs;
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
+
bool hasReachedGuardRailLimit() const;
bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);
- void pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition);
+ void pullAndMatchEventsLocked(const int64_t timestampNs);
void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
- int64_t originalPullTimeNs, int64_t eventElapsedTimeNs,
- ConditionState condition);
+ int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
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();
@@ -225,11 +260,9 @@
// diff against.
bool mHasGlobalBase;
- // Invalid bucket. There was a problem in collecting data in the current bucket so we cannot
- // trust any of the data in this bucket.
- //
- // For instance, one pull failed.
- bool mCurrentBucketIsInvalid;
+ // This is to track whether or not the bucket is skipped for any of the reasons listed in
+ // BucketDropReason, many of which make the bucket potentially invalid.
+ bool mCurrentBucketIsSkipped;
const int64_t mMaxPullDelayNs;
@@ -239,12 +272,10 @@
FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
- FRIEND_TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid);
FRIEND_TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet);
FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime);
FRIEND_TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged);
@@ -253,14 +284,8 @@
FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
- FRIEND_TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid);
- FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit);
- FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed);
- FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed);
- FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed);
FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff);
FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff);
- FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated);
FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries);
FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse);
FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue);
@@ -271,15 +296,12 @@
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
- FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition);
- FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded);
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange);
@@ -288,9 +310,28 @@
FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
+ 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);
+
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed);
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed);
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit);
+ FRIEND_TEST(ValueMetricProducerTest_BucketDrop,
+ TestInvalidBucketWhenAccumulateEventWrongBucket);
+
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPushedEvents);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue);
+ FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse);
+
friend class ValueMetricProducerTestHelper;
};
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 081e61e..8d59d13 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -56,11 +56,19 @@
int64_t mDuration;
};
+struct DurationValues {
+ // Recorded duration for current partial bucket.
+ int64_t mDuration;
+
+ // Sum of past partial bucket durations in current full bucket.
+ // Used for anomaly detection.
+ int64_t mDurationFullBucket;
+};
+
class DurationTracker {
public:
DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex,
- const std::vector<Matcher>& dimensionInCondition, bool nesting,
+ sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
@@ -70,11 +78,9 @@
mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
mBucketSizeNs(bucketSizeNs),
- mDimensionInCondition(dimensionInCondition),
mNested(nesting),
mCurrentBucketStartTimeNs(currentBucketStartNs),
mDuration(0),
- mDurationFullBucket(0),
mCurrentBucketNum(currentBucketNum),
mStartTimeNs(startTimeNs),
mConditionSliced(conditionSliced),
@@ -83,10 +89,8 @@
virtual ~DurationTracker(){};
- virtual unique_ptr<DurationTracker> clone(const int64_t eventTime) = 0;
-
- virtual void noteStart(const HashableDimensionKey& key, bool condition,
- const int64_t eventTime, const ConditionKey& conditionKey) = 0;
+ virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
+ const ConditionKey& conditionKey) = 0;
virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
const bool stopAll) = 0;
virtual void noteStopAll(const int64_t eventTime) = 0;
@@ -94,6 +98,9 @@
virtual void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) = 0;
virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0;
+ virtual void onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) = 0;
+
// Flush stale buckets if needed, and return true if the tracker has no on-going duration
// events, so that the owner can safely remove the tracker.
virtual bool flushIfNeeded(
@@ -112,9 +119,12 @@
// Dump internal states for debugging
virtual void dumpStates(FILE* out, bool verbose) const = 0;
- void setEventKey(const MetricDimensionKey& eventKey) {
- mEventKey = eventKey;
- }
+ virtual int64_t getCurrentStateKeyDuration() const = 0;
+
+ virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0;
+
+ // Replace old value with new value for the given state atom.
+ virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0;
protected:
int64_t getCurrentBucketEndTimeNs() const {
@@ -143,10 +153,11 @@
}
}
- void addPastBucketToAnomalyTrackers(const int64_t& bucketValue, const int64_t& bucketNum) {
+ void addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey,
+ const int64_t& bucketValue, const int64_t& bucketNum) {
for (auto& anomalyTracker : mAnomalyTrackers) {
if (anomalyTracker != nullptr) {
- anomalyTracker->addPastBucket(mEventKey, bucketValue, bucketNum);
+ anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum);
}
}
}
@@ -167,6 +178,10 @@
return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
}
+ void setEventKey(const MetricDimensionKey& eventKey) {
+ mEventKey = eventKey;
+ }
+
// A reference to the DurationMetricProducer's config key.
const ConfigKey& mConfigKey;
@@ -180,15 +195,14 @@
const int64_t mBucketSizeNs;
- const std::vector<Matcher>& mDimensionInCondition;
-
const bool mNested;
int64_t mCurrentBucketStartTimeNs;
int64_t mDuration; // current recorded duration result (for partial bucket)
- int64_t mDurationFullBucket; // Sum of past partial buckets in current full bucket.
+ // Recorded duration results for each state key in the current partial bucket.
+ std::unordered_map<HashableDimensionKey, DurationValues> mStateKeyDurationMap;
int64_t mCurrentBucketNum;
@@ -196,7 +210,6 @@
const bool mConditionSliced;
- bool mSameConditionDimensionsInTracker;
bool mHasLinksToAllConditionDimensionsInTracker;
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 6868b8c..ee4e167 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -26,37 +26,14 @@
MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex,
- const vector<Matcher>& dimensionInCondition, bool nesting,
+ sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
int64_t currentBucketStartNs, int64_t currentBucketNum,
int64_t startTimeNs, int64_t bucketSizeNs,
bool conditionSliced, bool fullLink,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
- currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
- conditionSliced, fullLink, anomalyTrackers) {
- if (mWizard != nullptr) {
- mSameConditionDimensionsInTracker =
- mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition);
- }
-}
-
-unique_ptr<DurationTracker> MaxDurationTracker::clone(const int64_t eventTime) {
- auto clonedTracker = make_unique<MaxDurationTracker>(*this);
- for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end();) {
- if (it->second.state != kStopped) {
- it->second.lastStartTime = eventTime;
- it->second.lastDuration = 0;
- it++;
- } else {
- it = clonedTracker->mInfos.erase(it);
- }
- }
- if (clonedTracker->mInfos.empty()) {
- return nullptr;
- } else {
- return clonedTracker;
- }
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+ currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
+ anomalyTrackers) {
}
bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -113,7 +90,6 @@
}
}
-
void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const int64_t eventTime,
bool forceStop) {
VLOG("MaxDuration: key %s stop", key.toString().c_str());
@@ -252,22 +228,21 @@
if (pair.second.state == kStopped) {
continue;
}
- std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
ConditionState conditionState = mWizard->query(
- mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &conditionDimensionKeySet);
- bool conditionMet =
- (conditionState == ConditionState::kTrue) &&
- (mDimensionInCondition.size() == 0 ||
- conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
- conditionDimensionKeySet.end());
+ mConditionTrackerIndex, pair.second.conditionKeys,
+ !mHasLinksToAllConditionDimensionsInTracker);
+ bool conditionMet = (conditionState == ConditionState::kTrue);
+
VLOG("key: %s, condition: %d", pair.first.toString().c_str(), conditionMet);
noteConditionChanged(pair.first, conditionMet, timestamp);
}
}
+void MaxDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) {
+ ALOGE("MaxDurationTracker does not handle sliced state changes.");
+}
+
void MaxDurationTracker::onConditionChanged(bool condition, const int64_t timestamp) {
for (auto& pair : mInfos) {
noteConditionChanged(pair.first, condition, timestamp);
@@ -337,6 +312,20 @@
fprintf(out, "\t\t current duration %lld\n", (long long)mDuration);
}
+int64_t MaxDurationTracker::getCurrentStateKeyDuration() const {
+ ALOGE("MaxDurationTracker does not handle sliced state changes.");
+ return -1;
+}
+
+int64_t MaxDurationTracker::getCurrentStateKeyFullBucketDuration() const {
+ ALOGE("MaxDurationTracker does not handle sliced state changes.");
+ return -1;
+}
+
+void MaxDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) {
+ ALOGE("MaxDurationTracker does not handle sliced state changes.");
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 8e8f2cd..2891c6e 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -30,7 +30,7 @@
public:
MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
sp<ConditionWizard> wizard, int conditionIndex,
- const std::vector<Matcher>& dimensionInCondition, bool nesting,
+ bool nesting,
int64_t currentBucketStartNs, int64_t currentBucketNum,
int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
bool fullLink,
@@ -38,8 +38,6 @@
MaxDurationTracker(const MaxDurationTracker& tracker) = default;
- unique_ptr<DurationTracker> clone(const int64_t eventTime) override;
-
void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
@@ -56,10 +54,19 @@
void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override;
void onConditionChanged(bool condition, const int64_t timestamp) override;
+ void onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) override;
+
int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
const int64_t currentTimestamp) const override;
void dumpStates(FILE* out, bool verbose) const override;
+ int64_t getCurrentStateKeyDuration() const override;
+
+ int64_t getCurrentStateKeyFullBucketDuration() const override;
+
+ void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
+
private:
// Returns true if at least one of the mInfos is started.
bool anyStarted();
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 956383a..0d49bbc 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -26,27 +26,15 @@
OringDurationTracker::OringDurationTracker(
const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, const vector<Matcher>& dimensionInCondition,
- bool nesting, int64_t currentBucketStartNs, int64_t currentBucketNum,
- int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
- const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
- currentBucketStartNs, currentBucketNum, startTimeNs, bucketSizeNs,
- conditionSliced, fullLink, anomalyTrackers),
+ sp<ConditionWizard> wizard, int conditionIndex, bool nesting, int64_t currentBucketStartNs,
+ int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
+ bool fullLink, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+ currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
+ anomalyTrackers),
mStarted(),
mPaused() {
mLastStartTime = 0;
- if (mWizard != nullptr) {
- mSameConditionDimensionsInTracker =
- mWizard->equalOutputDimensions(conditionIndex, mDimensionInCondition);
- }
-}
-
-unique_ptr<DurationTracker> OringDurationTracker::clone(const int64_t eventTime) {
- auto clonedTracker = make_unique<OringDurationTracker>(*this);
- clonedTracker->mLastStartTime = eventTime;
- clonedTracker->mDuration = 0;
- return clonedTracker;
}
bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -101,10 +89,14 @@
mConditionKeyMap.erase(key);
}
if (mStarted.empty()) {
- mDuration += (timestamp - mLastStartTime);
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
- VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime,
- (long long)mDuration);
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (timestamp - mLastStartTime);
+ detectAndDeclareAnomaly(
+ timestamp, mCurrentBucketNum,
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
+ VLOG("record duration %lld, total duration %lld for state key %s",
+ (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(),
+ mEventKey.getStateValuesKey().toString().c_str());
}
}
@@ -123,10 +115,14 @@
void OringDurationTracker::noteStopAll(const int64_t timestamp) {
if (!mStarted.empty()) {
- mDuration += (timestamp - mLastStartTime);
- VLOG("Oring Stop all: record duration %lld %lld ", (long long)timestamp - mLastStartTime,
- (long long)mDuration);
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (timestamp - mLastStartTime);
+ VLOG("Oring Stop all: record duration %lld, total duration %lld for state key %s",
+ (long long)timestamp - mLastStartTime, (long long)getCurrentStateKeyDuration(),
+ mEventKey.getStateValuesKey().toString().c_str());
+ detectAndDeclareAnomaly(
+ timestamp, mCurrentBucketNum,
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
}
stopAnomalyAlarm(timestamp);
@@ -157,21 +153,36 @@
// Process the current bucket.
if (mStarted.size() > 0) {
- mDuration += (currentBucketEndTimeNs - mLastStartTime);
+ // Calculate the duration for the current state key.
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (currentBucketEndTimeNs - mLastStartTime);
}
- if (mDuration > 0) {
- DurationBucket current_info;
- current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
- current_info.mBucketEndNs = currentBucketEndTimeNs;
- current_info.mDuration = mDuration;
- (*output)[mEventKey].push_back(current_info);
- mDurationFullBucket += mDuration;
- VLOG(" duration: %lld", (long long)current_info.mDuration);
- }
- if (eventTimeNs > fullBucketEnd) {
- // End of full bucket, can send to anomaly tracker now.
- addPastBucketToAnomalyTrackers(mDurationFullBucket, mCurrentBucketNum);
- mDurationFullBucket = 0;
+ // Store DurationBucket info for each whatKey, stateKey pair.
+ // Note: The whatKey stored in mEventKey is constant for each DurationTracker, while the
+ // stateKey stored in mEventKey is only the current stateKey. mStateKeyDurationMap is used to
+ // store durations for each stateKey, so we need to flush the bucket by creating a
+ // DurationBucket for each stateKey.
+ for (auto& durationIt : mStateKeyDurationMap) {
+ if (durationIt.second.mDuration > 0) {
+ DurationBucket current_info;
+ current_info.mBucketStartNs = mCurrentBucketStartTimeNs;
+ current_info.mBucketEndNs = currentBucketEndTimeNs;
+ current_info.mDuration = durationIt.second.mDuration;
+ (*output)[MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first)]
+ .push_back(current_info);
+
+ durationIt.second.mDurationFullBucket += durationIt.second.mDuration;
+ VLOG(" duration: %lld", (long long)current_info.mDuration);
+ }
+
+ if (eventTimeNs > fullBucketEnd) {
+ // End of full bucket, can send to anomaly tracker now.
+ addPastBucketToAnomalyTrackers(
+ MetricDimensionKey(mEventKey.getDimensionKeyInWhat(), durationIt.first),
+ getCurrentStateKeyFullBucketDuration(), mCurrentBucketNum);
+ durationIt.second.mDurationFullBucket = 0;
+ }
+ durationIt.second.mDuration = 0;
}
if (mStarted.size() > 0) {
@@ -180,20 +191,19 @@
info.mBucketStartNs = fullBucketEnd + mBucketSizeNs * (i - 1);
info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
info.mDuration = mBucketSizeNs;
+ // Full duration buckets are attributed to the current stateKey.
(*output)[mEventKey].push_back(info);
// Safe to send these buckets to anomaly tracker since they must be full buckets.
// If it's a partial bucket, numBucketsForward would be 0.
- addPastBucketToAnomalyTrackers(info.mDuration, mCurrentBucketNum + i);
+ addPastBucketToAnomalyTrackers(mEventKey, info.mDuration, mCurrentBucketNum + i);
VLOG(" add filling bucket with duration %lld", (long long)info.mDuration);
}
} else {
if (numBucketsForward >= 2) {
- addPastBucketToAnomalyTrackers(0, mCurrentBucketNum + numBucketsForward - 1);
+ addPastBucketToAnomalyTrackers(mEventKey, 0, mCurrentBucketNum + numBucketsForward - 1);
}
}
- mDuration = 0;
-
if (numBucketsForward > 0) {
mCurrentBucketStartTimeNs = fullBucketEnd + (numBucketsForward - 1) * mBucketSizeNs;
mCurrentBucketNum += numBucketsForward;
@@ -227,17 +237,10 @@
++it;
continue;
}
- std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
ConditionState conditionState =
mWizard->query(mConditionTrackerIndex, condIt->second,
- mDimensionInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &conditionDimensionKeySet);
- if (conditionState != ConditionState::kTrue ||
- (mDimensionInCondition.size() != 0 &&
- conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
- conditionDimensionKeySet.end())) {
+ !mHasLinksToAllConditionDimensionsInTracker);
+ if (conditionState != ConditionState::kTrue) {
startedToPaused.push_back(*it);
it = mStarted.erase(it);
VLOG("Key %s started -> paused", key.toString().c_str());
@@ -247,10 +250,14 @@
}
if (mStarted.empty()) {
- mDuration += (timestamp - mLastStartTime);
- VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime),
- (long long)mDuration);
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (timestamp - mLastStartTime);
+ VLOG("record duration %lld, total duration %lld for state key %s",
+ (long long)(timestamp - mLastStartTime), (long long)getCurrentStateKeyDuration(),
+ mEventKey.getStateValuesKey().toString().c_str());
+ detectAndDeclareAnomaly(
+ timestamp, mCurrentBucketNum,
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
}
}
@@ -262,17 +269,10 @@
++it;
continue;
}
- std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
ConditionState conditionState =
mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
- mDimensionInCondition,
- !mSameConditionDimensionsInTracker,
- !mHasLinksToAllConditionDimensionsInTracker,
- &conditionDimensionKeySet);
- if (conditionState == ConditionState::kTrue &&
- (mDimensionInCondition.size() == 0 ||
- conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
- conditionDimensionKeySet.end())) {
+ !mHasLinksToAllConditionDimensionsInTracker);
+ if (conditionState == ConditionState::kTrue) {
pausedToStarted.push_back(*it);
it = mPaused.erase(it);
VLOG("Key %s paused -> started", key.toString().c_str());
@@ -313,10 +313,13 @@
} else {
if (!mStarted.empty()) {
VLOG("Condition false, all paused");
- mDuration += (timestamp - mLastStartTime);
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration +=
+ (timestamp - mLastStartTime);
mPaused.insert(mStarted.begin(), mStarted.end());
mStarted.clear();
- detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration + mDurationFullBucket);
+ detectAndDeclareAnomaly(
+ timestamp, mCurrentBucketNum,
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration());
}
}
if (mStarted.empty()) {
@@ -324,6 +327,23 @@
}
}
+void OringDurationTracker::onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) {
+ // 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;
+ }
+ // Add the current duration length to the previous state key and then update
+ // the last start time and current state key.
+ mStateKeyDurationMap[mEventKey.getStateValuesKey()].mDuration += (timestamp - mLastStartTime);
+ mLastStartTime = timestamp;
+ updateCurrentStateKey(atomId, newState);
+}
+
int64_t OringDurationTracker::predictAnomalyTimestampNs(
const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const {
@@ -333,12 +353,13 @@
// The timestamp of the current bucket end.
const int64_t currentBucketEndNs = getCurrentBucketEndTimeNs();
- // The past duration ns for the current bucket.
- int64_t currentBucketPastNs = mDuration + mDurationFullBucket;
+ // The past duration ns for the current bucket of the current stateKey.
+ int64_t currentStateBucketPastNs =
+ getCurrentStateKeyDuration() + getCurrentStateKeyFullBucketDuration();
// As we move into the future, old buckets get overwritten (so their old data is erased).
// Sum of past durations. Will change as we overwrite old buckets.
- int64_t pastNs = currentBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
+ int64_t pastNs = currentStateBucketPastNs + anomalyTracker.getSumOverPastBuckets(mEventKey);
// The refractory period end timestamp for dimension mEventKey.
const int64_t refractoryPeriodEndNs =
@@ -397,7 +418,7 @@
mEventKey,
mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futureBucketIdx);
} else if (futureBucketIdx == anomalyTracker.getNumOfPastBuckets()) {
- pastNs -= (currentBucketPastNs + (currentBucketEndNs - eventTimestampNs));
+ pastNs -= (currentStateBucketPastNs + (currentBucketEndNs - eventTimestampNs));
}
}
@@ -407,7 +428,34 @@
void OringDurationTracker::dumpStates(FILE* out, bool verbose) const {
fprintf(out, "\t\t started count %lu\n", (unsigned long)mStarted.size());
fprintf(out, "\t\t paused count %lu\n", (unsigned long)mPaused.size());
- fprintf(out, "\t\t current duration %lld\n", (long long)mDuration);
+ fprintf(out, "\t\t current duration %lld\n", (long long)getCurrentStateKeyDuration());
+}
+
+int64_t OringDurationTracker::getCurrentStateKeyDuration() const {
+ auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey());
+ if (it == mStateKeyDurationMap.end()) {
+ return 0;
+ } else {
+ return it->second.mDuration;
+ }
+}
+
+int64_t OringDurationTracker::getCurrentStateKeyFullBucketDuration() const {
+ auto it = mStateKeyDurationMap.find(mEventKey.getStateValuesKey());
+ if (it == mStateKeyDurationMap.end()) {
+ return 0;
+ } else {
+ return it->second.mDurationFullBucket;
+ }
+}
+
+void OringDurationTracker::updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) {
+ HashableDimensionKey* stateValuesKey = mEventKey.getMutableStateValuesKey();
+ for (size_t i = 0; i < stateValuesKey->getValues().size(); i++) {
+ if (stateValuesKey->getValues()[i].mField.getTag() == atomId) {
+ stateValuesKey->mutableValue(i)->mValue = newState.mValue;
+ }
+ }
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index e466169..bd8017a 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -28,16 +28,13 @@
public:
OringDurationTracker(const ConfigKey& key, const int64_t& id,
const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, const std::vector<Matcher>& dimensionInCondition,
- bool nesting, int64_t currentBucketStartNs, int64_t currentBucketNum,
- int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
- bool fullLink,
+ int conditionIndex, bool nesting, int64_t currentBucketStartNs,
+ int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs,
+ bool conditionSliced, bool fullLink,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
OringDurationTracker(const OringDurationTracker& tracker) = default;
- unique_ptr<DurationTracker> clone(const int64_t eventTime) override;
-
void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
@@ -47,6 +44,9 @@
void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) override;
void onConditionChanged(bool condition, const int64_t timestamp) override;
+ void onStateChanged(const int64_t timestamp, const int32_t atomId,
+ const FieldValue& newState) override;
+
bool flushCurrentBucket(
const int64_t& eventTimeNs,
std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
@@ -58,6 +58,12 @@
const int64_t currentTimestamp) const override;
void dumpStates(FILE* out, bool verbose) const override;
+ int64_t getCurrentStateKeyDuration() const override;
+
+ int64_t getCurrentStateKeyFullBucketDuration() const override;
+
+ void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
+
private:
// We don't need to keep track of individual durations. The information that's needed is:
// 1) which keys are started. We record the first start time.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 45c3c69..8917c36 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -19,24 +19,24 @@
#include "metrics_manager_util.h"
-#include "../condition/CombinationConditionTracker.h"
-#include "../condition/SimpleConditionTracker.h"
-#include "../condition/StateTracker.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/CombinationLogMatchingTracker.h"
-#include "../matchers/SimpleLogMatchingTracker.h"
-#include "../matchers/EventMatcherWizard.h"
-#include "../metrics/CountMetricProducer.h"
-#include "../metrics/DurationMetricProducer.h"
-#include "../metrics/EventMetricProducer.h"
-#include "../metrics/GaugeMetricProducer.h"
-#include "../metrics/ValueMetricProducer.h"
-
-#include "atoms_info.h"
-#include "stats_util.h"
-
#include <inttypes.h>
+#include "FieldValue.h"
+#include "MetricProducer.h"
+#include "condition/CombinationConditionTracker.h"
+#include "condition/SimpleConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/CombinationLogMatchingTracker.h"
+#include "matchers/EventMatcherWizard.h"
+#include "matchers/SimpleLogMatchingTracker.h"
+#include "metrics/CountMetricProducer.h"
+#include "metrics/DurationMetricProducer.h"
+#include "metrics/EventMetricProducer.h"
+#include "metrics/GaugeMetricProducer.h"
+#include "metrics/ValueMetricProducer.h"
+#include "state/StateManager.h"
+#include "stats_util.h"
+
using std::set;
using std::unordered_map;
using std::vector;
@@ -136,6 +136,105 @@
return true;
}
+// Initializes state data structures for a metric.
+// input:
+// [config]: the input config
+// [stateIds]: the slice_by_state ids for this metric
+// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
+// [allStateGroupMaps]: this map contains the mapping from state ids and state
+// values to state group ids for all states
+// output:
+// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
+// [stateGroupMap]: this map should contain the mapping from states ids and state
+// values to state group ids for all states that this metric
+// is interested in
+bool handleMetricWithStates(
+ const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ vector<int>& slicedStateAtoms,
+ unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
+ for (const auto& stateId : stateIds) {
+ auto it = stateAtomIdMap.find(stateId);
+ if (it == stateAtomIdMap.end()) {
+ ALOGW("cannot find State %" PRId64 " in the config", stateId);
+ return false;
+ }
+ int atomId = it->second;
+ slicedStateAtoms.push_back(atomId);
+
+ auto stateIt = allStateGroupMaps.find(stateId);
+ if (stateIt != allStateGroupMaps.end()) {
+ stateGroupMap[atomId] = stateIt->second;
+ }
+ }
+ return true;
+}
+
+bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
+ const vector<Matcher>& dimensionsInWhat) {
+ vector<Matcher> stateMatchers;
+ translateFieldMatcher(stateMatcher, &stateMatchers);
+
+ return subsetDimensions(stateMatchers, dimensionsInWhat);
+}
+
+// Validates a metricActivation and populates state.
+// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
+// to provide the producer with state about its activators and deactivators.
+// Returns false if there are errors.
+bool handleMetricActivation(
+ const StatsdConfig& config,
+ const int64_t metricId,
+ const int metricIndex,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const unordered_map<int64_t, int>& logTrackerMap,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation,
+ unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
+ // Check if metric has an associated activation
+ auto itr = metricToActivationMap.find(metricId);
+ if (itr == metricToActivationMap.end()) return true;
+
+ int activationIndex = itr->second;
+ const MetricActivation& metricActivation = config.metric_activation(activationIndex);
+
+ for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+ const EventActivation& activation = metricActivation.event_activation(i);
+
+ auto itr = logTrackerMap.find(activation.atom_matcher_id());
+ if (itr == logTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event activation.");
+ return false;
+ }
+
+ ActivationType activationType = (activation.has_activation_type()) ?
+ activation.activation_type() : metricActivation.activation_type();
+ std::shared_ptr<Activation> activationWrapper = std::make_shared<Activation>(
+ activationType, activation.ttl_seconds() * NS_PER_SEC);
+
+ int atomMatcherIndex = itr->second;
+ activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
+ eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
+
+ if (activation.has_deactivation_atom_matcher_id()) {
+ itr = logTrackerMap.find(activation.deactivation_atom_matcher_id());
+ if (itr == logTrackerMap.end()) {
+ ALOGE("Atom matcher not found for event deactivation.");
+ return false;
+ }
+ int deactivationAtomMatcherIndex = itr->second;
+ deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
+ eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
+ }
+ }
+
+ metricsWithActivation.push_back(metricIndex);
+ return true;
+}
+
bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
unordered_map<int64_t, int>& logTrackerMap,
vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
@@ -182,74 +281,26 @@
return true;
}
-/**
- * A StateTracker is built from a SimplePredicate which has only "start", and no "stop"
- * or "stop_all". The start must be an atom matcher that matches a state atom. It must
- * have dimension, the dimension must be the state atom's primary fields plus exclusive state
- * field. For example, the StateTracker is used in tracking UidProcessState and ScreenState.
- *
- */
-bool isStateTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) {
- // 1. must not have "stop". must have "dimension"
- if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
- auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(
- simplePredicate.dimensions().field());
- // 2. must be based on a state atom.
- if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
- // 3. dimension must be primary fields + state field IN ORDER
- size_t expectedDimensionCount = it->second.primaryFields.size() + 1;
- vector<Matcher> dimensions;
- translateFieldMatcher(simplePredicate.dimensions(), &dimensions);
- if (dimensions.size() != expectedDimensionCount) {
- return false;
- }
- // 3.1 check the primary fields first.
- size_t index = 0;
- for (const auto& field : it->second.primaryFields) {
- Matcher matcher = getSimpleMatcher(it->first, field);
- if (!(matcher == dimensions[index])) {
- return false;
- }
- primaryKeys->push_back(matcher);
- index++;
- }
- Matcher stateFieldMatcher =
- getSimpleMatcher(it->first, it->second.exclusiveField);
- // 3.2 last dimension should be the exclusive field.
- if (!(dimensions.back() == stateFieldMatcher)) {
- return false;
- }
- return true;
- }
- }
- return false;
-} // namespace statsd
-
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);
int index = allConditionTrackers.size();
switch (condition.contents_case()) {
case Predicate::ContentsCase::kSimplePredicate: {
- vector<Matcher> primaryKeys;
- if (isStateTracker(condition.simple_predicate(), &primaryKeys)) {
- allConditionTrackers.push_back(new StateTracker(key, condition.id(), index,
- condition.simple_predicate(),
- logTrackerMap, primaryKeys));
- } else {
- allConditionTrackers.push_back(new SimpleConditionTracker(
- key, condition.id(), index, condition.simple_predicate(),
- logTrackerMap));
- }
+ allConditionTrackers.push_back(new SimpleConditionTracker(
+ key, condition.id(), index, condition.simple_predicate(), logTrackerMap));
break;
}
case Predicate::ContentsCase::kCombination: {
@@ -273,7 +324,7 @@
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()) {
@@ -284,23 +335,59 @@
return true;
}
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
+ for (int i = 0; i < config.state_size(); i++) {
+ const State& state = config.state(i);
+ const int64_t stateId = state.id();
+ stateAtomIdMap[stateId] = state.atom_id();
+
+ const StateMap& stateMap = state.map();
+ for (auto group : stateMap.group()) {
+ for (auto value : group.value()) {
+ allStateGroupMaps[stateId][value] = group.group_id();
+ }
+ }
+ }
+
+ return true;
+}
+
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, std::vector<int>>& conditionToMetricMap,
- unordered_map<int, std::vector<int>>& trackerToMetricMap,
- unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds) {
+ unordered_map<int, vector<int>>& conditionToMetricMap,
+ unordered_map<int, vector<int>>& trackerToMetricMap,
+ unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
+ unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+ unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+ vector<int>& metricsWithActivation) {
sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers);
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
- config.event_metric_size() + config.value_metric_size();
+ config.event_metric_size() + config.gauge_metric_size() +
+ config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
- StatsPullerManager statsPullerManager;
+
+ // Construct map from metric id to metric activation index. The map will be used to determine
+ // the metric activation corresponding to a metric.
+ unordered_map<int64_t, int> metricToActivationMap;
+ for (int i = 0; i < config.metric_activation_size(); i++) {
+ const MetricActivation& metricActivation = config.metric_activation(i);
+ int64_t metricId = metricActivation.metric_id();
+ if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
+ ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId);
+ return false;
+ }
+ metricToActivationMap.insert({metricId, i});
+ }
// Build MetricProducers for each metric defined in config.
// build CountMetricProducer
@@ -323,10 +410,9 @@
int conditionIndex = -1;
if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
return false;
}
} else {
@@ -336,8 +422,32 @@
}
}
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return false;
+ }
+ } else {
+ if (metric.state_link_size() > 0) {
+ ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
+ return false;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> countProducer =
- new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs);
+ new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ timeBaseTimeNs, currentTimeNs, eventActivationMap,
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap);
allMetricProducers.push_back(countProducer);
}
@@ -405,9 +515,46 @@
}
}
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
+ ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
+ return false;
+ }
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return false;
+ }
+ } else {
+ if (metric.state_link_size() > 0) {
+ ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
+ return false;
+ }
+ }
+
+ // Check that all metric state links are a subset of dimensions_in_what fields.
+ std::vector<Matcher> dimensionsInWhat;
+ translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+ for (const auto& stateLink : metric.state_link()) {
+ if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+ return false;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> durationMetric = new DurationMetricProducer(
- key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
- trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, currentTimeNs);
+ key, metric, conditionIndex, initialConditionCache, trackerIndices[0],
+ trackerIndices[1], trackerIndices[2], nesting, wizard, internalDimensions,
+ timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap,
+ slicedStateAtoms, stateGroupMap);
allMetricProducers.push_back(durationMetric);
}
@@ -442,8 +589,17 @@
}
}
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> eventMetric =
- new EventMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
+ new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+ timeBaseTimeNs, eventActivationMap, eventDeactivationMap);
allMetricProducers.push_back(eventMetric);
}
@@ -482,7 +638,7 @@
return false;
}
int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int conditionIndex = -1;
if (metric.has_condition()) {
@@ -499,9 +655,41 @@
}
}
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return false;
+ }
+ } else {
+ if (metric.state_link_size() > 0) {
+ ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state");
+ return false;
+ }
+ }
+
+ // Check that all metric state links are a subset of dimensions_in_what fields.
+ std::vector<Matcher> dimensionsInWhat;
+ translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+ for (const auto& stateLink : metric.state_link()) {
+ if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+ return false;
+ }
+ }
+
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(
+ config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation, eventActivationMap, eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> valueProducer = new ValueMetricProducer(
- key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId,
- timeBaseTimeNs, currentTimeNs, pullerManager);
+ key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
+ matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager,
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
allMetricProducers.push_back(valueProducer);
}
@@ -542,7 +730,7 @@
return false;
}
int atomTagId = *(atomMatcher->getAtomIds().begin());
- int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+ int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
int triggerTrackerIndex;
int triggerAtomId = -1;
@@ -585,10 +773,18 @@
}
}
+ unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+ unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+ bool success = handleMetricActivation(config, metric.id(), metricIndex,
+ metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
+ deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
+ eventDeactivationMap);
+ if (!success) return false;
+
sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
- key, metric, conditionIndex, wizard,
- trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId,
- timeBaseTimeNs, currentTimeNs, pullerManager);
+ 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) {
@@ -599,15 +795,30 @@
}
noReportMetricIds.insert(no_report_metric);
}
+
+ const set<int> whitelistedAtomIds(config.whitelisted_atom_ids().begin(),
+ config.whitelisted_atom_ids().end());
+ for (const auto& it : allMetricProducers) {
+ // Register metrics to StateTrackers
+ for (int atomId : it->getSlicedStateAtoms()) {
+ // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced
+ // state atom is not allowed.
+ if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) {
+ StateManager::getInstance().registerListener(atomId, it);
+ } else {
+ return false;
+ }
+ }
+ }
return true;
}
bool initAlerts(const StatsdConfig& config,
const unordered_map<int64_t, int>& metricProducerMap,
+ unordered_map<int64_t, int>& alertTrackerMap,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
vector<sp<MetricProducer>>& allMetricProducers,
vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
- unordered_map<int64_t, int> anomalyTrackerMap;
for (int i = 0; i < config.alert_size(); i++) {
const Alert& alert = config.alert(i);
const auto& itr = metricProducerMap.find(alert.metric_id());
@@ -632,7 +843,7 @@
// The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
return false;
}
- anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
+ alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
allAnomalyTrackers.push_back(anomalyTracker);
}
for (int i = 0; i < config.subscription_size(); ++i) {
@@ -646,8 +857,8 @@
(long long)subscription.id());
return false;
}
- const auto& itr = anomalyTrackerMap.find(subscription.rule_id());
- if (itr == anomalyTrackerMap.end()) {
+ const auto& itr = alertTrackerMap.find(subscription.rule_id());
+ if (itr == alertTrackerMap.end()) {
ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
(long long)subscription.id(), (long long)subscription.rule_id());
return false;
@@ -703,73 +914,6 @@
return true;
}
-bool initMetricActivations(const ConfigKey& key, const StatsdConfig& config,
- const int64_t currentTimeNs,
- const unordered_map<int64_t, int> &logEventTrackerMap,
- const unordered_map<int64_t, int> &metricProducerMap,
- vector<sp<MetricProducer>>& allMetricProducers,
- unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
- unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
- vector<int>& metricsWithActivation) {
- for (int i = 0; i < config.metric_activation_size(); ++i) {
- const MetricActivation& metric_activation = config.metric_activation(i);
- auto itr = metricProducerMap.find(metric_activation.metric_id());
- if (itr == metricProducerMap.end()) {
- ALOGE("Metric id not found in metric activation: %lld",
- (long long)metric_activation.metric_id());
- return false;
- }
- const int metricTrackerIndex = itr->second;
- if (metricTrackerIndex < 0 || metricTrackerIndex >= (int)allMetricProducers.size()) {
- ALOGE("Invalid metric tracker index.");
- return false;
- }
- const sp<MetricProducer>& metric = allMetricProducers[metricTrackerIndex];
- metricsWithActivation.push_back(metricTrackerIndex);
- for (int j = 0; j < metric_activation.event_activation_size(); ++j) {
- const EventActivation& activation = metric_activation.event_activation(j);
- auto logTrackerIt = logEventTrackerMap.find(activation.atom_matcher_id());
- if (logTrackerIt == logEventTrackerMap.end()) {
- ALOGE("Atom matcher not found for event activation.");
- return false;
- }
- const int atomMatcherIndex = logTrackerIt->second;
- activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(
- metricTrackerIndex);
-
- ActivationType activationType;
- if (activation.has_activation_type()) {
- activationType = activation.activation_type();
- } else {
- activationType = metric_activation.activation_type();
- }
-
- if (activation.has_deactivation_atom_matcher_id()) {
- auto deactivationAtomMatcherIt =
- logEventTrackerMap.find(activation.deactivation_atom_matcher_id());
- if (deactivationAtomMatcherIt == logEventTrackerMap.end()) {
- ALOGE("Atom matcher not found for event deactivation.");
- return false;
- }
- const int deactivationMatcherIndex = deactivationAtomMatcherIt->second;
- deactivationAtomTrackerToMetricMap[deactivationMatcherIndex]
- .push_back(metricTrackerIndex);
- metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds(),
- deactivationMatcherIndex);
- } else {
- metric->addActivation(atomMatcherIndex, activationType, activation.ttl_seconds());
- }
- }
- }
- return true;
-}
-
-void prepareFirstBucket(const vector<sp<MetricProducer>>& allMetricProducers) {
- for (const auto& metric: allMetricProducers) {
- metric->prepareFirstBucket();
- }
-}
-
bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -785,11 +929,15 @@
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) {
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;
if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
ALOGE("initLogMatchingTrackers failed");
@@ -798,20 +946,26 @@
VLOG("initLogMatchingTrackers succeed...");
if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
- trackerToConditionMap)) {
+ trackerToConditionMap, initialConditionCache)) {
ALOGE("initConditionTrackers failed");
return false;
}
+ if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
+ ALOGE("initStates failed");
+ return false;
+ }
if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap,
- conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
- conditionToMetricMap, trackerToMetricMap, metricProducerMap,
- noReportMetricIds)) {
+ conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps,
+ allConditionTrackers, initialConditionCache, allMetricProducers,
+ conditionToMetricMap, trackerToMetricMap, metricProducerMap, noReportMetricIds,
+ activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+ metricsWithActivation)) {
ALOGE("initMetricProducers failed");
return false;
}
- if (!initAlerts(config, metricProducerMap, anomalyAlarmMonitor, allMetricProducers,
- allAnomalyTrackers)) {
+ if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor,
+ allMetricProducers, allAnomalyTrackers)) {
ALOGE("initAlerts failed");
return false;
}
@@ -820,14 +974,6 @@
ALOGE("initAlarms failed");
return false;
}
- if (!initMetricActivations(key, config, currentTimeNs, logTrackerMap, metricProducerMap,
- allMetricProducers, activationAtomTrackerToMetricMap,
- deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
- ALOGE("initMetricActivations failed");
- return false;
- }
-
- prepareFirstBucket(allMetricProducers);
return true;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index ece986b..96b5c26 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -60,12 +60,25 @@
// [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.
+// input:
+// [config]: the input config
+// output:
+// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
+// [allStateGroupMaps]: this map should contain the mapping from states ids and state
+// values to state group ids for all states
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps);
// Initialize MetricProducers.
// input:
@@ -74,6 +87,9 @@
// [timeBaseSec]: start time base for all metrics
// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
// [conditionTrackerMap]: condition name to index mapping
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+// state group ids for all states
// output:
// [allMetricProducers]: contains the list of sp to the MetricProducers created.
// [conditionToMetricMap]: contains the mapping from condition tracker index to
@@ -86,11 +102,17 @@
const std::unordered_map<int64_t, int>& conditionTrackerMap,
const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
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 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,
- std::set<int64_t>& noReportMetricIds);
+ std::set<int64_t>& noReportMetricIds,
+ std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::vector<int>& metricsWithActivation);
// Initialize MetricsManager from StatsdConfig.
// Parameters are the members of MetricsManager. See MetricsManager for declaration.
@@ -109,11 +131,10 @@
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+ std::unordered_map<int64_t, int>& alertTrackerMap,
vector<int>& metricsWithActivation,
std::set<int64_t>& noReportMetricIds);
-bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index ab0e86e..acf40c8 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -545,7 +545,15 @@
{"AID_LMKD", 1069},
{"AID_LLKD", 1070},
{"AID_IORAPD", 1071},
+ {"AID_GPU_SERVICE", 1072},
{"AID_NETWORK_STACK", 1073},
+ {"AID_GSID", 1074},
+ {"AID_FSVERITY_CERT", 1075},
+ {"AID_CREDSTORE", 1076},
+ {"AID_EXTERNAL_STORAGE", 1077},
+ {"AID_EXT_DATA_RW", 1078},
+ {"AID_EXT_OBB_RW", 1079},
+ {"AID_CONTEXT_HUB", 1080},
{"AID_SHELL", 2000},
{"AID_CACHE", 2001},
{"AID_DIAG", 2002}};
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index bfac6e3..22250ae 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -21,10 +21,11 @@
#include "packages/PackageInfoListener.h"
#include "stats_util.h"
-#include <binder/IShellCallback.h>
#include <gtest/gtest_prod.h>
#include <stdio.h>
#include <utils/RefBase.h>
+#include <utils/String16.h>
+
#include <list>
#include <mutex>
#include <set>
@@ -138,7 +139,7 @@
// record is deleted.
void appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
bool includeVersionStrings, bool includeInstaller,
- util::ProtoOutputStream* proto);
+ ProtoOutputStream* proto);
// Forces the output to be cleared. We still generate a snapshot based on the current state.
// This results in extra data uploaded but helps us reconstruct the uid mapping on the server
@@ -148,7 +149,7 @@
// Get currently cached value of memory used by UID map.
size_t getBytesUsed() const;
- std::set<int32_t> getAppUid(const string& package) const;
+ virtual std::set<int32_t> getAppUid(const string& package) const;
// Write current PackageInfoSnapshot to ProtoOutputStream.
// interestingUids: If not empty, only write the package info for these uids. If empty, write
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index d6a0433..fd883c2 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -18,6 +18,8 @@
#include "ShellSubscriber.h"
+#include <android-base/file.h>
+
#include "matchers/matcher_util.h"
#include "stats_log_util.h"
@@ -29,82 +31,167 @@
const static int FIELD_ID_ATOM = 1;
-void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver,
- int timeoutSec) {
- VLOG("start new shell subscription");
+void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
+ int myToken = claimToken();
+ VLOG("ShellSubscriber: new subscription %d has come in", myToken);
+ mSubscriptionShouldEnd.notify_one();
+
+ shared_ptr<SubscriptionInfo> mySubscriptionInfo = make_shared<SubscriptionInfo>(in, out);
+ if (!readConfig(mySubscriptionInfo)) return;
+
{
- std::lock_guard<std::mutex> lock(mMutex);
- if (mResultReceiver != nullptr) {
- VLOG("Only one shell subscriber is allowed.");
- return;
+ std::unique_lock<std::mutex> lock(mMutex);
+ mSubscriptionInfo = mySubscriptionInfo;
+ spawnHelperThread(myToken);
+ waitForSubscriptionToEndLocked(mySubscriptionInfo, myToken, lock, timeoutSec);
+
+ if (mSubscriptionInfo == mySubscriptionInfo) {
+ mSubscriptionInfo = nullptr;
}
- mInput = in;
- mOutput = out;
- mResultReceiver = resultReceiver;
- IInterface::asBinder(mResultReceiver)->linkToDeath(this);
- }
- // Note that the following is blocking, and it's intended as we cannot return until the shell
- // cmd exits, otherwise all resources & FDs will be automatically closed.
-
- // Read config forever until EOF is reached. Clients may send multiple configs -- each new
- // config replace the previous one.
- readConfig(in);
- VLOG("timeout : %d", timeoutSec);
-
- // Now we have read an EOF we now wait for the semaphore until the client exits.
- VLOG("Now wait for client to exit");
- std::unique_lock<std::mutex> lk(mMutex);
-
- if (timeoutSec > 0) {
- mShellDied.wait_for(lk, timeoutSec * 1s,
- [this, resultReceiver] { return mResultReceiver != resultReceiver; });
- } else {
- mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
}
}
-void ShellSubscriber::updateConfig(const ShellSubscription& config) {
- std::lock_guard<std::mutex> lock(mMutex);
- mPushedMatchers.clear();
- mPulledInfo.clear();
+void ShellSubscriber::spawnHelperThread(int myToken) {
+ std::thread t([this, myToken] { pullAndSendHeartbeats(myToken); });
+ t.detach();
+}
- for (const auto& pushed : config.pushed()) {
- mPushedMatchers.push_back(pushed);
- VLOG("adding matcher for atom %d", pushed.atom_id());
+void ShellSubscriber::waitForSubscriptionToEndLocked(shared_ptr<SubscriptionInfo> myInfo,
+ int myToken,
+ std::unique_lock<std::mutex>& lock,
+ int timeoutSec) {
+ if (timeoutSec > 0) {
+ mSubscriptionShouldEnd.wait_for(lock, timeoutSec * 1s, [this, myToken, &myInfo] {
+ return mToken != myToken || !myInfo->mClientAlive;
+ });
+ } else {
+ mSubscriptionShouldEnd.wait(lock, [this, myToken, &myInfo] {
+ return mToken != myToken || !myInfo->mClientAlive;
+ });
+ }
+}
+
+// Atomically claim the next token. Token numbers denote subscriber ordering.
+int ShellSubscriber::claimToken() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ int myToken = ++mToken;
+ return myToken;
+}
+
+// Read and parse single config. There should only one config per input.
+bool ShellSubscriber::readConfig(shared_ptr<SubscriptionInfo> subscriptionInfo) {
+ // Read the size of the config.
+ size_t bufferSize;
+ if (!android::base::ReadFully(subscriptionInfo->mInputFd, &bufferSize, sizeof(bufferSize))) {
+ return false;
}
- int64_t token = getElapsedRealtimeNs();
- mPullToken = token;
+ // Read the config.
+ vector<uint8_t> buffer(bufferSize);
+ if (!android::base::ReadFully(subscriptionInfo->mInputFd, buffer.data(), bufferSize)) {
+ return false;
+ }
- int64_t minInterval = -1;
+ // Parse the config.
+ ShellSubscription config;
+ if (!config.ParseFromArray(buffer.data(), bufferSize)) {
+ return false;
+ }
+
+ // Update SubscriptionInfo with state from config
+ for (const auto& pushed : config.pushed()) {
+ subscriptionInfo->mPushedMatchers.push_back(pushed);
+ }
+
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()) {
+ auto it = UidMap::sAidToUidMapping.find(pkg);
+ if (it != UidMap::sAidToUidMapping.end()) {
+ uids.push_back(it->second);
+ } else {
+ packages.push_back(pkg);
+ }
}
- mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis());
+ subscriptionInfo->mPulledInfo.emplace_back(pulled.matcher(), pulled.freq_millis(), packages,
+ uids);
VLOG("adding matcher for pulled atom %d", pulled.matcher().atom_id());
}
- if (mPulledInfo.size() > 0 && minInterval > 0) {
- // This thread is guaranteed to terminate after it detects the token is different or
- // cleaned up.
- std::thread puller([token, minInterval, this] { startPull(token, minInterval); });
- puller.detach();
+ return true;
+}
+
+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: helper thread %d done!", myToken);
+ return;
+ }
+
+ int64_t nowMillis = getElapsedRealtimeMillis();
+ int64_t nowNanos = getElapsedRealtimeNs();
+ for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) {
+ if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval >= nowMillis) {
+ continue;
+ }
+
+ vector<int32_t> uids;
+ getUidsForPullAtom(&uids, pullInfo);
+
+ vector<std::shared_ptr<LogEvent>> data;
+ mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, nowNanos, &data);
+ VLOG("Pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id());
+ writePulledAtomsLocked(data, pullInfo.mPullerMatcher);
+
+ 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: helper thread %d sleeping for %lld ms", myToken,
+ (long long)sleepTimeMs);
+ std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs));
}
}
-void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
- const SimpleAtomMatcher& matcher) {
- if (mOutput == 0) return;
- int count = 0;
+void ShellSubscriber::getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo) {
+ uids->insert(uids->end(), pullInfo.mPullUids.begin(), pullInfo.mPullUids.end());
+ // This is slow. Consider storing the uids per app and listening to uidmap updates.
+ for (const string& pkg : pullInfo.mPullPackages) {
+ set<int32_t> uidsForPkg = mUidMap->getAppUid(pkg);
+ uids->insert(uids->end(), uidsForPkg.begin(), uidsForPkg.end());
+ }
+ uids->push_back(DEFAULT_PULL_UID);
+}
+
+void ShellSubscriber::writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data,
+ const SimpleAtomMatcher& matcher) {
mProto.clear();
+ int count = 0;
for (const auto& event : data) {
- VLOG("%s", event->ToString().c_str());
if (matchesSimple(*mUidMap, matcher, *event)) {
- VLOG("matched");
count++;
uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
@@ -113,120 +200,44 @@
}
}
- if (count > 0) {
- // First write the payload size.
- size_t bufferSize = mProto.size();
- write(mOutput, &bufferSize, sizeof(bufferSize));
- VLOG("%d atoms, proto size: %zu", count, bufferSize);
- // Then write the payload.
- mProto.flush(mOutput);
- }
- mProto.clear();
-}
-
-void ShellSubscriber::startPull(int64_t token, int64_t intervalMillis) {
- while (1) {
- int64_t nowMillis = getElapsedRealtimeMillis();
- {
- std::lock_guard<std::mutex> lock(mMutex);
- if (mPulledInfo.size() == 0 || mPullToken != token) {
- VLOG("Pulling thread %lld done!", (long long)token);
- return;
- }
- for (auto& pullInfo : mPulledInfo) {
- if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval < nowMillis) {
- VLOG("pull atom %d now", pullInfo.mPullerMatcher.atom_id());
-
- vector<std::shared_ptr<LogEvent>> data;
- mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), &data);
- VLOG("pulled %zu atoms", data.size());
- if (data.size() > 0) {
- writeToOutputLocked(data, pullInfo.mPullerMatcher);
- }
- pullInfo.mPrevPullElapsedRealtimeMs = nowMillis;
- }
- }
- }
- VLOG("Pulling thread %lld sleep....", (long long)token);
- std::this_thread::sleep_for(std::chrono::milliseconds(intervalMillis));
- }
-}
-
-void ShellSubscriber::readConfig(int in) {
- if (in <= 0) {
- return;
- }
-
- while (1) {
- size_t bufferSize = 0;
- int result = 0;
- if ((result = read(in, &bufferSize, sizeof(bufferSize))) == 0) {
- VLOG("Done reading");
- break;
- } else if (result < 0 || result != sizeof(bufferSize)) {
- ALOGE("Error reading config size");
- break;
- }
-
- vector<uint8_t> buffer(bufferSize);
- if ((result = read(in, buffer.data(), bufferSize)) > 0 && ((size_t)result) == bufferSize) {
- ShellSubscription config;
- if (config.ParseFromArray(buffer.data(), bufferSize)) {
- updateConfig(config);
- } else {
- ALOGE("error parsing the config");
- break;
- }
- } else {
- VLOG("Error reading the config, returned: %d, expecting %zu", result, bufferSize);
- break;
- }
- }
-}
-
-void ShellSubscriber::cleanUpLocked() {
- // The file descriptors will be closed by binder.
- mInput = 0;
- mOutput = 0;
- mResultReceiver = nullptr;
- mPushedMatchers.clear();
- mPulledInfo.clear();
- mPullToken = 0;
- VLOG("done clean up");
+ if (count > 0) attemptWriteToPipeLocked(mProto.size());
}
void ShellSubscriber::onLogEvent(const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
+ if (!mSubscriptionInfo) return;
- if (mOutput <= 0) {
- return;
- }
- for (const auto& matcher : mPushedMatchers) {
+ mProto.clear();
+ for (const auto& matcher : mSubscriptionInfo->mPushedMatchers) {
if (matchesSimple(*mUidMap, matcher, event)) {
- VLOG("%s", event.ToString().c_str());
uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
event.ToProto(mProto);
mProto.end(atomToken);
- // First write the payload size.
- size_t bufferSize = mProto.size();
- write(mOutput, &bufferSize, sizeof(bufferSize));
-
- // Then write the payload.
- mProto.flush(mOutput);
- mProto.clear();
- break;
+ attemptWriteToPipeLocked(mProto.size());
}
}
}
-void ShellSubscriber::binderDied(const wp<IBinder>& who) {
- {
- VLOG("Shell exits");
- std::lock_guard<std::mutex> lock(mMutex);
- cleanUpLocked();
+// 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::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;
}
- mShellDied.notify_all();
+
+ // 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;
+ }
+
+ mLastWriteMs = getElapsedRealtimeMillis();
}
} // namespace statsd
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 86d8590..4c05fa7 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -16,16 +16,17 @@
#pragma once
-#include "logd/LogEvent.h"
-
#include <android/util/ProtoOutputStream.h>
-#include <binder/IResultReceiver.h>
+#include <private/android_filesystem_config.h>
+
#include <condition_variable>
#include <mutex>
#include <thread>
+
#include "external/StatsPullerManager.h"
#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "logd/LogEvent.h"
#include "packages/UidMap.h"
namespace android {
@@ -37,11 +38,11 @@
*
* A shell subscription lasts *until shell exits*. Unlike config based clients, a shell client
* communicates with statsd via file descriptors. They can subscribe pushed and pulled atoms.
- * The atoms are sent back to the client in real time, as opposed to
- * keeping the data in memory. Shell clients do not subscribe aggregated metrics, as they are
- * responsible for doing the aggregation after receiving the atom events.
+ * The atoms are sent back to the client in real time, as opposed to keeping the data in memory.
+ * Shell clients do not subscribe aggregated metrics, as they are responsible for doing the
+ * aggregation after receiving the atom events.
*
- * Shell client pass ShellSubscription in the proto binary format. Client can update the
+ * Shell clients pass ShellSubscription in the proto binary format. Clients can update the
* subscription by sending a new subscription. The new subscription would replace the old one.
* Input data stream format is:
*
@@ -53,43 +54,70 @@
* The stream would be in the following format:
* |size_t|shellData proto|size_t|shellData proto|....
*
- * Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
+ * Only one shell subscriber is allowed at a time because each shell subscriber blocks one thread
* until it exits.
*/
-class ShellSubscriber : public virtual IBinder::DeathRecipient {
+class ShellSubscriber : public virtual RefBase {
public:
ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr)
: mUidMap(uidMap), mPullerMgr(pullerMgr){};
- /**
- * Start a new subscription.
- */
- void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver,
- int timeoutSec);
-
- void binderDied(const wp<IBinder>& who);
+ void startNewSubscription(int inFd, int outFd, int timeoutSec);
void onLogEvent(const LogEvent& event);
private:
struct PullInfo {
- PullInfo(const SimpleAtomMatcher& matcher, int64_t interval)
- : mPullerMatcher(matcher), mInterval(interval), mPrevPullElapsedRealtimeMs(0) {
+ PullInfo(const SimpleAtomMatcher& matcher, int64_t interval,
+ const std::vector<std::string>& packages, const std::vector<int32_t>& uids)
+ : mPullerMatcher(matcher),
+ mInterval(interval),
+ mPrevPullElapsedRealtimeMs(0),
+ mPullPackages(packages),
+ mPullUids(uids) {
}
SimpleAtomMatcher mPullerMatcher;
int64_t mInterval;
int64_t mPrevPullElapsedRealtimeMs;
+ std::vector<std::string> mPullPackages;
+ std::vector<int32_t> mPullUids;
};
- void readConfig(int in);
- void updateConfig(const ShellSubscription& config);
+ struct SubscriptionInfo {
+ SubscriptionInfo(const int& inputFd, const int& outputFd)
+ : mInputFd(inputFd), mOutputFd(outputFd), mClientAlive(true) {
+ }
- void startPull(int64_t token, int64_t intervalMillis);
+ int mInputFd;
+ int mOutputFd;
+ std::vector<SimpleAtomMatcher> mPushedMatchers;
+ std::vector<PullInfo> mPulledInfo;
+ bool mClientAlive;
+ };
- void cleanUpLocked();
+ int claimToken();
- void writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
- const SimpleAtomMatcher& matcher);
+ bool readConfig(std::shared_ptr<SubscriptionInfo> subscriptionInfo);
+
+ void spawnHelperThread(int myToken);
+
+ void waitForSubscriptionToEndLocked(std::shared_ptr<SubscriptionInfo> myInfo,
+ int myToken,
+ std::unique_lock<std::mutex>& lock,
+ int timeoutSec);
+
+ // 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 attemptWriteToPipeLocked(size_t dataSize);
sp<UidMap> mUidMap;
@@ -99,19 +127,18 @@
mutable std::mutex mMutex;
- std::condition_variable mShellDied; // semaphore for waiting until shell exits.
+ std::condition_variable mSubscriptionShouldEnd;
- int mInput; // The input file descriptor
+ std::shared_ptr<SubscriptionInfo> mSubscriptionInfo = nullptr;
- int mOutput; // The output file descriptor
+ int mToken = 0;
- sp<IResultReceiver> mResultReceiver;
+ const int32_t DEFAULT_PULL_UID = AID_SYSTEM;
- std::vector<SimpleAtomMatcher> mPushedMatchers;
-
- std::vector<PullInfo> mPulledInfo;
-
- int64_t mPullToken = 0; // A unique token to identify a puller thread.
+ // Tracks when we last send data to perfd. We need that time to determine
+ // when next to send a heartbeat.
+ int64_t mLastWriteMs = 0;
+ const int64_t kMsBetweenHeartbeats = 1000;
};
} // namespace statsd
diff --git a/cmds/statsd/src/shell/shell_config.proto b/cmds/statsd/src/shell/shell_config.proto
index 73cb49a..07d0310 100644
--- a/cmds/statsd/src/shell/shell_config.proto
+++ b/cmds/statsd/src/shell/shell_config.proto
@@ -28,6 +28,9 @@
/* gap between two pulls in milliseconds */
optional int32 freq_millis = 2;
+
+ /* Packages that the pull is requested from */
+ repeated string packages = 3;
}
message ShellSubscription {
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
index bedfa7d..b877cc9 100755
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -36,8 +36,6 @@
namespace os {
namespace statsd {
-static const int kLogMsgHeaderSize = 28;
-
StatsSocketListener::StatsSocketListener(std::shared_ptr<LogEventQueue> queue)
: SocketListener(getLogSocket(), false /*start listen*/), mQueue(queue) {
}
@@ -92,7 +90,7 @@
cred->uid = DEFAULT_OVERFLOWUID;
}
- char* ptr = ((char*)buffer) + sizeof(android_log_header_t);
+ uint8_t* ptr = ((uint8_t*)buffer) + sizeof(android_log_header_t);
n -= sizeof(android_log_header_t);
// When a log failed to write to statsd socket (e.g., due ot EBUSY), a special message would
@@ -121,18 +119,17 @@
}
}
- log_msg msg;
-
- msg.entry.len = n;
- msg.entry.hdr_size = kLogMsgHeaderSize;
- msg.entry.sec = time(nullptr);
- msg.entry.pid = cred->pid;
- msg.entry.uid = cred->uid;
-
- memcpy(msg.buf + kLogMsgHeaderSize, ptr, n + 1);
+ // move past the 4-byte StatsEventTag
+ uint8_t* msg = ptr + sizeof(uint32_t);
+ uint32_t len = n - sizeof(uint32_t);
+ uint32_t uid = cred->uid;
+ uint32_t pid = cred->pid;
int64_t oldestTimestamp;
- if (!mQueue->push(std::make_unique<LogEvent>(msg), &oldestTimestamp)) {
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(uid, pid);
+ logEvent->parseBuffer(msg, len);
+
+ if (!mQueue->push(std::move(logEvent), &oldestTimestamp)) {
StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp);
}
diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h
new file mode 100644
index 0000000..6388001
--- /dev/null
+++ b/cmds/statsd/src/state/StateListener.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <utils/RefBase.h>
+
+#include "HashableDimensionKey.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateListener : public virtual RefBase {
+public:
+ StateListener(){};
+
+ virtual ~StateListener(){};
+
+ /**
+ * Interface for handling a state change.
+ *
+ * The old and new state values map to the original state values.
+ * StateTrackers only track the original state values and are unaware
+ * of higher-level state groups. MetricProducers hold information on
+ * state groups and are responsible for mapping original state values to
+ * the correct state group.
+ *
+ * [eventTimeNs]: Time of the state change log event.
+ * [atomId]: The id of the state atom
+ * [primaryKey]: The primary field values of the state atom
+ * [oldState]: Previous state value before state change
+ * [newState]: Current state value after state change
+ */
+ virtual void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
new file mode 100644
index 0000000..c29afeb
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "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;
+}
+
+void StateManager::clear() {
+ mStateTrackers.clear();
+}
+
+void StateManager::onLogEvent(const LogEvent& 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);
+ }
+ }
+}
+
+void StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
+ // Check if state tracker already exists.
+ if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
+ mStateTrackers[atomId] = new StateTracker(atomId);
+ }
+ mStateTrackers[atomId]->registerListener(listener);
+}
+
+void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ // Hold the sp<> until the lock is released so that ~StateTracker() is
+ // not called while the lock is held.
+ sp<StateTracker> toRemove;
+
+ // Unregister listener from correct StateTracker
+ auto it = mStateTrackers.find(atomId);
+ if (it != mStateTrackers.end()) {
+ it->second->unregisterListener(listener);
+
+ // Remove the StateTracker if it has no listeners
+ if (it->second->getListenersCount() == 0) {
+ toRemove = it->second;
+ mStateTrackers.erase(it);
+ }
+ } else {
+ ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist",
+ atomId);
+ }
+ lock.unlock();
+}
+
+bool StateManager::getStateValue(const int32_t atomId, const HashableDimensionKey& key,
+ FieldValue* output) const {
+ auto it = mStateTrackers.find(atomId);
+ if (it != mStateTrackers.end()) {
+ return it->second->getStateValue(key, output);
+ }
+ 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
new file mode 100644
index 0000000..18c404c
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <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"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * This class is NOT thread safe.
+ * It should only be used while StatsLogProcessor's lock is held.
+ */
+class StateManager : public virtual RefBase {
+public:
+ StateManager();
+
+ ~StateManager(){};
+
+ // Returns a pointer to the single, shared StateManager object.
+ static StateManager& getInstance();
+
+ // Unregisters all listeners and removes all trackers from StateManager.
+ void clear();
+
+ // Notifies the correct StateTracker of an event.
+ void onLogEvent(const LogEvent& event);
+
+ // Notifies the StateTracker for the given atomId to register listener.
+ // If the correct StateTracker does not exist, a new StateTracker is created.
+ // Note: StateTrackers can be created for non-state atoms. They are essentially empty and
+ // do not perform any actions.
+ void registerListener(const int32_t atomId, wp<StateListener> listener);
+
+ // Notifies the correct StateTracker to unregister a listener
+ // and removes the tracker if it no longer has any listeners.
+ void unregisterListener(const int32_t atomId, wp<StateListener> listener);
+
+ // Returns true if the StateTracker exists and queries for the
+ // original state value mapped to the given query key. The state value is
+ // stored and output in a FieldValue class.
+ // Returns false if the StateTracker doesn't exist.
+ 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();
+ }
+
+ inline int getListenersCount(const int32_t atomId) const {
+ auto it = mStateTrackers.find(atomId);
+ if (it != mStateTrackers.end()) {
+ return it->second->getListenersCount();
+ }
+ return -1;
+ }
+
+private:
+ mutable std::mutex mMutex;
+
+ // 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
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
new file mode 100644
index 0000000..41e525c
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "stats_util.h"
+
+#include "StateTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
+}
+
+void StateTracker::onLogEvent(const LogEvent& event) {
+ const int64_t eventTimeNs = event.GetElapsedTimestampNs();
+
+ // Parse event for primary field values i.e. primary key.
+ HashableDimensionKey primaryKey;
+ filterPrimaryKey(event.getValues(), &primaryKey);
+
+ FieldValue newState;
+ if (!getStateFieldValueFromLogEvent(event, &newState)) {
+ ALOGE("StateTracker error extracting state from log event. Missing exclusive state field.");
+ clearStateForPrimaryKey(eventTimeNs, primaryKey);
+ return;
+ }
+
+ mField.setField(newState.mField.getField());
+
+ if (newState.mValue.getType() != INT) {
+ ALOGE("StateTracker error extracting state from log event. Type: %d",
+ newState.mValue.getType());
+ clearStateForPrimaryKey(eventTimeNs, primaryKey);
+ return;
+ }
+
+ if (int resetState = event.getResetState(); resetState != -1) {
+ VLOG("StateTracker new reset state: %d", resetState);
+ const FieldValue resetStateFieldValue(mField, Value(resetState));
+ handleReset(eventTimeNs, resetStateFieldValue);
+ return;
+ }
+
+ const bool nested = newState.mAnnotations.isNested();
+ StateValueInfo* stateValueInfo = &mStateMap[primaryKey];
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, stateValueInfo);
+}
+
+void StateTracker::registerListener(wp<StateListener> listener) {
+ mListeners.insert(listener);
+}
+
+void StateTracker::unregisterListener(wp<StateListener> listener) {
+ mListeners.erase(listener);
+}
+
+bool StateTracker::getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const {
+ output->mField = mField;
+
+ if (const auto it = mStateMap.find(queryKey); it != mStateMap.end()) {
+ output->mValue = it->second.state;
+ return true;
+ }
+
+ // Set the state value to kStateUnknown if query key is not found in state map.
+ output->mValue = kStateUnknown;
+ return false;
+}
+
+void StateTracker::handleReset(const int64_t eventTimeNs, const FieldValue& newState) {
+ VLOG("StateTracker handle reset");
+ for (auto& [primaryKey, stateValueInfo] : mStateMap) {
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, newState,
+ false /* nested; treat this state change as not nested */,
+ &stateValueInfo);
+ }
+}
+
+void StateTracker::clearStateForPrimaryKey(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey) {
+ VLOG("StateTracker clear state for primary key");
+ const std::unordered_map<HashableDimensionKey, StateValueInfo>::iterator it =
+ mStateMap.find(primaryKey);
+
+ // If there is no entry for the primaryKey in mStateMap, then the state is already
+ // kStateUnknown.
+ const FieldValue state(mField, Value(kStateUnknown));
+ if (it != mStateMap.end()) {
+ updateStateForPrimaryKey(eventTimeNs, primaryKey, state,
+ false /* nested; treat this state change as not nested */,
+ &it->second);
+ }
+}
+
+void StateTracker::updateStateForPrimaryKey(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& newState, const bool nested,
+ StateValueInfo* stateValueInfo) {
+ FieldValue oldState;
+ oldState.mField = mField;
+ oldState.mValue.setInt(stateValueInfo->state);
+ const int32_t oldStateValue = stateValueInfo->state;
+ const int32_t newStateValue = newState.mValue.int_value;
+
+ if (kStateUnknown == newStateValue) {
+ mStateMap.erase(primaryKey);
+ }
+
+ // Update state map for non-nested counting case.
+ // Every state event triggers a state overwrite.
+ if (!nested) {
+ stateValueInfo->state = newStateValue;
+ stateValueInfo->count = 1;
+
+ // Notify listeners if state has changed.
+ if (oldStateValue != newStateValue) {
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ }
+ return;
+ }
+
+ // Update state map for nested counting case.
+ //
+ // Nested counting is only allowed for binary state events such as ON/OFF or
+ // ACQUIRE/RELEASE. For example, WakelockStateChanged might have the state
+ // events: ON, ON, OFF. The state will still be ON until we see the same
+ // number of OFF events as ON events.
+ //
+ // In atoms.proto, a state atom with nested counting enabled
+ // must only have 2 states. There is no enforcemnt here of this requirement.
+ // The atom must be logged correctly.
+ if (kStateUnknown == newStateValue) {
+ if (kStateUnknown != oldStateValue) {
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ }
+ } else if (oldStateValue == kStateUnknown) {
+ stateValueInfo->state = newStateValue;
+ stateValueInfo->count = 1;
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ } else if (oldStateValue == newStateValue) {
+ stateValueInfo->count++;
+ } else if (--stateValueInfo->count == 0) {
+ stateValueInfo->state = newStateValue;
+ stateValueInfo->count = 1;
+ notifyListeners(eventTimeNs, primaryKey, oldState, newState);
+ }
+}
+
+void StateTracker::notifyListeners(const int64_t eventTimeNs,
+ const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState) {
+ for (auto l : mListeners) {
+ auto sl = l.promote();
+ if (sl != nullptr) {
+ sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
+ }
+ }
+}
+
+bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output) {
+ const int exclusiveStateFieldIndex = event.getExclusiveStateFieldIndex();
+ if (-1 == exclusiveStateFieldIndex) {
+ ALOGE("error extracting state from log event. Missing exclusive state field.");
+ return false;
+ }
+
+ *output = event.getValues()[exclusiveStateFieldIndex];
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
new file mode 100644
index 0000000..abd579e
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <utils/RefBase.h>
+#include "HashableDimensionKey.h"
+#include "logd/LogEvent.h"
+
+#include "state/StateListener.h"
+
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateTracker : public virtual RefBase {
+public:
+ StateTracker(const int32_t atomId);
+
+ virtual ~StateTracker(){};
+
+ // Updates state map and notifies all listeners if a state change occurs.
+ // Checks if a state change has occurred by getting the state value from
+ // the log event and comparing the old and new states.
+ void onLogEvent(const LogEvent& event);
+
+ // Adds new listeners to set of StateListeners. If a listener is already
+ // registered, it is ignored.
+ void registerListener(wp<StateListener> listener);
+
+ void unregisterListener(wp<StateListener> listener);
+
+ // The output is a FieldValue object that has mStateField as the field and
+ // the original state value (found using the given query key) as the value.
+ //
+ // If the key isn't mapped to a state or the key size doesn't match the
+ // number of primary fields, the output value is set to kStateUnknown.
+ bool getStateValue(const HashableDimensionKey& queryKey, FieldValue* output) const;
+
+ inline int getListenersCount() const {
+ return mListeners.size();
+ }
+
+ const static int kStateUnknown = -1;
+
+private:
+ struct StateValueInfo {
+ int32_t state = kStateUnknown; // state value
+ int count = 0; // nested count (only used for binary states)
+ };
+
+ Field mField;
+
+ // Maps primary key to state value info
+ std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
+
+ // Set of all StateListeners (objects listening for state changes)
+ std::set<wp<StateListener>> mListeners;
+
+ // Reset all state values in map to the given state.
+ void handleReset(const int64_t eventTimeNs, const FieldValue& newState);
+
+ // Clears the state value mapped to the given primary key by setting it to kStateUnknown.
+ void clearStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey);
+
+ // Update the StateMap based on the received state value.
+ void updateStateForPrimaryKey(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
+ const FieldValue& newState, const bool nested,
+ StateValueInfo* stateValueInfo);
+
+ // Notify registered state listeners of state change.
+ void notifyListeners(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey,
+ const FieldValue& oldState, const FieldValue& newState);
+};
+
+bool getStateFieldValueFromLogEvent(const LogEvent& event, FieldValue* output);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index b875470..ddd2725 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -41,6 +41,15 @@
repeated DimensionsValue dimensions_value = 1;
}
+message StateValue {
+ optional int32 atom_id = 1;
+
+ oneof contents {
+ int64 group_id = 2;
+ int32 value = 3;
+ }
+}
+
message EventMetricData {
optional int64 elapsed_timestamp_nanos = 1;
@@ -66,13 +75,15 @@
message CountMetricData {
optional DimensionsValue dimensions_in_what = 1;
- optional DimensionsValue dimensions_in_condition = 2;
+ repeated StateValue slice_by_state = 6;
repeated CountBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
- repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+ optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
+ repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
}
message DurationBucketInfo {
@@ -92,13 +103,15 @@
message DurationMetricData {
optional DimensionsValue dimensions_in_what = 1;
- optional DimensionsValue dimensions_in_condition = 2;
+ repeated StateValue slice_by_state = 6;
repeated DurationBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
- repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+ optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
+ repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
}
message ValueBucketInfo {
@@ -136,13 +149,15 @@
message ValueMetricData {
optional DimensionsValue dimensions_in_what = 1;
- optional DimensionsValue dimensions_in_condition = 2;
+ repeated StateValue slice_by_state = 6;
repeated ValueBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
- repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+ optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
+ repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
}
message GaugeBucketInfo {
@@ -166,13 +181,16 @@
message GaugeMetricData {
optional DimensionsValue dimensions_in_what = 1;
- optional DimensionsValue dimensions_in_condition = 2;
+ // Currently unsupported
+ repeated StateValue slice_by_state = 6;
repeated GaugeBucketInfo bucket_info = 3;
repeated DimensionsValue dimension_leaf_values_in_what = 4;
- repeated DimensionsValue dimension_leaf_values_in_condition = 5;
+ optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
+ repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
}
message StatsLogReport {
@@ -180,11 +198,42 @@
// Fields 2 and 3 are reserved.
+ // Keep this in sync with BucketDropReason enum in MetricProducer.h.
+ enum BucketDropReason {
+ // For ValueMetric, a bucket is dropped during a dump report request iff
+ // current bucket should be included, a pull is needed (pulled metric and
+ // condition is true), and we are under fast time constraints.
+ DUMP_REPORT_REQUESTED = 1;
+ EVENT_IN_WRONG_BUCKET = 2;
+ CONDITION_UNKNOWN = 3;
+ PULL_FAILED = 4;
+ PULL_DELAYED = 5;
+ DIMENSION_GUARDRAIL_REACHED = 6;
+ MULTIPLE_BUCKETS_SKIPPED = 7;
+ // Not an invalid bucket case, but the bucket is dropped.
+ BUCKET_TOO_SMALL = 8;
+ // Not an invalid bucket case, but the bucket is skipped.
+ NO_DATA = 9;
+ };
+
+ message DropEvent {
+ optional BucketDropReason drop_reason = 1;
+
+ optional int64 drop_time_millis = 2;
+ }
+
message SkippedBuckets {
optional int64 start_bucket_elapsed_nanos = 1;
+
optional int64 end_bucket_elapsed_nanos = 2;
+
optional int64 start_bucket_elapsed_millis = 3;
+
optional int64 end_bucket_elapsed_millis = 4;
+
+ // The number of drop events is capped by StatsdStats::kMaxLoggedBucketDropEvents.
+ // The current maximum is 10 drop events.
+ repeated DropEvent drop_event = 5;
}
message EventMetricDataWrapper {
@@ -220,7 +269,7 @@
optional DimensionsValue dimensions_path_in_what = 11;
- optional DimensionsValue dimensions_path_in_condition = 12;
+ optional DimensionsValue dimensions_path_in_condition = 12 [deprecated = true];
// DO NOT USE field 13.
@@ -363,7 +412,7 @@
repeated ConditionStats condition_stats = 14;
repeated MetricStats metric_stats = 15;
repeated AlertStats alert_stats = 16;
- repeated MetricStats metric_dimension_in_condition_stats = 17;
+ repeated MetricStats metric_dimension_in_condition_stats = 17 [deprecated = true];
message Annotation {
optional int64 field_int64 = 1;
optional int32 field_int32 = 2;
@@ -378,6 +427,7 @@
message AtomStats {
optional int32 tag = 1;
optional int32 count = 2;
+ optional int32 error_count = 3;
}
repeated AtomStats atom_stats = 7;
@@ -408,11 +458,20 @@
optional int64 pull_timeout = 10;
optional int64 pull_exceed_max_delay = 11;
optional int64 pull_failed = 12;
- optional int64 stats_companion_pull_failed = 13;
- optional int64 stats_companion_pull_binder_transaction_failed = 14;
+ optional int64 stats_companion_pull_failed = 13 [deprecated = true];
+ optional int64 stats_companion_pull_binder_transaction_failed = 14 [deprecated = true];
optional int64 empty_data = 15;
optional int64 registered_count = 16;
optional int64 unregistered_count = 17;
+ optional int32 atom_error_count = 18;
+ 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;
@@ -483,7 +542,7 @@
message MetricValue {
optional int64 metric_id = 1;
optional DimensionsValue dimension_in_what = 2;
- optional DimensionsValue dimension_in_condition = 3;
+ optional DimensionsValue dimension_in_condition = 3 [deprecated = true];
optional int64 value = 4;
}
oneof value {
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a7b6810..423bae8 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -17,11 +17,13 @@
#include "hash.h"
#include "stats_log_util.h"
+#include <aidl/android/os/IStatsCompanionService.h>
#include <private/android_filesystem_config.h>
#include <set>
#include <utils/SystemClock.h>
-using android::util::AtomsInfo;
+#include "statscompanion_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FIXED64;
@@ -33,6 +35,10 @@
using android::util::FIELD_TYPE_UINT64;
using android::util::ProtoOutputStream;
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+using std::string;
+
namespace android {
namespace os {
namespace statsd {
@@ -49,6 +55,11 @@
const int DIMENSIONS_VALUE_TUPLE_VALUE = 1;
+// for StateValue Proto
+const int STATE_VALUE_ATOM_ID = 1;
+const int STATE_VALUE_CONTENTS_GROUP_ID = 2;
+const int STATE_VALUE_CONTENTS_VALUE = 3;
+
// for PulledAtomStats proto
const int FIELD_ID_PULLED_ATOM_STATS = 10;
const int FIELD_ID_PULL_ATOM_ID = 1;
@@ -63,11 +74,17 @@
const int FIELD_ID_PULL_TIMEOUT = 10;
const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11;
const int FIELD_ID_PULL_FAILED = 12;
-const int FIELD_ID_STATS_COMPANION_FAILED = 13;
-const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14;
const int FIELD_ID_EMPTY_DATA = 15;
const int FIELD_ID_PULL_REGISTERED_COUNT = 16;
const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17;
+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;
const int FIELD_ID_METRIC_ID = 1;
@@ -345,30 +362,7 @@
protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value);
break;
case STRING: {
- bool isBytesField = false;
- // Bytes field is logged via string format in log_msg format. So here we check
- // if this string field is a byte field.
- std::map<int, std::vector<int>>::const_iterator itr;
- if (depth == 0 && (itr = AtomsInfo::kBytesFieldAtoms.find(tagId)) !=
- AtomsInfo::kBytesFieldAtoms.end()) {
- const std::vector<int>& bytesFields = itr->second;
- for (int bytesField : bytesFields) {
- if (bytesField == fieldNum) {
- // This is a bytes field
- isBytesField = true;
- break;
- }
- }
- }
- if (isBytesField) {
- if (dim.mValue.str_value.length() > 0) {
- protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
- (const char*)dim.mValue.str_value.c_str(),
- dim.mValue.str_value.length());
- }
- } else {
- protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
- }
+ protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
break;
}
case STORAGE:
@@ -412,6 +406,23 @@
protoOutput->end(atomToken);
}
+void writeStateToProto(const FieldValue& state, util::ProtoOutputStream* protoOutput) {
+ protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_ATOM_ID, state.mField.getTag());
+
+ switch (state.mValue.getType()) {
+ case INT:
+ protoOutput->write(FIELD_TYPE_INT32 | STATE_VALUE_CONTENTS_VALUE,
+ state.mValue.int_value);
+ break;
+ case LONG:
+ protoOutput->write(FIELD_TYPE_INT64 | STATE_VALUE_CONTENTS_GROUP_ID,
+ state.mValue.long_value);
+ break;
+ default:
+ break;
+ }
+}
+
int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL &&
@@ -441,6 +452,8 @@
return 12 * 60 * 60 * 1000LL;
case ONE_DAY:
return 24 * 60 * 60 * 1000LL;
+ case ONE_WEEK:
+ return 7 * 24 * 60 * 60 * 1000LL;
case CTS:
return 1000;
case TIME_UNIT_UNSPECIFIED:
@@ -474,16 +487,29 @@
(long long)pair.second.pullExceedMaxDelay);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED,
(long long)pair.second.pullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_FAILED,
- (long long)pair.second.statsCompanionPullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED,
- (long long)pair.second.statsCompanionPullBinderTransactionFailed);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA,
(long long)pair.second.emptyData);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT,
(long long) pair.second.registeredCount);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
(long long) pair.second.unregisteredCount);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BINDER_CALL_FAIL_COUNT,
+ (long long)pair.second.binderCallFailCount);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND,
+ (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);
}
@@ -529,6 +555,10 @@
return ::android::elapsedRealtime();
}
+int64_t getSystemUptimeMillis() {
+ return ::android::uptimeMillis();
+}
+
int64_t getWallClockNs() {
return time(nullptr) * NS_PER_SEC;
}
@@ -541,14 +571,13 @@
return time(nullptr) * MS_PER_SEC;
}
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs) {
- if (AtomsInfo::kTruncatingTimestampAtomBlackList.find(atomId) !=
- AtomsInfo::kTruncatingTimestampAtomBlackList.end() ||
- (atomId >= StatsdStats::kTimestampTruncationStartTag &&
- atomId <= StatsdStats::kTimestampTruncationEndTag)) {
- return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
+int64_t truncateTimestampIfNecessary(const LogEvent& event) {
+ if (event.shouldTruncateTimestamp() ||
+ (event.GetTagId() >= StatsdStats::kTimestampTruncationStartTag &&
+ event.GetTagId() <= StatsdStats::kTimestampTruncationEndTag)) {
+ return event.GetElapsedTimestampNs() / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
} else {
- return timestampNs;
+ return event.GetElapsedTimestampNs();
}
}
@@ -560,6 +589,21 @@
return millis * 1000000;
}
+bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) {
+ shared_ptr<IStatsCompanionService> scs = getStatsCompanionService();
+ if (scs == nullptr) {
+ return false;
+ }
+
+ bool success;
+ ::ndk::ScopedAStatus status = scs->checkPermission(string(permission), pid, uid, &success);
+ if (!status.isOk()) {
+ return false;
+ }
+
+ return success;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index a28165d..eb65dc6 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -17,28 +17,33 @@
#pragma once
#include <android/util/ProtoOutputStream.h>
+
#include "FieldValue.h"
#include "HashableDimensionKey.h"
-#include "atoms_info.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+
+using android::util::ProtoOutputStream;
namespace android {
namespace os {
namespace statsd {
void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension,
const int dimensionLeafFieldId,
std::set<string> *str_set,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
+
+void writeStateToProto(const FieldValue& state, ProtoOutputStream* protoOutput);
// Convert the TimeUnit enum to the bucket size in millis with a guardrail on
// bucket size.
@@ -56,6 +61,9 @@
// 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();
@@ -71,14 +79,14 @@
// Helper function to write PulledAtomStats to ProtoOutputStream
void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
- util::ProtoOutputStream* protoOutput);
+ ProtoOutputStream* protoOutput);
// Helper function to write AtomMetricStats to ProtoOutputStream
void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetricStats> &pair,
- util::ProtoOutputStream *protoOutput);
+ ProtoOutputStream *protoOutput);
template<class T>
-bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) {
+bool parseProtoOutputStream(ProtoOutputStream& protoOutput, T* message) {
std::string pbBytes;
sp<android::util::ProtoReader> reader = protoOutput.data();
while (reader->readBuffer() != NULL) {
@@ -89,14 +97,21 @@
return message->ParseFromArray(pbBytes.c_str(), pbBytes.size());
}
-// Checks the blacklist of atoms as well as the blacklisted range of 300,000 - 304,999.
+// Checks the truncate timestamp annotation as well as the blacklisted range of 300,000 - 304,999.
// Returns the truncated timestamp to the nearest 5 minutes if needed.
-int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs);
+int64_t truncateTimestampIfNecessary(const LogEvent& event);
+
+// Checks permission for given pid and uid.
+bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid);
inline bool isVendorPulledAtom(int atomId) {
return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag;
}
+inline bool isPulledAtom(int atomId) {
+ return atomId >= StatsdStats::kPullAtomStartTag && atomId < StatsdStats::kVendorAtomStartTag;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/statscompanion_util.cpp b/cmds/statsd/src/statscompanion_util.cpp
index d338827..ce07ec0 100644
--- a/cmds/statsd/src/statscompanion_util.cpp
+++ b/cmds/statsd/src/statscompanion_util.cpp
@@ -18,26 +18,16 @@
#include "Log.h"
#include "statscompanion_util.h"
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
namespace android {
namespace os {
namespace statsd {
-sp <IStatsCompanionService> getStatsCompanionService() {
- sp<IStatsCompanionService> statsCompanion = nullptr;
- // Get statscompanion service from service manager
- static const sp <IServiceManager> sm(defaultServiceManager());
- if (statsCompanion == nullptr) {
- if (sm != nullptr) {
- const String16 name("statscompanion");
- statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
- if (statsCompanion == nullptr) {
- ALOGW("statscompanion service unavailable!");
- return nullptr;
- }
- }
- }
- return statsCompanion;
+shared_ptr<IStatsCompanionService> getStatsCompanionService() {
+ ::ndk::SpAIBinder binder(AServiceManager_getService("statscompanion"));
+ return IStatsCompanionService::fromBinder(binder);
}
} // namespace statsd
diff --git a/cmds/statsd/src/statscompanion_util.h b/cmds/statsd/src/statscompanion_util.h
index dc4f283..e20c40b 100644
--- a/cmds/statsd/src/statscompanion_util.h
+++ b/cmds/statsd/src/statscompanion_util.h
@@ -16,14 +16,17 @@
#pragma once
-#include "StatsLogProcessor.h"
+#include <aidl/android/os/IStatsCompanionService.h>
+
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
/** Fetches and returns the StatsCompanionService. */
-sp<IStatsCompanionService> getStatsCompanionService();
+shared_ptr<IStatsCompanionService> getStatsCompanionService();
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 79c06b9..acdffd3 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -35,7 +35,7 @@
enum TimeUnit {
TIME_UNIT_UNSPECIFIED = 0;
- ONE_MINUTE = 1;
+ ONE_MINUTE = 1; // WILL BE GUARDRAILED TO 5 MINS UNLESS UID = SHELL OR ROOT
FIVE_MINUTES = 2;
TEN_MINUTES = 3;
THIRTY_MINUTES = 4;
@@ -44,6 +44,7 @@
SIX_HOURS = 7;
TWELVE_HOURS = 8;
ONE_DAY = 9;
+ ONE_WEEK = 10;
CTS = 1000;
}
@@ -130,7 +131,7 @@
UNKNOWN = 0;
FALSE = 1;
}
- optional InitialValue initial_value = 5 [default = FALSE];
+ optional InitialValue initial_value = 5 [default = UNKNOWN];
optional FieldMatcher dimensions = 6;
}
@@ -150,6 +151,24 @@
}
}
+message StateMap {
+ message StateGroup {
+ optional int64 group_id = 1;
+
+ repeated int32 value = 2;
+ }
+
+ repeated StateGroup group = 1;
+}
+
+message State {
+ optional int64 id = 1;
+
+ optional int32 atom_id = 2;
+
+ optional StateMap map = 3;
+}
+
message MetricConditionLink {
optional int64 condition = 1;
@@ -158,6 +177,14 @@
optional FieldMatcher fields_in_condition = 3;
}
+message MetricStateLink {
+ optional int32 state_atom_id = 1;
+
+ optional FieldMatcher fields_in_what = 2;
+
+ optional FieldMatcher fields_in_state = 3;
+}
+
message FieldFilter {
optional bool include_all = 1 [default = false];
optional FieldMatcher fields = 2;
@@ -171,6 +198,9 @@
optional int64 condition = 3;
repeated MetricConditionLink links = 4;
+
+ reserved 100;
+ reserved 101;
}
message CountMetric {
@@ -182,11 +212,18 @@
optional FieldMatcher dimensions_in_what = 4;
- optional FieldMatcher dimensions_in_condition = 7;
+ repeated int64 slice_by_state = 8;
optional TimeUnit bucket = 5;
repeated MetricConditionLink links = 6;
+
+ repeated MetricStateLink state_link = 9;
+
+ optional FieldMatcher dimensions_in_condition = 7 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message DurationMetric {
@@ -196,8 +233,12 @@
optional int64 condition = 3;
+ repeated int64 slice_by_state = 9;
+
repeated MetricConditionLink links = 4;
+ repeated MetricStateLink state_link = 10;
+
enum AggregationType {
SUM = 1;
@@ -207,9 +248,12 @@
optional FieldMatcher dimensions_in_what = 6;
- optional FieldMatcher dimensions_in_condition = 8;
-
optional TimeUnit bucket = 7;
+
+ optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message GaugeMetric {
@@ -225,7 +269,7 @@
optional FieldMatcher dimensions_in_what = 5;
- optional FieldMatcher dimensions_in_condition = 8;
+ optional FieldMatcher dimensions_in_condition = 8 [deprecated = true];
optional TimeUnit bucket = 6;
@@ -243,9 +287,12 @@
optional int64 max_num_gauge_atoms_per_bucket = 11 [default = 10];
- optional int32 max_pull_delay_sec = 13 [default = 10];
+ 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 {
@@ -259,12 +306,14 @@
optional FieldMatcher dimensions_in_what = 5;
- optional FieldMatcher dimensions_in_condition = 9;
+ repeated int64 slice_by_state = 18;
optional TimeUnit bucket = 6;
repeated MetricConditionLink links = 7;
+ repeated MetricStateLink state_link = 19;
+
enum AggregationType {
SUM = 1;
MIN = 2;
@@ -291,9 +340,14 @@
optional bool skip_zero_diff_output = 14 [default = true];
- optional int32 max_pull_delay_sec = 16 [default = 10];
+ optional int32 max_pull_delay_sec = 16 [default = 30];
optional bool split_bucket_for_app_upgrade = 17 [default = true];
+
+ optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
+
+ reserved 100;
+ reserved 101;
}
message Alert {
@@ -393,6 +447,12 @@
repeated EventActivation event_activation = 2;
}
+message PullAtomPackages {
+ optional int32 atom_id = 1;
+
+ repeated string packages = 2;
+}
+
message StatsdConfig {
optional int64 id = 1;
@@ -438,6 +498,14 @@
optional bool persist_locally = 20 [default = false];
+ repeated State state = 21;
+
+ repeated string default_pull_packages = 22;
+
+ repeated PullAtomPackages pull_atom_packages = 23;
+
+ repeated int32 whitelisted_atom_ids = 24;
+
// Field number 1000 is reserved for later use.
reserved 1000;
}
diff --git a/cmds/statsd/src/statsd_metadata.proto b/cmds/statsd/src/statsd_metadata.proto
new file mode 100644
index 0000000..200b392
--- /dev/null
+++ b/cmds/statsd/src/statsd_metadata.proto
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd.metadata;
+
+message ConfigKey {
+ optional int64 config_id = 1;
+ optional int32 uid = 2;
+}
+
+message Field {
+ optional int32 tag = 1;
+ optional int32 field = 2;
+}
+
+message FieldValue {
+ optional Field field = 1;
+ oneof value {
+ int32 value_int = 2;
+ int64 value_long = 3;
+ float value_float = 4;
+ double value_double = 5;
+ string value_str = 6;
+ bytes value_storage = 7;
+ }
+}
+
+message MetricDimensionKey {
+ repeated FieldValue dimension_key_in_what = 1;
+ repeated FieldValue state_values_key = 2;
+}
+
+message AlertDimensionKeyedData {
+ // The earliest time the alert can be fired again in wall clock time.
+ optional int32 last_refractory_ends_sec = 1;
+ optional MetricDimensionKey dimension_key = 2;
+}
+
+message AlertMetadata {
+ optional int64 alert_id = 1;
+ repeated AlertDimensionKeyedData alert_dim_keyed_data = 2;
+}
+
+// All metadata for a config in statsd
+message StatsMetadata {
+ optional ConfigKey config_key = 1;
+ repeated AlertMetadata alert_metadata = 2;
+}
+
+message StatsMetadataList {
+ repeated StatsMetadata stats_metadata = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 507297c..dcfdfe3 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -44,7 +44,7 @@
#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
// Magic word at the start of the train info file, change this if changing the file format
-const uint32_t TRAIN_INFO_FILE_MAGIC = 0xff7447ff;
+const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf;
// for ConfigMetricsReportList
const int FIELD_ID_REPORTS = 2;
@@ -75,6 +75,29 @@
(long long)id);
}
+static string findTrainInfoFileNameLocked(const string& trainName) {
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+ if (dir == NULL) {
+ VLOG("Path %s does not exist", TRAIN_INFO_DIR);
+ return "";
+ }
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* fileName = de->d_name;
+ if (fileName[0] == '.') continue;
+
+ size_t fileNameLength = strlen(fileName);
+ if (fileNameLength >= trainName.length()) {
+ if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(),
+ trainName.length())) {
+ return string(fileName);
+ }
+ }
+ }
+
+ return "";
+}
+
// Returns array of int64_t which contains timestamp in seconds, uid,
// configID and whether the file is a local history file.
static void parseFileName(char* name, FileName* output) {
@@ -123,20 +146,25 @@
close(fd);
}
-bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
- int32_t status, const std::vector<int64_t>& experimentIds) {
+bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) {
std::lock_guard<std::mutex> lock(sTrainInfoMutex);
- deleteAllFiles(TRAIN_INFO_DIR);
+ if (trainInfo.trainName.empty()) {
+ return false;
+ }
+ deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str());
- int fd = open(TRAIN_INFO_PATH, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+ std::string fileName =
+ StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(),
+ trainInfo.trainName.c_str());
+
+ int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
if (fd == -1) {
- VLOG("Attempt to access %s but failed", TRAIN_INFO_PATH);
+ VLOG("Attempt to access %s but failed", fileName.c_str());
return false;
}
size_t result;
-
// Write the magic word
result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
@@ -146,8 +174,8 @@
}
// Write the train version
- const size_t trainVersionCodeByteCount = sizeof(trainVersionCode);
- result = write(fd, &trainVersionCode, trainVersionCodeByteCount);
+ const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode);
+ result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
if (result != trainVersionCodeByteCount) {
VLOG("Failed to wrtie train version code");
close(fd);
@@ -155,7 +183,7 @@
}
// Write # of bytes in trainName to file
- const size_t trainNameSize = trainName.size();
+ const size_t trainNameSize = trainInfo.trainName.size();
const size_t trainNameSizeByteCount = sizeof(trainNameSize);
result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
if (result != trainNameSizeByteCount) {
@@ -165,7 +193,7 @@
}
// Write trainName to file
- result = write(fd, trainName.c_str(), trainNameSize);
+ result = write(fd, trainInfo.trainName.c_str(), trainNameSize);
if (result != trainNameSize) {
VLOG("Failed to write train name");
close(fd);
@@ -173,8 +201,8 @@
}
// Write status to file
- const size_t statusByteCount = sizeof(status);
- result = write(fd, (uint8_t*)&status, statusByteCount);
+ const size_t statusByteCount = sizeof(trainInfo.status);
+ result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount);
if (result != statusByteCount) {
VLOG("Failed to write status");
close(fd);
@@ -182,7 +210,7 @@
}
// Write experiment id count to file.
- const size_t experimentIdsCount = experimentIds.size();
+ const size_t experimentIdsCount = trainInfo.experimentIds.size();
const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
if (result != experimentIdsCountByteCount) {
@@ -193,7 +221,7 @@
// Write experimentIds to file
for (size_t i = 0; i < experimentIdsCount; i++) {
- const int64_t experimentId = experimentIds[i];
+ const int64_t experimentId = trainInfo.experimentIds[i];
const size_t experimentIdByteCount = sizeof(experimentId);
result = write(fd, &experimentId, experimentIdByteCount);
if (result == experimentIdByteCount) {
@@ -205,23 +233,47 @@
}
}
- result = fchown(fd, AID_STATSD, AID_STATSD);
- if (result) {
- VLOG("Failed to chown train info file to statsd");
- close(fd);
- return false;
+ // Write bools to file
+ const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
+ result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to write requires staging");
+ close(fd);
+ return false;
+ }
+
+ result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to write rollback enabled");
+ close(fd);
+ return false;
+ }
+
+ result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to write requires log latency monitor");
+ close(fd);
+ return false;
}
close(fd);
return true;
}
-bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
+bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) {
std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+ return readTrainInfoLocked(trainName, trainInfo);
+}
- int fd = open(TRAIN_INFO_PATH, O_RDONLY | O_CLOEXEC);
+bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) {
+ trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true);
+ string fileName = findTrainInfoFileNameLocked(trainName);
+ if (fileName.empty()) {
+ return false;
+ }
+ int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName.c_str()).c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1) {
- VLOG("Failed to open train-info.bin");
+ VLOG("Failed to open %s", fileName.c_str());
return false;
}
@@ -297,6 +349,29 @@
trainInfo.experimentIds.push_back(experimentId);
}
+ // Read bools
+ const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
+ result = read(fd, &trainInfo.requiresStaging, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to read requires requires staging from train info file");
+ close(fd);
+ return false;
+ }
+
+ result = read(fd, &trainInfo.rollbackEnabled, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to read requires rollback enabled from train info file");
+ close(fd);
+ return false;
+ }
+
+ result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount);
+ if (result != boolByteCount) {
+ VLOG("Failed to read requires requires low latency monitor from train info file");
+ close(fd);
+ return false;
+ }
+
// Expect to be at EOF.
char c;
result = read(fd, &c, 1);
@@ -311,6 +386,32 @@
return true;
}
+vector<InstallTrainInfo> StorageManager::readAllTrainInfo() {
+ std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+ vector<InstallTrainInfo> trainInfoList;
+ unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+ if (dir == NULL) {
+ VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
+ return trainInfoList;
+ }
+
+ dirent* de;
+ while ((de = readdir(dir.get()))) {
+ char* name = de->d_name;
+ if (name[0] == '.') {
+ continue;
+ }
+
+ InstallTrainInfo trainInfo;
+ bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo);
+ if (!readSuccess) {
+ continue;
+ }
+ trainInfoList.push_back(trainInfo);
+ }
+ return trainInfoList;
+}
+
void StorageManager::deleteFile(const char* file) {
if (remove(file) != 0) {
VLOG("Attempt to delete %s but is not found", file);
@@ -574,7 +675,7 @@
});
}
-void StorageManager::trimToFit(const char* path) {
+void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", path);
@@ -589,9 +690,16 @@
if (name[0] == '.') continue;
FileName output;
- parseFileName(name, &output);
+ string file_name;
+ if (parseTimestampOnly) {
+ file_name = StringPrintf("%s/%s", path, name);
+ output.mTimestampSec = StrToInt64(strtok(name, "_"));
+ output.mIsHistory = false;
+ } else {
+ parseFileName(name, &output);
+ file_name = output.getFullFileName(path);
+ }
if (output.mTimestampSec == -1) continue;
- string file_name = output.getFullFileName(path);
// Check for timestamp and delete if it's too old.
long fileAge = nowSec - output.mTimestampSec;
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 69b41c2..d59046d 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -52,13 +52,22 @@
/**
* Writes train info.
*/
- static bool writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
- int32_t status, const std::vector<int64_t>& experimentIds);
+ static bool writeTrainInfo(const InstallTrainInfo& trainInfo);
/**
* Reads train info.
*/
- static bool readTrainInfo(InstallTrainInfo& trainInfo);
+ static bool readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo);
+
+ /**
+ * Reads train info assuming lock is obtained.
+ */
+ static bool readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo);
+
+ /**
+ * Reads all train info and returns a vector of train info.
+ */
+ static vector<InstallTrainInfo> readAllTrainInfo();
/**
* Reads the file content to the buffer.
@@ -124,7 +133,7 @@
* Trims files in the provided directory to limit the total size, number of
* files, accumulation of outdated files.
*/
- static void trimToFit(const char* dir);
+ static void trimToFit(const char* dir, bool parseTimestampOnly = false);
/**
* Returns true if there already exists identical configuration on device.
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index ba5e667..1d77513 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -21,10 +21,8 @@
#include "packages/UidMap.h"
#include "stats_log_util.h"
-#include <android/os/IIncidentManager.h>
-#include <android/os/IncidentReportArgs.h>
#include <android/util/ProtoOutputStream.h>
-#include <binder/IServiceManager.h>
+#include <incident/incident_report.h>
#include <vector>
@@ -51,7 +49,6 @@
const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1;
const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1;
const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2;
-const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3;
const int FIELD_ID_METRIC_VALUE_VALUE = 4;
const int FIELD_ID_PACKAGE_INFO = 3;
@@ -83,10 +80,8 @@
writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto);
headerProto.end(dimToken);
+ // deprecated field
// optional DimensionsValue dimension_in_condition = 3;
- dimToken = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION);
- writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), nullptr, &headerProto);
- headerProto.end(dimToken);
// optional int64 value = 4;
headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue);
@@ -105,13 +100,6 @@
}
}
- for (const auto& dim : dimensionKey.getDimensionKeyInCondition().getValues()) {
- int uid = getUidIfExists(dim);
- if (uid > 2000) {
- uids.insert(uid);
- }
- }
-
if (!uids.empty()) {
uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO);
UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids,
@@ -142,44 +130,38 @@
return false;
}
- IncidentReportArgs incidentReport;
+ AIncidentReportArgs* args = AIncidentReportArgs_init();
vector<uint8_t> protoData;
getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey,
config.alert_description(), &protoData);
- incidentReport.addHeader(protoData);
+ AIncidentReportArgs_addHeader(args, protoData.data(), protoData.size());
for (int i = 0; i < config.section_size(); i++) {
- incidentReport.addSection(config.section(i));
+ AIncidentReportArgs_addSection(args, config.section(i));
}
uint8_t dest;
switch (config.dest()) {
case IncidentdDetails_Destination_AUTOMATIC:
- dest = android::os::PRIVACY_POLICY_AUTOMATIC;
+ dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC;
break;
case IncidentdDetails_Destination_EXPLICIT:
- dest = android::os::PRIVACY_POLICY_EXPLICIT;
+ dest = INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT;
break;
default:
- dest = android::os::PRIVACY_POLICY_AUTOMATIC;
+ dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC;
}
- incidentReport.setPrivacyPolicy(dest);
+ AIncidentReportArgs_setPrivacyPolicy(args, dest);
- incidentReport.setReceiverPkg(config.receiver_pkg());
+ AIncidentReportArgs_setReceiverPackage(args, config.receiver_pkg().c_str());
- incidentReport.setReceiverCls(config.receiver_cls());
+ AIncidentReportArgs_setReceiverClass(args, config.receiver_cls().c_str());
- sp<IIncidentManager> service = interface_cast<IIncidentManager>(
- defaultServiceManager()->getService(android::String16("incident")));
- if (service == nullptr) {
- ALOGW("Failed to fetch incident service.");
- return false;
- }
- VLOG("Calling incidentd %p", service.get());
- binder::Status s = service->reportIncident(incidentReport);
- VLOG("Report incident status: %s", s.toString8().string());
- return s.isOk();
+ int err = AIncidentReportArgs_takeReport(args);
+ AIncidentReportArgs_delete(args);
+
+ return err == NO_ERROR;
}
} // namespace statsd
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index d4f4478..c915ef3 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -19,7 +19,6 @@
#include "SubscriberReporter.h"
-using android::IBinder;
using std::lock_guard;
namespace android {
@@ -28,18 +27,66 @@
using std::vector;
+struct BroadcastSubscriberDeathCookie {
+ BroadcastSubscriberDeathCookie(const ConfigKey& configKey, int64_t subscriberId,
+ const shared_ptr<IPendingIntentRef>& pir):
+ mConfigKey(configKey),
+ mSubscriberId(subscriberId),
+ mPir(pir) {}
+
+ ConfigKey mConfigKey;
+ int64_t mSubscriberId;
+ shared_ptr<IPendingIntentRef> mPir;
+};
+
+void SubscriberReporter::broadcastSubscriberDied(void* cookie) {
+ auto cookie_ = static_cast<BroadcastSubscriberDeathCookie*>(cookie);
+ ConfigKey& configKey = cookie_->mConfigKey;
+ int64_t subscriberId = cookie_->mSubscriberId;
+ shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
+
+ SubscriberReporter& thiz = getInstance();
+
+ // Erase the mapping from a (config_key, subscriberId) to a pir if the
+ // mapping exists.
+ lock_guard<mutex> lock(thiz.mLock);
+ auto subscriberMapIt = thiz.mIntentMap.find(configKey);
+ if (subscriberMapIt != thiz.mIntentMap.end()) {
+ auto subscriberMap = subscriberMapIt->second;
+ auto pirIt = subscriberMap.find(subscriberId);
+ if (pirIt != subscriberMap.end() && pirIt->second == pir) {
+ subscriberMap.erase(subscriberId);
+ if (subscriberMap.empty()) {
+ thiz.mIntentMap.erase(configKey);
+ }
+ }
+ }
+
+ // The death recipient corresponding to this specific pir can never be
+ // triggered again, so free up resources.
+ delete cookie_;
+}
+
+SubscriberReporter::SubscriberReporter() :
+ mBroadcastSubscriberDeathRecipient(AIBinder_DeathRecipient_new(broadcastSubscriberDied)) {
+}
+
void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
- const sp<IBinder>& intentSender) {
+ const shared_ptr<IPendingIntentRef>& pir) {
VLOG("SubscriberReporter::setBroadcastSubscriber called.");
- lock_guard<std::mutex> lock(mLock);
- mIntentMap[configKey][subscriberId] = intentSender;
+ {
+ lock_guard<mutex> lock(mLock);
+ mIntentMap[configKey][subscriberId] = pir;
+ }
+ AIBinder_linkToDeath(pir->asBinder().get(), mBroadcastSubscriberDeathRecipient.get(),
+ new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir));
}
void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId) {
VLOG("SubscriberReporter::unsetBroadcastSubscriber called.");
- lock_guard<std::mutex> lock(mLock);
+ lock_guard<mutex> lock(mLock);
auto subscriberMapIt = mIntentMap.find(configKey);
if (subscriberMapIt != mIntentMap.end()) {
subscriberMapIt->second.erase(subscriberId);
@@ -49,12 +96,6 @@
}
}
-void SubscriberReporter::removeConfig(const ConfigKey& configKey) {
- VLOG("SubscriberReporter::removeConfig called.");
- lock_guard<std::mutex> lock(mLock);
- mIntentMap.erase(configKey);
-}
-
void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
const Subscription& subscription,
const MetricDimensionKey& dimKey) const {
@@ -67,7 +108,7 @@
// config id - the name of this config (for this particular uid)
VLOG("SubscriberReporter::alertBroadcastSubscriber called.");
- lock_guard<std::mutex> lock(mLock);
+ lock_guard<mutex> lock(mLock);
if (!subscription.has_broadcast_subscriber_details()
|| !subscription.broadcast_subscriber_details().has_subscriber_id()) {
@@ -76,10 +117,10 @@
}
int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
- vector<String16> cookies;
+ vector<string> cookies;
cookies.reserve(subscription.broadcast_subscriber_details().cookie_size());
for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) {
- cookies.push_back(String16(cookie.c_str()));
+ cookies.push_back(cookie);
}
auto it1 = mIntentMap.find(configKey);
@@ -96,79 +137,33 @@
sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
}
-void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
+void SubscriberReporter::sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
- const vector<String16>& cookies,
+ const vector<string>& cookies,
const MetricDimensionKey& dimKey) const {
VLOG("SubscriberReporter::sendBroadcastLocked called.");
- if (mStatsCompanionService == nullptr) {
- ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
- return;
- }
- mStatsCompanionService->sendSubscriberBroadcast(
- intentSender,
+ pir->sendSubscriberBroadcast(
configKey.GetUid(),
configKey.GetId(),
subscription.id(),
subscription.rule_id(),
cookies,
- getStatsDimensionsValue(dimKey.getDimensionKeyInWhat()));
+ dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel());
}
-void getStatsDimensionsValueHelper(const vector<FieldValue>& dims, size_t* index, int depth,
- int prefix, vector<StatsDimensionsValue>* output) {
- size_t count = dims.size();
- while (*index < count) {
- const auto& dim = dims[*index];
- const int valueDepth = dim.mField.getDepth();
- const int valuePrefix = dim.mField.getPrefix(depth);
- if (valueDepth > 2) {
- ALOGE("Depth > 2 not supported");
- return;
- }
- if (depth == valueDepth && valuePrefix == prefix) {
- switch (dim.mValue.getType()) {
- case INT:
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
- dim.mValue.int_value));
- break;
- case LONG:
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
- dim.mValue.long_value));
- break;
- case FLOAT:
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
- dim.mValue.float_value));
- break;
- case STRING:
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
- String16(dim.mValue.str_value.c_str())));
- break;
- default:
- break;
- }
- (*index)++;
- } else if (valueDepth > depth && valuePrefix == prefix) {
- vector<StatsDimensionsValue> childOutput;
- getStatsDimensionsValueHelper(dims, index, depth + 1, dim.mField.getPrefix(depth + 1),
- &childOutput);
- output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), childOutput));
- } else {
- return;
- }
+shared_ptr<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
+ int64_t subscriberId) {
+ lock_guard<mutex> lock(mLock);
+ auto subscriberMapIt = mIntentMap.find(configKey);
+ if (subscriberMapIt == mIntentMap.end()) {
+ return nullptr;
}
-}
-
-StatsDimensionsValue SubscriberReporter::getStatsDimensionsValue(const HashableDimensionKey& dim) {
- if (dim.getValues().size() == 0) {
- return StatsDimensionsValue();
+ auto pirMapIt = subscriberMapIt->second.find(subscriberId);
+ if (pirMapIt == subscriberMapIt->second.end()) {
+ return nullptr;
}
-
- vector<StatsDimensionsValue> fields;
- size_t index = 0;
- getStatsDimensionsValueHelper(dim.getValues(), &index, 0, 0, &fields);
- return StatsDimensionsValue(dim.getValues()[0].mField.getTag(), fields);
+ return pirMapIt->second;
}
} // namespace statsd
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 2a7f771..4fe4281 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -16,18 +16,25 @@
#pragma once
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IPendingIntentRef.h>
#include <utils/RefBase.h>
+#include <utils/String16.h>
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // subscription
-#include "android/os/StatsDimensionsValue.h"
#include "HashableDimensionKey.h"
#include <mutex>
#include <unordered_map>
#include <vector>
+using aidl::android::os::IPendingIntentRef;
+using std::mutex;
+using std::shared_ptr;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
namespace android {
namespace os {
namespace statsd {
@@ -47,32 +54,17 @@
void operator=(SubscriberReporter const&) = delete;
/**
- * Tells SubscriberReporter what IStatsCompanionService to use.
- * May be nullptr, but SubscriberReporter will not send broadcasts for any calls
- * to alertBroadcastSubscriber that occur while nullptr.
- */
- void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
- std::lock_guard<std::mutex> lock(mLock);
- sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
- mStatsCompanionService = statsCompanionService;
- }
-
- /**
* Stores the given intentSender, associating it with the given (configKey, subscriberId) pair.
- * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
*/
void setBroadcastSubscriber(const ConfigKey& configKey,
int64_t subscriberId,
- const sp<android::IBinder>& intentSender);
+ const shared_ptr<IPendingIntentRef>& pir);
/**
* Erases any intentSender information from the given (configKey, subscriberId) pair.
*/
void unsetBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
- /** Remove all information stored by SubscriberReporter about the given config. */
- void removeConfig(const ConfigKey& configKey);
-
/**
* Sends a broadcast via the intentSender previously stored for the
* given (configKey, subscriberId) pair by setBroadcastSubscriber.
@@ -82,29 +74,34 @@
const Subscription& subscription,
const MetricDimensionKey& dimKey) const;
- static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim);
+ shared_ptr<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey,
+ int64_t subscriberId);
private:
- SubscriberReporter() {};
+ SubscriberReporter();
- mutable std::mutex mLock;
+ mutable mutex mLock;
- /** Binder interface for communicating with StatsCompanionService. */
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
-
- /** Maps <ConfigKey, SubscriberId> -> IBinder (which represents an IIntentSender). */
- std::unordered_map<ConfigKey,
- std::unordered_map<int64_t, sp<android::IBinder>>> mIntentMap;
+ /** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */
+ unordered_map<ConfigKey, unordered_map<int64_t, shared_ptr<IPendingIntentRef>>> mIntentMap;
/**
* Sends a broadcast via the given intentSender (using mStatsCompanionService), along
* with the information in the other parameters.
*/
- void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
+ void sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
const ConfigKey& configKey,
const Subscription& subscription,
- const std::vector<String16>& cookies,
+ const vector<string>& cookies,
const MetricDimensionKey& dimKey) const;
+
+ ::ndk::ScopedAIBinder_DeathRecipient mBroadcastSubscriberDeathRecipient;
+
+ /**
+ * Death recipient callback that is called when a broadcast subscriber dies.
+ * The cookie is a pointer to a BroadcastSubscriberDeathCookie.
+ */
+ static void broadcastSubscriberDied(void* cookie);
};
} // namespace statsd
diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.cpp b/cmds/statsd/src/utils/MultiConditionTrigger.cpp
new file mode 100644
index 0000000..43a6933
--- /dev/null
+++ b/cmds/statsd/src/utils/MultiConditionTrigger.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+#define DEBUG false // STOPSHIP if true
+
+#include "MultiConditionTrigger.h"
+
+#include <thread>
+
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+MultiConditionTrigger::MultiConditionTrigger(const set<string>& conditionNames,
+ function<void()> trigger)
+ : mRemainingConditionNames(conditionNames),
+ mTrigger(trigger),
+ mCompleted(mRemainingConditionNames.empty()) {
+ if (mCompleted) {
+ thread executorThread([this] { mTrigger(); });
+ executorThread.detach();
+ }
+}
+
+void MultiConditionTrigger::markComplete(const string& conditionName) {
+ bool doTrigger = false;
+ {
+ lock_guard<mutex> lg(mMutex);
+ if (mCompleted) {
+ return;
+ }
+ mRemainingConditionNames.erase(conditionName);
+ mCompleted = mRemainingConditionNames.empty();
+ doTrigger = mCompleted;
+ }
+ if (doTrigger) {
+ std::thread executorThread([this] { mTrigger(); });
+ executorThread.detach();
+ }
+}
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.h b/cmds/statsd/src/utils/MultiConditionTrigger.h
new file mode 100644
index 0000000..51f6029
--- /dev/null
+++ b/cmds/statsd/src/utils/MultiConditionTrigger.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <gtest/gtest_prod.h>
+
+#include <mutex>
+#include <set>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * This class provides a utility to wait for a set of named conditions to occur.
+ *
+ * It will execute the trigger runnable in a detached thread once all conditions have been marked
+ * true.
+ */
+class MultiConditionTrigger {
+public:
+ explicit MultiConditionTrigger(const std::set<std::string>& conditionNames,
+ std::function<void()> trigger);
+
+ MultiConditionTrigger(const MultiConditionTrigger&) = delete;
+ MultiConditionTrigger& operator=(const MultiConditionTrigger&) = delete;
+
+ // Mark a specific condition as true. If this condition has called markComplete already or if
+ // the event was not specified in the constructor, the function is a no-op.
+ void markComplete(const std::string& eventName);
+
+private:
+ mutable std::mutex mMutex;
+ std::set<std::string> mRemainingConditionNames;
+ std::function<void()> mTrigger;
+ bool mCompleted;
+
+ FRIEND_TEST(MultiConditionTriggerTest, TestCountDownCalledBySameEventName);
+};
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
deleted file mode 100644
index b87a483..0000000
--- a/cmds/statsd/statsd.rc
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (C) 2017 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.
-
-service statsd /system/bin/statsd
- class main
- socket statsdw dgram+passcred 0222 statsd statsd
- user statsd
- group statsd log
- writepid /dev/cpuset/system-background/tasks
-
-on property:ro.statsd.enable=false
- stop statsd
-
diff --git a/cmds/statsd/statsd_test.xml b/cmds/statsd/statsd_test.xml
new file mode 100644
index 0000000..8f9bb1c
--- /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/AlarmMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp
index 1fccb35..1dc9795 100644
--- a/cmds/statsd/tests/AlarmMonitor_test.cpp
+++ b/cmds/statsd/tests/AlarmMonitor_test.cpp
@@ -17,14 +17,16 @@
#include <gtest/gtest.h>
using namespace android::os::statsd;
+using std::shared_ptr;
#ifdef __ANDROID__
TEST(AlarmMonitor, popSoonerThan) {
std::string emptyMetricId;
std::string emptyDimensionId;
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> set;
- AlarmMonitor am(2, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){});
+ AlarmMonitor am(2,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+ [](const shared_ptr<IStatsCompanionService>&){});
set = am.popSoonerThan(5);
EXPECT_TRUE(set.empty());
@@ -47,19 +49,19 @@
EXPECT_TRUE(set.empty());
set = am.popSoonerThan(30);
- EXPECT_EQ(4u, set.size());
+ ASSERT_EQ(4u, set.size());
EXPECT_EQ(1u, set.count(a));
EXPECT_EQ(1u, set.count(b));
EXPECT_EQ(1u, set.count(c));
EXPECT_EQ(1u, set.count(d));
set = am.popSoonerThan(60);
- EXPECT_EQ(2u, set.size());
+ ASSERT_EQ(2u, set.size());
EXPECT_EQ(1u, set.count(e));
EXPECT_EQ(1u, set.count(f));
set = am.popSoonerThan(80);
- EXPECT_EQ(0u, set.size());
+ ASSERT_EQ(0u, set.size());
}
#else
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index f1cad92..a21eb9b 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -14,13 +14,16 @@
* limitations under the License.
*/
#include <gtest/gtest.h>
+
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/matcher_util.h"
#include "src/logd/LogEvent.h"
+#include "stats_event.h"
#include "stats_log_util.h"
#include "stats_util.h"
#include "subscriber/SubscriberReporter.h"
+#include "tests/statsd_test_util.h"
#ifdef __ANDROID__
@@ -30,6 +33,40 @@
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,
+ const string& name) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_writeString(statsEvent, name.c_str());
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+ const vector<int>& attributionUids, const vector<string>& attributionTags,
+ const int32_t value) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_writeInt32(statsEvent, value);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+} // anonymous namespace
+
TEST(AtomMatcherTest, TestFieldTranslation) {
FieldMatcher matcher1;
matcher1.set_field(10);
@@ -43,7 +80,7 @@
vector<Matcher> output;
translateFieldMatcher(matcher1, &output);
- EXPECT_EQ((size_t)1, output.size());
+ ASSERT_EQ((size_t)1, output.size());
const auto& matcher12 = output[0];
EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag());
@@ -64,7 +101,7 @@
vector<Matcher> output;
translateFieldMatcher(matcher1, &output);
- EXPECT_EQ((size_t)1, output.size());
+ ASSERT_EQ((size_t)1, output.size());
const auto& matcher12 = output[0];
EXPECT_EQ((int32_t)10, matcher12.mMatcher.getTag());
@@ -88,31 +125,16 @@
vector<Matcher> matchers;
translateFieldMatcher(matcher1, &matchers);
- AttributionNodeInternal attribution_node1;
- attribution_node1.set_uid(1111);
- attribution_node1.set_tag("location1");
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
- AttributionNodeInternal attribution_node2;
- attribution_node2.set_uid(2222);
- attribution_node2.set_tag("location2");
-
- AttributionNodeInternal attribution_node3;
- attribution_node3.set_uid(3333);
- attribution_node3.set_tag("location3");
- std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
- attribution_node3};
-
- // Set up the event
- LogEvent event(10, 12345);
- event.write(attribution_nodes);
- event.write("some value");
- // Convert to a LogEvent
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value");
HashableDimensionKey output;
filterValues(matchers, event.getValues(), &output);
- EXPECT_EQ((size_t)7, output.getValues().size());
+ ASSERT_EQ((size_t)7, output.getValues().size());
EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField());
EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value);
EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField());
@@ -174,26 +196,11 @@
}
TEST(AtomMatcherTest, TestMetric2ConditionLink) {
- AttributionNodeInternal attribution_node1;
- attribution_node1.set_uid(1111);
- attribution_node1.set_tag("location1");
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
- AttributionNodeInternal attribution_node2;
- attribution_node2.set_uid(2222);
- attribution_node2.set_tag("location2");
-
- AttributionNodeInternal attribution_node3;
- attribution_node3.set_uid(3333);
- attribution_node3.set_tag("location3");
- std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
- attribution_node3};
-
- // Set up the event
- LogEvent event(10, 12345);
- event.write(attribution_nodes);
- event.write("some value");
- // Convert to a LogEvent
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event, 10 /*atomId*/, 12345, attributionUids, attributionTags, "some value");
FieldMatcher whatMatcher;
whatMatcher.set_field(10);
@@ -217,12 +224,12 @@
translateFieldMatcher(whatMatcher, &link.metricFields);
translateFieldMatcher(conditionMatcher, &link.conditionFields);
- EXPECT_EQ((size_t)1, link.metricFields.size());
+ ASSERT_EQ((size_t)1, link.metricFields.size());
EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField());
EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask);
EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag());
- EXPECT_EQ((size_t)1, link.conditionFields.size());
+ ASSERT_EQ((size_t)1, link.conditionFields.size());
EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField());
EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask);
EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag());
@@ -263,15 +270,15 @@
}
DimensionsValue result;
- EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
+ ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
EXPECT_EQ(10, result.field());
EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case());
- EXPECT_EQ(3, result.value_tuple().dimensions_value_size());
+ ASSERT_EQ(3, result.value_tuple().dimensions_value_size());
const auto& dim1 = result.value_tuple().dimensions_value(0);
EXPECT_EQ(2, dim1.field());
- EXPECT_EQ(2, dim1.value_tuple().dimensions_value_size());
+ ASSERT_EQ(2, dim1.value_tuple().dimensions_value_size());
const auto& dim11 = dim1.value_tuple().dimensions_value(0);
EXPECT_EQ(1, dim11.field());
@@ -284,38 +291,81 @@
const auto& dim3 = result.value_tuple().dimensions_value(2);
EXPECT_EQ(6, dim3.field());
- EXPECT_EQ(1, dim3.value_tuple().dimensions_value_size());
+ ASSERT_EQ(1, dim3.value_tuple().dimensions_value_size());
const auto& dim31 = dim3.value_tuple().dimensions_value(0);
EXPECT_EQ(2, dim31.field());
}
}
-TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
- HashableDimensionKey dim;
+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, 1, 3};
- int pos4[] = {2, 0, 0};
+ int pos3[] = {1, 2, 1};
+ int pos4[] = {1, 2, 2};
+ int pos5[] = {2, 1, 1};
- Field field1(10, pos1, 2);
- Field field2(10, pos2, 2);
- Field field3(10, pos3, 2);
- Field field4(10, pos4, 0);
+ 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)10025);
- Value value2("tag");
- Value value3((int32_t)987654);
- Value value4((int32_t)99999);
+ Value value1((int32_t)1);
+ Value value2("string2");
+ Value value3((int32_t)3);
+ Value value4("string4");
+ Value value5((float)5.0);
- dim.addValue(FieldValue(field1, value1));
- dim.addValue(FieldValue(field2, value2));
- dim.addValue(FieldValue(field3, value3));
- dim.addValue(FieldValue(field4, value4));
+ 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));
- SubscriberReporter::getStatsDimensionsValue(dim);
- // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't
- // have any read api.
+ 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) {
@@ -354,14 +404,14 @@
}
DimensionsValue result;
- EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
+ ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
EXPECT_EQ(10, result.field());
EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, result.value_case());
- EXPECT_EQ(2, result.value_tuple().dimensions_value_size());
+ ASSERT_EQ(2, result.value_tuple().dimensions_value_size());
const auto& dim1 = result.value_tuple().dimensions_value(0);
EXPECT_EQ(DimensionsValue::ValueCase::kValueTuple, dim1.value_case());
- EXPECT_EQ(3, dim1.value_tuple().dimensions_value_size());
+ ASSERT_EQ(3, dim1.value_tuple().dimensions_value_size());
const auto& dim11 = dim1.value_tuple().dimensions_value(0);
EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim11.value_case());
@@ -416,8 +466,8 @@
}
DimensionsValueTuple result;
- EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
- EXPECT_EQ(4, result.dimensions_value_size());
+ ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
+ ASSERT_EQ(4, result.dimensions_value_size());
const auto& dim1 = result.dimensions_value(0);
EXPECT_EQ(DimensionsValue::ValueCase::kValueInt, dim1.value_case());
@@ -437,22 +487,11 @@
}
TEST(AtomMatcherTest, TestWriteAtomToProto) {
- AttributionNodeInternal attribution_node1;
- attribution_node1.set_uid(1111);
- attribution_node1.set_tag("location1");
+ std::vector<int> attributionUids = {1111, 2222};
+ std::vector<string> attributionTags = {"location1", "location2"};
- AttributionNodeInternal attribution_node2;
- attribution_node2.set_uid(2222);
- attribution_node2.set_tag("location2");
-
- std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2};
-
- // Set up the event
- LogEvent event(4, 12345);
- event.write(attribution_nodes);
- event.write((int32_t)999);
- // Convert to a LogEvent
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event, 4 /*atomId*/, 12345, attributionUids, attributionTags, 999);
android::util::ProtoOutputStream protoOutput;
writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput);
@@ -469,10 +508,10 @@
}
Atom result;
- EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
+ ASSERT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case());
const auto& atom = result.ble_scan_result_received();
- EXPECT_EQ(2, atom.attribution_node_size());
+ ASSERT_EQ(2, atom.attribution_node_size());
EXPECT_EQ(1111, atom.attribution_node(0).uid());
EXPECT_EQ("location1", atom.attribution_node(0).tag());
EXPECT_EQ(2222, atom.attribution_node(1).uid());
@@ -480,6 +519,137 @@
EXPECT_EQ(999, atom.num_results());
}
+/*
+ * Test two Matchers is not a subset of one Matcher.
+ * Test one Matcher is subset of two Matchers.
+ */
+TEST(AtomMatcherTest, TestSubsetDimensions1) {
+ // Initialize first set of matchers
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(1);
+ child->set_position(Position::ALL);
+ child->add_child()->set_field(1);
+ child->add_child()->set_field(2);
+
+ vector<Matcher> matchers1;
+ translateFieldMatcher(matcher1, &matchers1);
+ ASSERT_EQ(2, matchers1.size());
+
+ // Initialize second set of matchers
+ FieldMatcher matcher2;
+ matcher2.set_field(10);
+
+ child = matcher2.add_child();
+ child->set_field(1);
+ child->set_position(Position::ALL);
+ child->add_child()->set_field(1);
+
+ vector<Matcher> matchers2;
+ translateFieldMatcher(matcher2, &matchers2);
+ ASSERT_EQ(1, matchers2.size());
+
+ EXPECT_FALSE(subsetDimensions(matchers1, matchers2));
+ EXPECT_TRUE(subsetDimensions(matchers2, matchers1));
+}
+/*
+ * Test not a subset with one matching Matcher, one non-matching Matcher.
+ */
+TEST(AtomMatcherTest, TestSubsetDimensions2) {
+ // Initialize first set of matchers
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(1);
+
+ child = matcher1.add_child();
+ child->set_field(2);
+
+ vector<Matcher> matchers1;
+ translateFieldMatcher(matcher1, &matchers1);
+
+ // Initialize second set of matchers
+ FieldMatcher matcher2;
+ matcher2.set_field(10);
+
+ child = matcher2.add_child();
+ child->set_field(1);
+
+ child = matcher2.add_child();
+ child->set_field(3);
+
+ vector<Matcher> matchers2;
+ translateFieldMatcher(matcher2, &matchers2);
+
+ EXPECT_FALSE(subsetDimensions(matchers1, matchers2));
+}
+
+/*
+ * Test not a subset if parent field is not equal.
+ */
+TEST(AtomMatcherTest, TestSubsetDimensions3) {
+ // Initialize first set of matchers
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(1);
+
+ vector<Matcher> matchers1;
+ translateFieldMatcher(matcher1, &matchers1);
+
+ // Initialize second set of matchers
+ FieldMatcher matcher2;
+ matcher2.set_field(5);
+
+ child = matcher2.add_child();
+ child->set_field(1);
+
+ vector<Matcher> matchers2;
+ translateFieldMatcher(matcher2, &matchers2);
+
+ EXPECT_FALSE(subsetDimensions(matchers1, matchers2));
+}
+
+/*
+ * Test is subset with two matching Matchers.
+ */
+TEST(AtomMatcherTest, TestSubsetDimensions4) {
+ // Initialize first set of matchers
+ FieldMatcher matcher1;
+ matcher1.set_field(10);
+
+ FieldMatcher* child = matcher1.add_child();
+ child->set_field(1);
+
+ child = matcher1.add_child();
+ child->set_field(2);
+
+ vector<Matcher> matchers1;
+ translateFieldMatcher(matcher1, &matchers1);
+
+ // Initialize second set of matchers
+ FieldMatcher matcher2;
+ matcher2.set_field(10);
+
+ child = matcher2.add_child();
+ child->set_field(1);
+
+ child = matcher2.add_child();
+ child->set_field(2);
+
+ child = matcher2.add_child();
+ child->set_field(3);
+
+ vector<Matcher> matchers2;
+ translateFieldMatcher(matcher2, &matchers2);
+
+ EXPECT_TRUE(subsetDimensions(matchers1, matchers2));
+ EXPECT_FALSE(subsetDimensions(matchers2, matchers1));
+}
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/tests/HashableDimensionKey_test.cpp b/cmds/statsd/tests/HashableDimensionKey_test.cpp
new file mode 100644
index 0000000..29adcd0
--- /dev/null
+++ b/cmds/statsd/tests/HashableDimensionKey_test.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+#include "src/HashableDimensionKey.h"
+
+#include <gtest/gtest.h>
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+using android::util::ProtoReader;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Test that #containsLinkedStateValues returns false when the whatKey is
+ * smaller than the primaryKey.
+ */
+TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_WhatKeyTooSmall) {
+ std::vector<Metric2State> mMetric2StateLinks;
+
+ int32_t uid1 = 1000;
+ HashableDimensionKey whatKey = DEFAULT_DIMENSION_KEY;
+ HashableDimensionKey primaryKey;
+ getUidProcessKey(uid1, &primaryKey);
+
+ EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks,
+ UID_PROCESS_STATE_ATOM_ID));
+}
+
+/**
+ * Test that #containsLinkedStateValues returns false when the linked values
+ * are not equal.
+ */
+TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_UnequalLinkedValues) {
+ int stateAtomId = UID_PROCESS_STATE_ATOM_ID;
+
+ FieldMatcher whatMatcher;
+ whatMatcher.set_field(util::OVERLAY_STATE_CHANGED);
+ FieldMatcher* child11 = whatMatcher.add_child();
+ child11->set_field(1);
+
+ FieldMatcher stateMatcher;
+ stateMatcher.set_field(stateAtomId);
+ FieldMatcher* child21 = stateMatcher.add_child();
+ child21->set_field(1);
+
+ std::vector<Metric2State> mMetric2StateLinks;
+ Metric2State ms;
+ ms.stateAtomId = stateAtomId;
+ translateFieldMatcher(whatMatcher, &ms.metricFields);
+ translateFieldMatcher(stateMatcher, &ms.stateFields);
+ mMetric2StateLinks.push_back(ms);
+
+ int32_t uid1 = 1000;
+ int32_t uid2 = 1001;
+ HashableDimensionKey whatKey;
+ getOverlayKey(uid2, "package", &whatKey);
+ HashableDimensionKey primaryKey;
+ getUidProcessKey(uid1, &primaryKey);
+
+ EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId));
+}
+
+/**
+ * Test that #containsLinkedStateValues returns false when there is no link
+ * between the key values.
+ */
+TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_MissingMetric2StateLinks) {
+ int stateAtomId = UID_PROCESS_STATE_ATOM_ID;
+
+ std::vector<Metric2State> mMetric2StateLinks;
+
+ int32_t uid1 = 1000;
+ HashableDimensionKey whatKey;
+ getOverlayKey(uid1, "package", &whatKey);
+ HashableDimensionKey primaryKey;
+ getUidProcessKey(uid1, &primaryKey);
+
+ EXPECT_FALSE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId));
+}
+
+/**
+ * Test that #containsLinkedStateValues returns true when the key values are
+ * linked and equal.
+ */
+TEST(HashableDimensionKeyTest, TestContainsLinkedStateValues_AllConditionsMet) {
+ int stateAtomId = UID_PROCESS_STATE_ATOM_ID;
+
+ FieldMatcher whatMatcher;
+ whatMatcher.set_field(util::OVERLAY_STATE_CHANGED);
+ FieldMatcher* child11 = whatMatcher.add_child();
+ child11->set_field(1);
+
+ FieldMatcher stateMatcher;
+ stateMatcher.set_field(stateAtomId);
+ FieldMatcher* child21 = stateMatcher.add_child();
+ child21->set_field(1);
+
+ std::vector<Metric2State> mMetric2StateLinks;
+ Metric2State ms;
+ ms.stateAtomId = stateAtomId;
+ translateFieldMatcher(whatMatcher, &ms.metricFields);
+ translateFieldMatcher(stateMatcher, &ms.stateFields);
+ mMetric2StateLinks.push_back(ms);
+
+ int32_t uid1 = 1000;
+ HashableDimensionKey whatKey;
+ getOverlayKey(uid1, "package", &whatKey);
+ HashableDimensionKey primaryKey;
+ getUidProcessKey(uid1, &primaryKey);
+
+ EXPECT_TRUE(containsLinkedStateValues(whatKey, primaryKey, mMetric2StateLinks, stateAtomId));
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 2cbcf28..6264c07 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -12,20 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <gtest/gtest.h>
+#include <stdio.h>
+
+#include "annotations.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/matcher_util.h"
+#include "stats_event.h"
#include "stats_log_util.h"
#include "stats_util.h"
-
-#include <gtest/gtest.h>
-
-#include <stdio.h>
+#include "statsd_test_util.h"
using namespace android::os::statsd;
using std::unordered_map;
using std::vector;
const int32_t TAG_ID = 123;
+const int32_t TAG_ID_2 = 28; // hardcoded tag of atom with uid field
const int FIELD_ID_1 = 1;
const int FIELD_ID_2 = 2;
const int FIELD_ID_3 = 2;
@@ -35,6 +38,77 @@
#ifdef __ANDROID__
+
+namespace {
+
+void makeIntLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+ const int32_t value) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+ AStatsEvent_writeInt32(statsEvent, value);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+ const float floatValue) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+ AStatsEvent_writeFloat(statsEvent, floatValue);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+ const string& name) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+ AStatsEvent_writeString(statsEvent, name.c_str());
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+void makeIntWithBoolAnnotationLogEvent(LogEvent* logEvent, const int32_t atomId,
+ const int32_t field, const uint8_t annotationId,
+ const bool annotationValue) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_writeInt32(statsEvent, field);
+ AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags, const string& name) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_writeString(statsEvent, name.c_str());
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+ const bool bool1, const bool bool2) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+ AStatsEvent_writeBool(statsEvent, bool1);
+ AStatsEvent_writeBool(statsEvent, bool2);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+} // anonymous namespace
+
TEST(AtomMatcherTest, TestSimpleMatcher) {
UidMap uidMap;
@@ -43,9 +117,8 @@
auto simpleMatcher = matcher.mutable_simple_atom_matcher();
simpleMatcher->set_atom_id(TAG_ID);
- LogEvent event(TAG_ID, 0);
- EXPECT_TRUE(event.write(11));
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeIntLogEvent(&event, TAG_ID, 0, 11);
// Test
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
@@ -57,26 +130,12 @@
TEST(AtomMatcherTest, TestAttributionMatcher) {
UidMap uidMap;
- AttributionNodeInternal attribution_node1;
- attribution_node1.set_uid(1111);
- attribution_node1.set_tag("location1");
+ std::vector<int> attributionUids = {1111, 2222, 3333};
+ std::vector<string> attributionTags = {"location1", "location2", "location3"};
- AttributionNodeInternal attribution_node2;
- attribution_node2.set_uid(2222);
- attribution_node2.set_tag("location2");
-
- AttributionNodeInternal attribution_node3;
- attribution_node3.set_uid(3333);
- attribution_node3.set_tag("location3");
- std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
- attribution_node3};
-
- // Set up the event
- LogEvent event(TAG_ID, 0);
- event.write(attribution_nodes);
- event.write("some value");
- // Convert to a LogEvent
- event.init();
+ // Set up the log event.
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
// Set up the matcher
AtomMatcher matcher;
@@ -88,8 +147,9 @@
attributionMatcher->set_field(FIELD_ID_1);
attributionMatcher->set_position(Position::FIRST);
attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
- ATTRIBUTION_TAG_FIELD_ID);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("tag");
+ ATTRIBUTION_TAG_FIELD_ID);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "tag");
auto fieldMatcher = simpleMatcher->add_field_value_matcher();
fieldMatcher->set_field(FIELD_ID_2);
@@ -97,40 +157,40 @@
// Tag not matched.
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("location3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "location3");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "location1");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
// Match last node.
attributionMatcher->set_position(Position::LAST);
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("location3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "location3");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
// Match any node.
attributionMatcher->set_position(Position::ANY);
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "location1");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("location2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "location2");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("location3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "location3");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("location4");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "location4");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
// Attribution match but primitive field not match.
attributionMatcher->set_position(Position::ANY);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("location2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "location2");
fieldMatcher->set_eq_string("wrong value");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
@@ -139,8 +199,9 @@
// Uid match.
attributionMatcher->set_position(Position::ANY);
attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field(
- ATTRIBUTION_UID_FIELD_ID);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("pkg0");
+ ATTRIBUTION_UID_FIELD_ID);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg0");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
uidMap.updateMap(
@@ -153,147 +214,183 @@
android::String16(""), android::String16("")});
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg3");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg2");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg1");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg0");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg0");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
attributionMatcher->set_position(Position::FIRST);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg0");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg0");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg3");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg2");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg1");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
attributionMatcher->set_position(Position::LAST);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg0");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg0");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg3");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg2");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg1");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
// Uid + tag.
attributionMatcher->set_position(Position::ANY);
attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
- ATTRIBUTION_TAG_FIELD_ID);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg0");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location1");
+ ATTRIBUTION_TAG_FIELD_ID);
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg0");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location1");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location1");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location2");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg2");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location3");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location3");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location1");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
attributionMatcher->set_position(Position::FIRST);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg0");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg0");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location1");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location1");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location2");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg2");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location3");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location3");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location1");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
attributionMatcher->set_position(Position::LAST);
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg0");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg0");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location1");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location1");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg1");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location2");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg2");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg2");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location3");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location3");
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
- ->set_eq_string("pkg3");
- attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
- ->set_eq_string("location1");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+ "pkg3");
+ attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+ "location1");
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
}
+TEST(AtomMatcherTest, TestUidFieldMatcher) {
+ UidMap uidMap;
+ uidMap.updateMap(
+ 1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+ {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+ android::String16("v1"), android::String16("v2")},
+ {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+ android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+ {android::String16(""), android::String16(""), android::String16(""),
+ android::String16(""), android::String16("")});
+
+ // Set up matcher
+ AtomMatcher matcher;
+ auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+ simpleMatcher->set_atom_id(TAG_ID);
+ simpleMatcher->add_field_value_matcher()->set_field(1);
+ simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0");
+
+ // Make event without is_uid annotation.
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeIntLogEvent(&event1, TAG_ID, 0, 1111);
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+
+ // Make event with is_uid annotation.
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID_2, 1111, ANNOTATION_ID_IS_UID, true);
+
+ // Event has is_uid annotation, so mapping from uid to package name occurs.
+ simpleMatcher->set_atom_id(TAG_ID_2);
+ EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+
+ // Event has is_uid annotation, but uid maps to different package name.
+ simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
+ EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+}
+
TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
UidMap uidMap;
uidMap.updateMap(
@@ -305,30 +402,12 @@
{android::String16(""), android::String16(""), android::String16(""),
android::String16(""), android::String16("")});
- AttributionNodeInternal attribution_node1;
- attribution_node1.set_uid(1111);
- attribution_node1.set_tag("location1");
-
- AttributionNodeInternal attribution_node2;
- attribution_node2.set_uid(2222);
- attribution_node2.set_tag("location2");
-
- AttributionNodeInternal attribution_node3;
- attribution_node3.set_uid(3333);
- attribution_node3.set_tag("location3");
-
- AttributionNodeInternal attribution_node4;
- attribution_node4.set_uid(1066);
- attribution_node4.set_tag("location3");
- std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
- attribution_node3, attribution_node4};
+ std::vector<int> attributionUids = {1111, 2222, 3333, 1066};
+ std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"};
// Set up the event
- LogEvent event(TAG_ID, 0);
- event.write(attribution_nodes);
- event.write("some value");
- // Convert to a LogEvent
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
// Set up the matcher
AtomMatcher matcher;
@@ -384,30 +463,12 @@
{android::String16(""), android::String16(""), android::String16(""),
android::String16(""), android::String16("")});
- AttributionNodeInternal attribution_node1;
- attribution_node1.set_uid(1067);
- attribution_node1.set_tag("location1");
-
- AttributionNodeInternal attribution_node2;
- attribution_node2.set_uid(2222);
- attribution_node2.set_tag("location2");
-
- AttributionNodeInternal attribution_node3;
- attribution_node3.set_uid(3333);
- attribution_node3.set_tag("location3");
-
- AttributionNodeInternal attribution_node4;
- attribution_node4.set_uid(1066);
- attribution_node4.set_tag("location3");
- std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
- attribution_node3, attribution_node4};
+ std::vector<int> attributionUids = {1067, 2222, 3333, 1066};
+ std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"};
// Set up the event
- LogEvent event(TAG_ID, 0);
- event.write(attribution_nodes);
- event.write("some value");
- // Convert to a LogEvent
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
// Set up the matcher
AtomMatcher matcher;
@@ -467,11 +528,8 @@
keyValue2->set_field(FIELD_ID_2);
// Set up the event
- LogEvent event(TAG_ID, 0);
- EXPECT_TRUE(event.write(true));
- EXPECT_TRUE(event.write(false));
- // Convert to a LogEvent
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeBoolLogEvent(&event, TAG_ID, 0, true, false);
// Test
keyValue1->set_eq_bool(true);
@@ -502,10 +560,8 @@
keyValue->set_eq_string("some value");
// Set up the event
- LogEvent event(TAG_ID, 0);
- event.write("some value");
- // Convert to a LogEvent
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeStringLogEvent(&event, TAG_ID, 0, "some value");
// Test
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
@@ -523,12 +579,8 @@
keyValue2->set_field(FIELD_ID_2);
// Set up the event
- LogEvent event(TAG_ID, 0);
- event.write(2);
- event.write(3);
-
- // Convert to a LogEvent
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event, TAG_ID, 0, 2, 3);
// Test
keyValue1->set_eq_int(2);
@@ -555,9 +607,8 @@
keyValue->set_field(FIELD_ID_1);
// Set up the event
- LogEvent event(TAG_ID, 0);
- event.write(11);
- event.init();
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ makeIntLogEvent(&event, TAG_ID, 0, 11);
// Test
@@ -612,26 +663,22 @@
auto keyValue = simpleMatcher->add_field_value_matcher();
keyValue->set_field(FIELD_ID_1);
- LogEvent event1(TAG_ID, 0);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeFloatLogEvent(&event1, TAG_ID, 0, 10.1f);
keyValue->set_lt_float(10.0);
- event1.write(10.1f);
- event1.init();
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
- LogEvent event2(TAG_ID, 0);
- event2.write(9.9f);
- event2.init();
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeFloatLogEvent(&event2, TAG_ID, 0, 9.9f);
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
- LogEvent event3(TAG_ID, 0);
- event3.write(10.1f);
- event3.init();
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeFloatLogEvent(&event3, TAG_ID, 0, 10.1f);
keyValue->set_gt_float(10.0);
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
- LogEvent event4(TAG_ID, 0);
- event4.write(9.9f);
- event4.init();
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ makeFloatLogEvent(&event4, TAG_ID, 0, 9.9f);
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 504ee22..5c170c0 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -13,10 +13,13 @@
// limitations under the License.
#include "src/logd/LogEvent.h"
+
#include <gtest/gtest.h>
-#include <log/log_event_list.h>
+
#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
#include "frameworks/base/core/proto/android/stats/launcher/launcher.pb.h"
+#include "log/log_event_list.h"
+#include "stats_event.h"
#ifdef __ANDROID__
@@ -25,642 +28,341 @@
namespace statsd {
using std::string;
+using std::vector;
using util::ProtoOutputStream;
using util::ProtoReader;
-TEST(LogEventTest, TestLogParsing) {
- LogEvent event1(1, 2000);
+namespace {
- std::vector<AttributionNodeInternal> nodes;
+Field getField(int32_t tag, const vector<int32_t>& pos, int32_t depth, const vector<bool>& last) {
+ Field f(tag, (int32_t*)pos.data(), depth);
- AttributionNodeInternal node1;
- node1.set_uid(1000);
- node1.set_tag("tag1");
- nodes.push_back(node1);
+ // For loop starts at 1 because the last field at depth 0 is not decorated.
+ for (int i = 1; i < depth; i++) {
+ if (last[i]) f.decorateLastPos(i);
+ }
- AttributionNodeInternal node2;
- node2.set_uid(2000);
- node2.set_tag("tag2");
- nodes.push_back(node2);
-
- event1.write(nodes);
- event1.write("hello");
- event1.write((int32_t)10);
- event1.write((int64_t)20);
- event1.write((float)1.1);
- event1.init();
-
- const auto& items = event1.getValues();
- EXPECT_EQ((size_t)8, items.size());
- EXPECT_EQ(1, event1.GetTagId());
-
- const FieldValue& item0 = event1.getValues()[0];
- EXPECT_EQ(0x2010101, item0.mField.getField());
- EXPECT_EQ(Type::INT, item0.mValue.getType());
- EXPECT_EQ(1000, item0.mValue.int_value);
-
- const FieldValue& item1 = event1.getValues()[1];
- EXPECT_EQ(0x2010182, item1.mField.getField());
- EXPECT_EQ(Type::STRING, item1.mValue.getType());
- EXPECT_EQ("tag1", item1.mValue.str_value);
-
- const FieldValue& item2 = event1.getValues()[2];
- EXPECT_EQ(0x2018201, item2.mField.getField());
- EXPECT_EQ(Type::INT, item2.mValue.getType());
- EXPECT_EQ(2000, item2.mValue.int_value);
-
- const FieldValue& item3 = event1.getValues()[3];
- EXPECT_EQ(0x2018282, item3.mField.getField());
- EXPECT_EQ(Type::STRING, item3.mValue.getType());
- EXPECT_EQ("tag2", item3.mValue.str_value);
-
- const FieldValue& item4 = event1.getValues()[4];
- EXPECT_EQ(0x20000, item4.mField.getField());
- EXPECT_EQ(Type::STRING, item4.mValue.getType());
- EXPECT_EQ("hello", item4.mValue.str_value);
-
- const FieldValue& item5 = event1.getValues()[5];
- EXPECT_EQ(0x30000, item5.mField.getField());
- EXPECT_EQ(Type::INT, item5.mValue.getType());
- EXPECT_EQ(10, item5.mValue.int_value);
-
- const FieldValue& item6 = event1.getValues()[6];
- EXPECT_EQ(0x40000, item6.mField.getField());
- EXPECT_EQ(Type::LONG, item6.mValue.getType());
- EXPECT_EQ((int64_t)20, item6.mValue.long_value);
-
- const FieldValue& item7 = event1.getValues()[7];
- EXPECT_EQ(0x50000, item7.mField.getField());
- EXPECT_EQ(Type::FLOAT, item7.mValue.getType());
- EXPECT_EQ((float)1.1, item7.mValue.float_value);
+ return f;
}
-TEST(LogEventTest, TestKeyValuePairsAtomParsing) {
- LogEvent event1(83, 2000, 1000);
- std::map<int32_t, int32_t> int_map;
- std::map<int32_t, int64_t> long_map;
- std::map<int32_t, std::string> string_map;
- std::map<int32_t, float> float_map;
+void createIntWithBoolAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId,
+ bool annotationValue) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
+ AStatsEvent_writeInt32(statsEvent, 10);
+ AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue);
+ AStatsEvent_build(statsEvent);
- int_map[11] = 123;
- int_map[22] = 345;
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ EXPECT_TRUE(logEvent->parseBuffer(buf, size));
- long_map[33] = 678L;
- long_map[44] = 890L;
-
- string_map[1] = "test2";
- string_map[2] = "test1";
-
- float_map[111] = 2.2f;
- float_map[222] = 1.1f;
-
- EXPECT_TRUE(event1.writeKeyValuePairs(0, // Logging side logs 0 uid.
- int_map,
- long_map,
- string_map,
- float_map));
- event1.init();
-
- EXPECT_EQ(83, event1.GetTagId());
- const auto& items = event1.getValues();
- EXPECT_EQ((size_t)17, items.size());
-
- const FieldValue& item0 = event1.getValues()[0];
- EXPECT_EQ(0x10000, item0.mField.getField());
- EXPECT_EQ(Type::INT, item0.mValue.getType());
- EXPECT_EQ(1000, item0.mValue.int_value);
-
- const FieldValue& item1 = event1.getValues()[1];
- EXPECT_EQ(0x2010201, item1.mField.getField());
- EXPECT_EQ(Type::INT, item1.mValue.getType());
- EXPECT_EQ(11, item1.mValue.int_value);
-
- const FieldValue& item2 = event1.getValues()[2];
- EXPECT_EQ(0x2010282, item2.mField.getField());
- EXPECT_EQ(Type::INT, item2.mValue.getType());
- EXPECT_EQ(123, item2.mValue.int_value);
-
- const FieldValue& item3 = event1.getValues()[3];
- EXPECT_EQ(0x2010301, item3.mField.getField());
- EXPECT_EQ(Type::INT, item3.mValue.getType());
- EXPECT_EQ(22, item3.mValue.int_value);
-
- const FieldValue& item4 = event1.getValues()[4];
- EXPECT_EQ(0x2010382, item4.mField.getField());
- EXPECT_EQ(Type::INT, item4.mValue.getType());
- EXPECT_EQ(345, item4.mValue.int_value);
-
- const FieldValue& item5 = event1.getValues()[5];
- EXPECT_EQ(0x2010401, item5.mField.getField());
- EXPECT_EQ(Type::INT, item5.mValue.getType());
- EXPECT_EQ(33, item5.mValue.int_value);
-
- const FieldValue& item6 = event1.getValues()[6];
- EXPECT_EQ(0x2010483, item6.mField.getField());
- EXPECT_EQ(Type::LONG, item6.mValue.getType());
- EXPECT_EQ(678L, item6.mValue.int_value);
-
- const FieldValue& item7 = event1.getValues()[7];
- EXPECT_EQ(0x2010501, item7.mField.getField());
- EXPECT_EQ(Type::INT, item7.mValue.getType());
- EXPECT_EQ(44, item7.mValue.int_value);
-
- const FieldValue& item8 = event1.getValues()[8];
- EXPECT_EQ(0x2010583, item8.mField.getField());
- EXPECT_EQ(Type::LONG, item8.mValue.getType());
- EXPECT_EQ(890L, item8.mValue.int_value);
-
- const FieldValue& item9 = event1.getValues()[9];
- EXPECT_EQ(0x2010601, item9.mField.getField());
- EXPECT_EQ(Type::INT, item9.mValue.getType());
- EXPECT_EQ(1, item9.mValue.int_value);
-
- const FieldValue& item10 = event1.getValues()[10];
- EXPECT_EQ(0x2010684, item10.mField.getField());
- EXPECT_EQ(Type::STRING, item10.mValue.getType());
- EXPECT_EQ("test2", item10.mValue.str_value);
-
- const FieldValue& item11 = event1.getValues()[11];
- EXPECT_EQ(0x2010701, item11.mField.getField());
- EXPECT_EQ(Type::INT, item11.mValue.getType());
- EXPECT_EQ(2, item11.mValue.int_value);
-
- const FieldValue& item12 = event1.getValues()[12];
- EXPECT_EQ(0x2010784, item12.mField.getField());
- EXPECT_EQ(Type::STRING, item12.mValue.getType());
- EXPECT_EQ("test1", item12.mValue.str_value);
-
- const FieldValue& item13 = event1.getValues()[13];
- EXPECT_EQ(0x2010801, item13.mField.getField());
- EXPECT_EQ(Type::INT, item13.mValue.getType());
- EXPECT_EQ(111, item13.mValue.int_value);
-
- const FieldValue& item14 = event1.getValues()[14];
- EXPECT_EQ(0x2010885, item14.mField.getField());
- EXPECT_EQ(Type::FLOAT, item14.mValue.getType());
- EXPECT_EQ(2.2f, item14.mValue.float_value);
-
- const FieldValue& item15 = event1.getValues()[15];
- EXPECT_EQ(0x2018901, item15.mField.getField());
- EXPECT_EQ(Type::INT, item15.mValue.getType());
- EXPECT_EQ(222, item15.mValue.int_value);
-
- const FieldValue& item16 = event1.getValues()[16];
- EXPECT_EQ(0x2018985, item16.mField.getField());
- EXPECT_EQ(Type::FLOAT, item16.mValue.getType());
- EXPECT_EQ(1.1f, item16.mValue.float_value);
+ AStatsEvent_release(statsEvent);
}
-TEST(LogEventTest, TestLogParsing2) {
- LogEvent event1(1, 2000);
+void createIntWithIntAnnotationLogEvent(LogEvent* logEvent, uint8_t annotationId,
+ int annotationValue) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, /*atomId=*/100);
+ AStatsEvent_writeInt32(statsEvent, 10);
+ AStatsEvent_addInt32Annotation(statsEvent, annotationId, annotationValue);
+ AStatsEvent_build(statsEvent);
- std::vector<AttributionNodeInternal> nodes;
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ EXPECT_TRUE(logEvent->parseBuffer(buf, size));
- event1.write("hello");
+ AStatsEvent_release(statsEvent);
+}
- // repeated msg can be in the middle
- AttributionNodeInternal node1;
- node1.set_uid(1000);
- node1.set_tag("tag1");
- nodes.push_back(node1);
+} // anonymous namespace
- AttributionNodeInternal node2;
- node2.set_uid(2000);
- node2.set_tag("tag2");
- nodes.push_back(node2);
- event1.write(nodes);
+TEST(LogEventTest, TestPrimitiveParsing) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ AStatsEvent_writeInt32(event, 10);
+ AStatsEvent_writeInt64(event, 0x123456789);
+ AStatsEvent_writeFloat(event, 2.0);
+ AStatsEvent_writeBool(event, true);
+ AStatsEvent_build(event);
- event1.write((int32_t)10);
- event1.write((int64_t)20);
- event1.write((float)1.1);
- event1.init();
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- const auto& items = event1.getValues();
- EXPECT_EQ((size_t)8, items.size());
- EXPECT_EQ(1, event1.GetTagId());
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
- const FieldValue& item = event1.getValues()[0];
- EXPECT_EQ(0x00010000, item.mField.getField());
+ EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
+ EXPECT_FALSE(logEvent.hasAttributionChain());
+
+ const vector<FieldValue>& values = logEvent.getValues();
+ ASSERT_EQ(4, values.size());
+
+ const FieldValue& int32Item = values[0];
+ Field expectedField = getField(100, {1, 1, 1}, 0, {false, false, false});
+ EXPECT_EQ(expectedField, int32Item.mField);
+ EXPECT_EQ(Type::INT, int32Item.mValue.getType());
+ EXPECT_EQ(10, int32Item.mValue.int_value);
+
+ const FieldValue& int64Item = values[1];
+ expectedField = getField(100, {2, 1, 1}, 0, {false, false, false});
+ EXPECT_EQ(expectedField, int64Item.mField);
+ EXPECT_EQ(Type::LONG, int64Item.mValue.getType());
+ EXPECT_EQ(0x123456789, int64Item.mValue.long_value);
+
+ const FieldValue& floatItem = values[2];
+ expectedField = getField(100, {3, 1, 1}, 0, {false, false, false});
+ EXPECT_EQ(expectedField, floatItem.mField);
+ EXPECT_EQ(Type::FLOAT, floatItem.mValue.getType());
+ EXPECT_EQ(2.0, floatItem.mValue.float_value);
+
+ const FieldValue& boolItem = values[3];
+ expectedField = getField(100, {4, 1, 1}, 0, {true, false, false});
+ EXPECT_EQ(expectedField, boolItem.mField);
+ EXPECT_EQ(Type::INT, boolItem.mValue.getType()); // FieldValue does not support boolean type
+ EXPECT_EQ(1, boolItem.mValue.int_value);
+
+ AStatsEvent_release(event);
+}
+
+TEST(LogEventTest, TestStringAndByteArrayParsing) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ string str = "test";
+ AStatsEvent_writeString(event, str.c_str());
+ AStatsEvent_writeByteArray(event, (uint8_t*)str.c_str(), str.length());
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
+ EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
+ EXPECT_FALSE(logEvent.hasAttributionChain());
+
+ const vector<FieldValue>& values = logEvent.getValues();
+ ASSERT_EQ(2, values.size());
+
+ const FieldValue& stringItem = values[0];
+ Field expectedField = getField(100, {1, 1, 1}, 0, {false, false, false});
+ EXPECT_EQ(expectedField, stringItem.mField);
+ EXPECT_EQ(Type::STRING, stringItem.mValue.getType());
+ EXPECT_EQ(str, stringItem.mValue.str_value);
+
+ const FieldValue& storageItem = values[1];
+ expectedField = getField(100, {2, 1, 1}, 0, {true, false, false});
+ EXPECT_EQ(expectedField, storageItem.mField);
+ EXPECT_EQ(Type::STORAGE, storageItem.mValue.getType());
+ vector<uint8_t> expectedValue = {'t', 'e', 's', 't'};
+ EXPECT_EQ(expectedValue, storageItem.mValue.storage_value);
+
+ AStatsEvent_release(event);
+}
+
+TEST(LogEventTest, TestEmptyString) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ string empty = "";
+ AStatsEvent_writeString(event, empty.c_str());
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
+ EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
+ EXPECT_FALSE(logEvent.hasAttributionChain());
+
+ const vector<FieldValue>& values = logEvent.getValues();
+ ASSERT_EQ(1, values.size());
+
+ const FieldValue& item = values[0];
+ Field expectedField = getField(100, {1, 1, 1}, 0, {true, false, false});
+ EXPECT_EQ(expectedField, item.mField);
EXPECT_EQ(Type::STRING, item.mValue.getType());
- EXPECT_EQ("hello", item.mValue.str_value);
+ EXPECT_EQ(empty, item.mValue.str_value);
- const FieldValue& item0 = event1.getValues()[1];
- EXPECT_EQ(0x2020101, item0.mField.getField());
- EXPECT_EQ(Type::INT, item0.mValue.getType());
- EXPECT_EQ(1000, item0.mValue.int_value);
-
- const FieldValue& item1 = event1.getValues()[2];
- EXPECT_EQ(0x2020182, item1.mField.getField());
- EXPECT_EQ(Type::STRING, item1.mValue.getType());
- EXPECT_EQ("tag1", item1.mValue.str_value);
-
- const FieldValue& item2 = event1.getValues()[3];
- EXPECT_EQ(0x2028201, item2.mField.getField());
- EXPECT_EQ(Type::INT, item2.mValue.getType());
- EXPECT_EQ(2000, item2.mValue.int_value);
-
- const FieldValue& item3 = event1.getValues()[4];
- EXPECT_EQ(0x2028282, item3.mField.getField());
- EXPECT_EQ(Type::STRING, item3.mValue.getType());
- EXPECT_EQ("tag2", item3.mValue.str_value);
-
- const FieldValue& item5 = event1.getValues()[5];
- EXPECT_EQ(0x30000, item5.mField.getField());
- EXPECT_EQ(Type::INT, item5.mValue.getType());
- EXPECT_EQ(10, item5.mValue.int_value);
-
- const FieldValue& item6 = event1.getValues()[6];
- EXPECT_EQ(0x40000, item6.mField.getField());
- EXPECT_EQ(Type::LONG, item6.mValue.getType());
- EXPECT_EQ((int64_t)20, item6.mValue.long_value);
-
- const FieldValue& item7 = event1.getValues()[7];
- EXPECT_EQ(0x50000, item7.mField.getField());
- EXPECT_EQ(Type::FLOAT, item7.mValue.getType());
- EXPECT_EQ((float)1.1, item7.mValue.float_value);
+ AStatsEvent_release(event);
}
-TEST(LogEventTest, TestKeyValuePairsEvent) {
- std::map<int32_t, int32_t> int_map;
- std::map<int32_t, int64_t> long_map;
- std::map<int32_t, std::string> string_map;
- std::map<int32_t, float> float_map;
+TEST(LogEventTest, TestByteArrayWithNullCharacter) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
+ uint8_t message[] = {'\t', 'e', '\0', 's', 't'};
+ AStatsEvent_writeByteArray(event, message, 5);
+ AStatsEvent_build(event);
- int_map[11] = 123;
- int_map[22] = 345;
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
- long_map[33] = 678L;
- long_map[44] = 890L;
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
- string_map[1] = "test2";
- string_map[2] = "test1";
+ EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
- float_map[111] = 2.2f;
- float_map[222] = 1.1f;
+ const vector<FieldValue>& values = logEvent.getValues();
+ ASSERT_EQ(1, values.size());
- LogEvent event1(83, 2000, 2001, 10001, int_map, long_map, string_map, float_map);
- event1.init();
+ const FieldValue& item = values[0];
+ Field expectedField = getField(100, {1, 1, 1}, 0, {true, false, false});
+ EXPECT_EQ(expectedField, item.mField);
+ EXPECT_EQ(Type::STORAGE, item.mValue.getType());
+ vector<uint8_t> expectedValue(message, message + 5);
+ EXPECT_EQ(expectedValue, item.mValue.storage_value);
- EXPECT_EQ(83, event1.GetTagId());
- EXPECT_EQ((int64_t)2000, event1.GetLogdTimestampNs());
- EXPECT_EQ((int64_t)2001, event1.GetElapsedTimestampNs());
- EXPECT_EQ((int64_t)10001, event1.GetUid());
-
- const auto& items = event1.getValues();
- EXPECT_EQ((size_t)17, items.size());
-
- const FieldValue& item0 = event1.getValues()[0];
- EXPECT_EQ(0x00010000, item0.mField.getField());
- EXPECT_EQ(Type::INT, item0.mValue.getType());
- EXPECT_EQ(10001, item0.mValue.int_value);
-
- const FieldValue& item1 = event1.getValues()[1];
- EXPECT_EQ(0x2020101, item1.mField.getField());
- EXPECT_EQ(Type::INT, item1.mValue.getType());
- EXPECT_EQ(11, item1.mValue.int_value);
-
- const FieldValue& item2 = event1.getValues()[2];
- EXPECT_EQ(0x2020182, item2.mField.getField());
- EXPECT_EQ(Type::INT, item2.mValue.getType());
- EXPECT_EQ(123, item2.mValue.int_value);
-
- const FieldValue& item3 = event1.getValues()[3];
- EXPECT_EQ(0x2020201, item3.mField.getField());
- EXPECT_EQ(Type::INT, item3.mValue.getType());
- EXPECT_EQ(22, item3.mValue.int_value);
-
- const FieldValue& item4 = event1.getValues()[4];
- EXPECT_EQ(0x2020282, item4.mField.getField());
- EXPECT_EQ(Type::INT, item4.mValue.getType());
- EXPECT_EQ(345, item4.mValue.int_value);
-
- const FieldValue& item5 = event1.getValues()[5];
- EXPECT_EQ(0x2020301, item5.mField.getField());
- EXPECT_EQ(Type::INT, item5.mValue.getType());
- EXPECT_EQ(33, item5.mValue.int_value);
-
- const FieldValue& item6 = event1.getValues()[6];
- EXPECT_EQ(0x2020383, item6.mField.getField());
- EXPECT_EQ(Type::LONG, item6.mValue.getType());
- EXPECT_EQ(678L, item6.mValue.long_value);
-
- const FieldValue& item7 = event1.getValues()[7];
- EXPECT_EQ(0x2020401, item7.mField.getField());
- EXPECT_EQ(Type::INT, item7.mValue.getType());
- EXPECT_EQ(44, item7.mValue.int_value);
-
- const FieldValue& item8 = event1.getValues()[8];
- EXPECT_EQ(0x2020483, item8.mField.getField());
- EXPECT_EQ(Type::LONG, item8.mValue.getType());
- EXPECT_EQ(890L, item8.mValue.long_value);
-
- const FieldValue& item9 = event1.getValues()[9];
- EXPECT_EQ(0x2020501, item9.mField.getField());
- EXPECT_EQ(Type::INT, item9.mValue.getType());
- EXPECT_EQ(1, item9.mValue.int_value);
-
- const FieldValue& item10 = event1.getValues()[10];
- EXPECT_EQ(0x2020584, item10.mField.getField());
- EXPECT_EQ(Type::STRING, item10.mValue.getType());
- EXPECT_EQ("test2", item10.mValue.str_value);
-
- const FieldValue& item11 = event1.getValues()[11];
- EXPECT_EQ(0x2020601, item11.mField.getField());
- EXPECT_EQ(Type::INT, item11.mValue.getType());
- EXPECT_EQ(2, item11.mValue.int_value);
-
- const FieldValue& item12 = event1.getValues()[12];
- EXPECT_EQ(0x2020684, item12.mField.getField());
- EXPECT_EQ(Type::STRING, item12.mValue.getType());
- EXPECT_EQ("test1", item12.mValue.str_value);
-
- const FieldValue& item13 = event1.getValues()[13];
- EXPECT_EQ(0x2020701, item13.mField.getField());
- EXPECT_EQ(Type::INT, item13.mValue.getType());
- EXPECT_EQ(111, item13.mValue.int_value);
-
- const FieldValue& item14 = event1.getValues()[14];
- EXPECT_EQ(0x2020785, item14.mField.getField());
- EXPECT_EQ(Type::FLOAT, item14.mValue.getType());
- EXPECT_EQ(2.2f, item14.mValue.float_value);
-
- const FieldValue& item15 = event1.getValues()[15];
- EXPECT_EQ(0x2028801, item15.mField.getField());
- EXPECT_EQ(Type::INT, item15.mValue.getType());
- EXPECT_EQ(222, item15.mValue.int_value);
-
- const FieldValue& item16 = event1.getValues()[16];
- EXPECT_EQ(0x2028885, item16.mField.getField());
- EXPECT_EQ(Type::FLOAT, item16.mValue.getType());
- EXPECT_EQ(1.1f, item16.mValue.float_value);
+ AStatsEvent_release(event);
}
-TEST(LogEventTest, TestStatsLogEventWrapperNoChain) {
- Parcel parcel;
- // tag id
- parcel.writeInt32(1);
- // elapsed realtime
- parcel.writeInt64(1111L);
- // wallclock time
- parcel.writeInt64(2222L);
- // no chain
- parcel.writeInt32(0);
- // 2 data
- parcel.writeInt32(2);
- // int 6
- parcel.writeInt32(1);
- parcel.writeInt32(6);
- // long 10
- parcel.writeInt32(2);
- parcel.writeInt64(10);
- parcel.setDataPosition(0);
+TEST(LogEventTest, TestAttributionChain) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, 100);
- StatsLogEventWrapper statsLogEventWrapper;
- EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
- EXPECT_EQ(1, statsLogEventWrapper.getTagId());
- EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
- EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
- EXPECT_EQ(0, statsLogEventWrapper.getWorkChains().size());
- EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
- EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
- EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
- LogEvent event(statsLogEventWrapper, -1);
- EXPECT_EQ(1, event.GetTagId());
- EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event.GetLogdTimestampNs());
- EXPECT_EQ(2, event.size());
- EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
- EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
+ string tag1 = "tag1";
+ string tag2 = "tag2";
+
+ uint32_t uids[] = {1001, 1002};
+ const char* tags[] = {tag1.c_str(), tag2.c_str()};
+
+ AStatsEvent_writeAttributionChain(event, uids, tags, 2);
+ AStatsEvent_build(event);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(event, &size);
+
+ LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
+ EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
+
+ const vector<FieldValue>& values = logEvent.getValues();
+ ASSERT_EQ(4, values.size()); // 2 per attribution node
+
+ std::pair<int, int> attrIndexRange;
+ EXPECT_TRUE(logEvent.hasAttributionChain(&attrIndexRange));
+ EXPECT_EQ(0, attrIndexRange.first);
+ EXPECT_EQ(3, attrIndexRange.second);
+
+ // Check first attribution node
+ const FieldValue& uid1Item = values[0];
+ Field expectedField = getField(100, {1, 1, 1}, 2, {true, false, false});
+ EXPECT_EQ(expectedField, uid1Item.mField);
+ EXPECT_EQ(Type::INT, uid1Item.mValue.getType());
+ EXPECT_EQ(1001, uid1Item.mValue.int_value);
+
+ const FieldValue& tag1Item = values[1];
+ expectedField = getField(100, {1, 1, 2}, 2, {true, false, true});
+ EXPECT_EQ(expectedField, tag1Item.mField);
+ EXPECT_EQ(Type::STRING, tag1Item.mValue.getType());
+ EXPECT_EQ(tag1, tag1Item.mValue.str_value);
+
+ // Check second attribution nodes
+ const FieldValue& uid2Item = values[2];
+ expectedField = getField(100, {1, 2, 1}, 2, {true, true, false});
+ EXPECT_EQ(expectedField, uid2Item.mField);
+ EXPECT_EQ(Type::INT, uid2Item.mValue.getType());
+ EXPECT_EQ(1002, uid2Item.mValue.int_value);
+
+ const FieldValue& tag2Item = values[3];
+ expectedField = getField(100, {1, 2, 2}, 2, {true, true, true});
+ EXPECT_EQ(expectedField, tag2Item.mField);
+ EXPECT_EQ(Type::STRING, tag2Item.mValue.getType());
+ EXPECT_EQ(tag2, tag2Item.mValue.str_value);
+
+ AStatsEvent_release(event);
}
-TEST(LogEventTest, TestStatsLogEventWrapperWithChain) {
- Parcel parcel;
- // tag id
- parcel.writeInt32(1);
- // elapsed realtime
- parcel.writeInt64(1111L);
- // wallclock time
- parcel.writeInt64(2222L);
- // 3 chains
- parcel.writeInt32(3);
- // chain1, 2 nodes (1, "tag1") (2, "tag2")
- parcel.writeInt32(2);
- parcel.writeInt32(1);
- parcel.writeString16(String16("tag1"));
- parcel.writeInt32(2);
- parcel.writeString16(String16("tag2"));
- // chain2, 1 node (3, "tag3")
- parcel.writeInt32(1);
- parcel.writeInt32(3);
- parcel.writeString16(String16("tag3"));
- // chain3, 2 nodes (4, "") (5, "")
- parcel.writeInt32(2);
- parcel.writeInt32(4);
- parcel.writeString16(String16(""));
- parcel.writeInt32(5);
- parcel.writeString16(String16(""));
- // 2 data
- parcel.writeInt32(2);
- // int 6
- parcel.writeInt32(1);
- parcel.writeInt32(6);
- // long 10
- parcel.writeInt32(2);
- parcel.writeInt64(10);
- parcel.setDataPosition(0);
+TEST(LogEventTest, TestAnnotationIdIsUid) {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_IS_UID, true);
- StatsLogEventWrapper statsLogEventWrapper;
- EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
- EXPECT_EQ(1, statsLogEventWrapper.getTagId());
- EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
- EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
- EXPECT_EQ(3, statsLogEventWrapper.getWorkChains().size());
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids.size());
- EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[0].uids[0]);
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids[1]);
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].tags.size());
- EXPECT_EQ("tag1", statsLogEventWrapper.getWorkChains()[0].tags[0]);
- EXPECT_EQ("tag2", statsLogEventWrapper.getWorkChains()[0].tags[1]);
- EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].uids.size());
- EXPECT_EQ(3, statsLogEventWrapper.getWorkChains()[1].uids[0]);
- EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].tags.size());
- EXPECT_EQ("tag3", statsLogEventWrapper.getWorkChains()[1].tags[0]);
- EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
- EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
- EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].uids.size());
- EXPECT_EQ(4, statsLogEventWrapper.getWorkChains()[2].uids[0]);
- EXPECT_EQ(5, statsLogEventWrapper.getWorkChains()[2].uids[1]);
- EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].tags.size());
- EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[0]);
- EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[1]);
-
- LogEvent event(statsLogEventWrapper, -1);
- EXPECT_EQ(1, event.GetTagId());
- EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event.GetLogdTimestampNs());
- EXPECT_EQ(2, event.size());
- EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
- EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
-
- LogEvent event1(statsLogEventWrapper, 0);
-
- EXPECT_EQ(1, event1.GetTagId());
- EXPECT_EQ(1111L, event1.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event1.GetLogdTimestampNs());
- EXPECT_EQ(6, event1.size());
- EXPECT_EQ(1, event1.getValues()[0].mValue.int_value);
- EXPECT_EQ(0x2010101, event1.getValues()[0].mField.getField());
- EXPECT_EQ("tag1", event1.getValues()[1].mValue.str_value);
- EXPECT_EQ(0x2010182, event1.getValues()[1].mField.getField());
- EXPECT_EQ(2, event1.getValues()[2].mValue.int_value);
- EXPECT_EQ(0x2010201, event1.getValues()[2].mField.getField());
- EXPECT_EQ("tag2", event1.getValues()[3].mValue.str_value);
- EXPECT_EQ(0x2018282, event1.getValues()[3].mField.getField());
- EXPECT_EQ(6, event1.getValues()[4].mValue.int_value);
- EXPECT_EQ(0x20000, event1.getValues()[4].mField.getField());
- EXPECT_EQ(10, event1.getValues()[5].mValue.long_value);
- EXPECT_EQ(0x30000, event1.getValues()[5].mField.getField());
-
- LogEvent event2(statsLogEventWrapper, 1);
-
- EXPECT_EQ(1, event2.GetTagId());
- EXPECT_EQ(1111L, event2.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event2.GetLogdTimestampNs());
- EXPECT_EQ(4, event2.size());
- EXPECT_EQ(3, event2.getValues()[0].mValue.int_value);
- EXPECT_EQ(0x2010101, event2.getValues()[0].mField.getField());
- EXPECT_EQ("tag3", event2.getValues()[1].mValue.str_value);
- EXPECT_EQ(0x2018182, event2.getValues()[1].mField.getField());
- EXPECT_EQ(6, event2.getValues()[2].mValue.int_value);
- EXPECT_EQ(0x20000, event2.getValues()[2].mField.getField());
- EXPECT_EQ(10, event2.getValues()[3].mValue.long_value);
- EXPECT_EQ(0x30000, event2.getValues()[3].mField.getField());
-
- LogEvent event3(statsLogEventWrapper, 2);
-
- EXPECT_EQ(1, event3.GetTagId());
- EXPECT_EQ(1111L, event3.GetElapsedTimestampNs());
- EXPECT_EQ(2222L, event3.GetLogdTimestampNs());
- EXPECT_EQ(6, event3.size());
- EXPECT_EQ(4, event3.getValues()[0].mValue.int_value);
- EXPECT_EQ(0x2010101, event3.getValues()[0].mField.getField());
- EXPECT_EQ("", event3.getValues()[1].mValue.str_value);
- EXPECT_EQ(0x2010182, event3.getValues()[1].mField.getField());
- EXPECT_EQ(5, event3.getValues()[2].mValue.int_value);
- EXPECT_EQ(0x2010201, event3.getValues()[2].mField.getField());
- EXPECT_EQ("", event3.getValues()[3].mValue.str_value);
- EXPECT_EQ(0x2018282, event3.getValues()[3].mField.getField());
- EXPECT_EQ(6, event3.getValues()[4].mValue.int_value);
- EXPECT_EQ(0x20000, event3.getValues()[4].mField.getField());
- EXPECT_EQ(10, event3.getValues()[5].mValue.long_value);
- EXPECT_EQ(0x30000, event3.getValues()[5].mField.getField());
+ const vector<FieldValue>& values = event.getValues();
+ ASSERT_EQ(values.size(), 1);
+ EXPECT_EQ(event.getUidFieldIndex(), 0);
}
-TEST(LogEventTest, TestBinaryFieldAtom) {
- Atom launcherAtom;
- auto launcher_event = launcherAtom.mutable_launcher_event();
- launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS);
- launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW);
- launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS);
+TEST(LogEventTest, TestAnnotationIdStateNested) {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_STATE_NESTED, true);
- auto extension = launcher_event->mutable_extension();
+ const vector<FieldValue>& values = event.getValues();
+ ASSERT_EQ(values.size(), 1);
+ EXPECT_TRUE(values[0].mAnnotations.isNested());
+}
- auto src_target = extension->add_src_target();
- src_target->set_type(stats::launcher::LauncherTarget_Type_ITEM_TYPE);
- src_target->set_item(stats::launcher::LauncherTarget_Item_FOLDER_ICON);
+TEST(LogEventTest, TestPrimaryFieldAnnotation) {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_PRIMARY_FIELD, true);
- auto dst_target = extension->add_dst_target();
- dst_target->set_type(stats::launcher::LauncherTarget_Type_ITEM_TYPE);
- dst_target->set_item(stats::launcher::LauncherTarget_Item_WIDGET);
+ const vector<FieldValue>& values = event.getValues();
+ ASSERT_EQ(values.size(), 1);
+ EXPECT_TRUE(values[0].mAnnotations.isPrimaryField());
+}
- string extension_str;
- extension->SerializeToString(&extension_str);
+TEST(LogEventTest, TestExclusiveStateAnnotation) {
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ createIntWithBoolAnnotationLogEvent(&event, ANNOTATION_ID_EXCLUSIVE_STATE, true);
- LogEvent event1(Atom::kLauncherEventFieldNumber, 1000);
+ const vector<FieldValue>& values = event.getValues();
+ ASSERT_EQ(values.size(), 1);
+ EXPECT_TRUE(values[0].mAnnotations.isExclusiveState());
+}
- event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
- event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
- event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
- event1.write(extension_str);
- event1.init();
+TEST(LogEventTest, TestPrimaryFieldFirstUidAnnotation) {
+ // Event has 10 ints and then an attribution chain
+ int numInts = 10;
+ int firstUidInChainIndex = numInts;
+ string tag1 = "tag1";
+ string tag2 = "tag2";
+ uint32_t uids[] = {1001, 1002};
+ const char* tags[] = {tag1.c_str(), tag2.c_str()};
- ProtoOutputStream proto;
- event1.ToProto(proto);
-
- std::vector<uint8_t> outData;
- outData.resize(proto.size());
- size_t pos = 0;
- sp<ProtoReader> reader = proto.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
+ // Construct AStatsEvent
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, 100);
+ for (int i = 0; i < numInts; i++) {
+ AStatsEvent_writeInt32(statsEvent, 10);
}
+ AStatsEvent_writeAttributionChain(statsEvent, uids, tags, 2);
+ AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
+ AStatsEvent_build(statsEvent);
- std::string result_str(outData.begin(), outData.end());
- std::string orig_str;
- launcherAtom.SerializeToString(&orig_str);
+ // Construct LogEvent
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ LogEvent logEvent(/*uid=*/0, /*pid=*/0);
+ EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+ AStatsEvent_release(statsEvent);
- EXPECT_EQ(orig_str, result_str);
+ // Check annotation
+ const vector<FieldValue>& values = logEvent.getValues();
+ ASSERT_EQ(values.size(), numInts + 4);
+ EXPECT_TRUE(values[firstUidInChainIndex].mAnnotations.isPrimaryField());
}
-TEST(LogEventTest, TestBinaryFieldAtom_empty) {
- Atom launcherAtom;
- auto launcher_event = launcherAtom.mutable_launcher_event();
- launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS);
- launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW);
- launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS);
+TEST(LogEventTest, TestResetStateAnnotation) {
+ int32_t resetState = 10;
+ LogEvent event(/*uid=*/0, /*pid=*/0);
+ createIntWithIntAnnotationLogEvent(&event, ANNOTATION_ID_TRIGGER_STATE_RESET, resetState);
- // empty string.
- string extension_str;
-
- LogEvent event1(Atom::kLauncherEventFieldNumber, 1000);
-
- event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
- event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
- event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
- event1.write(extension_str);
- event1.init();
-
- ProtoOutputStream proto;
- event1.ToProto(proto);
-
- std::vector<uint8_t> outData;
- outData.resize(proto.size());
- size_t pos = 0;
- sp<ProtoReader> reader = proto.data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-
- std::string result_str(outData.begin(), outData.end());
- std::string orig_str;
- launcherAtom.SerializeToString(&orig_str);
-
- EXPECT_EQ(orig_str, result_str);
+ const vector<FieldValue>& values = event.getValues();
+ ASSERT_EQ(values.size(), 1);
+ EXPECT_EQ(event.getResetState(), resetState);
}
-TEST(LogEventTest, TestWriteExperimentIdsToProto) {
- std::vector<int64_t> expIds;
- expIds.push_back(5038);
- std::vector<uint8_t> proto;
-
- writeExperimentIdsToProto(expIds, &proto);
-
- EXPECT_EQ(proto.size(), 3);
- // Proto wire format for field ID 1, varint
- EXPECT_EQ(proto[0], 0x08);
- // varint of 5038, 2 bytes long
- EXPECT_EQ(proto[1], 0xae);
- EXPECT_EQ(proto[2], 0x27);
-}
-
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 71adc57..6259757 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -13,7 +13,15 @@
// limitations under the License.
#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "metrics/metrics_test_helper.h"
#include "src/condition/ConditionTracker.h"
#include "src/matchers/LogMatchingTracker.h"
#include "src/metrics/CountMetricProducer.h"
@@ -21,25 +29,26 @@
#include "src/metrics/MetricProducer.h"
#include "src/metrics/ValueMetricProducer.h"
#include "src/metrics/metrics_manager_util.h"
+#include "src/state/StateManager.h"
#include "statsd_test_util.h"
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-
-#include <stdio.h>
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-using namespace android::os::statsd;
+using namespace testing;
using android::sp;
+using android::os::statsd::Predicate;
+using std::map;
using std::set;
using std::unordered_map;
using std::vector;
-using android::os::statsd::Predicate;
#ifdef __ANDROID__
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
const ConfigKey kConfigKey(0, 12345);
+const long kAlertId = 3;
const long timeBaseSec = 1000;
@@ -85,7 +94,7 @@
config.add_no_report_metric(3);
auto alert = config.add_alert();
- alert->set_id(3);
+ alert->set_id(kAlertId);
alert->set_metric_id(3);
alert->set_num_buckets(10);
alert->set_refractory_period_secs(100);
@@ -218,7 +227,7 @@
metric->mutable_dimensions_in_what()->add_child()->set_field(1);
auto alert = config.add_alert();
- alert->set_id(103);
+ alert->set_id(kAlertId);
alert->set_metric_id(3);
alert->set_num_buckets(10);
alert->set_refractory_period_secs(100);
@@ -267,6 +276,157 @@
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();
@@ -284,6 +444,7 @@
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;
@@ -293,10 +454,14 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
- EXPECT_EQ(1u, allMetricProducers.size());
- EXPECT_EQ(1u, allAnomalyTrackers.size());
- EXPECT_EQ(1u, noReportMetricIds.size());
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
+ ASSERT_EQ(1u, allMetricProducers.size());
+ ASSERT_EQ(1u, allAnomalyTrackers.size());
+ ASSERT_EQ(1u, noReportMetricIds.size());
+ ASSERT_EQ(1u, alertTrackerMap.size());
+ EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end());
+ EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0);
}
TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
@@ -316,6 +481,7 @@
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;
@@ -325,7 +491,8 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -345,6 +512,7 @@
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;
@@ -354,7 +522,8 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -374,6 +543,7 @@
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_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
@@ -382,7 +552,8 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, TestMissingPredicate) {
@@ -402,6 +573,7 @@
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_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
@@ -410,7 +582,7 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation, noReportMetricIds));
}
TEST(MetricsManagerTest, TestCirclePredicateDependency) {
@@ -430,6 +602,7 @@
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;
@@ -439,7 +612,8 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
}
TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -459,6 +633,7 @@
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;
@@ -468,9 +643,157 @@
allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
trackerToMetricMap, trackerToConditionMap,
activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
- metricsWithActivation, noReportMetricIds));
+ alertTrackerMap, metricsWithActivation,
+ noReportMetricIds));
}
+TEST(MetricsManagerTest, TestLogSources) {
+ string app1 = "app1";
+ set<int32_t> app1Uids = {1111, 11111};
+ string app2 = "app2";
+ set<int32_t> app2Uids = {2222};
+ string app3 = "app3";
+ set<int32_t> app3Uids = {3333, 1111};
+
+ map<string, set<int32_t>> pkgToUids;
+ pkgToUids[app1] = app1Uids;
+ pkgToUids[app2] = app2Uids;
+ pkgToUids[app3] = app3Uids;
+
+ int32_t atom1 = 10;
+ int32_t atom2 = 20;
+ int32_t atom3 = 30;
+ sp<MockUidMap> uidMap = new StrictMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getAppUid(_))
+ .Times(4)
+ .WillRepeatedly(Invoke([&pkgToUids](const string& pkg) {
+ const auto& it = pkgToUids.find(pkg);
+ if (it != pkgToUids.end()) {
+ return it->second;
+ }
+ return set<int32_t>();
+ }));
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterPullUidProvider(kConfigKey, _)).Times(1);
+ EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey, _)).Times(1);
+
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+
+ StatsdConfig config = buildGoodConfig();
+ config.add_allowed_log_source("AID_SYSTEM");
+ config.add_allowed_log_source(app1);
+ config.add_default_pull_packages("AID_SYSTEM");
+ config.add_default_pull_packages("AID_ROOT");
+
+ const set<int32_t> defaultPullUids = {AID_SYSTEM, AID_ROOT};
+
+ PullAtomPackages* pullAtomPackages = config.add_pull_atom_packages();
+ pullAtomPackages->set_atom_id(atom1);
+ pullAtomPackages->add_packages(app1);
+ pullAtomPackages->add_packages(app3);
+
+ pullAtomPackages = config.add_pull_atom_packages();
+ pullAtomPackages->set_atom_id(atom2);
+ pullAtomPackages->add_packages(app2);
+ pullAtomPackages->add_packages("AID_STATSD");
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+
+ EXPECT_TRUE(metricsManager.isConfigValid());
+
+ ASSERT_EQ(metricsManager.mAllowedUid.size(), 1);
+ EXPECT_EQ(metricsManager.mAllowedUid[0], AID_SYSTEM);
+
+ ASSERT_EQ(metricsManager.mAllowedPkg.size(), 1);
+ EXPECT_EQ(metricsManager.mAllowedPkg[0], app1);
+
+ ASSERT_EQ(metricsManager.mAllowedLogSources.size(), 3);
+ EXPECT_TRUE(isSubset({AID_SYSTEM}, metricsManager.mAllowedLogSources));
+ EXPECT_TRUE(isSubset(app1Uids, metricsManager.mAllowedLogSources));
+
+ ASSERT_EQ(metricsManager.mDefaultPullUids.size(), 2);
+ EXPECT_TRUE(isSubset(defaultPullUids, metricsManager.mDefaultPullUids));
+ ;
+
+ vector<int32_t> atom1Uids = metricsManager.getPullAtomUids(atom1);
+ ASSERT_EQ(atom1Uids.size(), 5);
+ set<int32_t> expectedAtom1Uids;
+ expectedAtom1Uids.insert(defaultPullUids.begin(), defaultPullUids.end());
+ expectedAtom1Uids.insert(app1Uids.begin(), app1Uids.end());
+ expectedAtom1Uids.insert(app3Uids.begin(), app3Uids.end());
+ EXPECT_TRUE(isSubset(expectedAtom1Uids, set<int32_t>(atom1Uids.begin(), atom1Uids.end())));
+
+ vector<int32_t> atom2Uids = metricsManager.getPullAtomUids(atom2);
+ ASSERT_EQ(atom2Uids.size(), 4);
+ set<int32_t> expectedAtom2Uids;
+ expectedAtom1Uids.insert(defaultPullUids.begin(), defaultPullUids.end());
+ expectedAtom1Uids.insert(app2Uids.begin(), app2Uids.end());
+ expectedAtom1Uids.insert(AID_STATSD);
+ EXPECT_TRUE(isSubset(expectedAtom2Uids, set<int32_t>(atom2Uids.begin(), atom2Uids.end())));
+
+ vector<int32_t> atom3Uids = metricsManager.getPullAtomUids(atom3);
+ ASSERT_EQ(atom3Uids.size(), 2);
+ EXPECT_TRUE(isSubset(defaultPullUids, set<int32_t>(atom3Uids.begin(), atom3Uids.end())));
+}
+
+TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom) {
+ sp<UidMap> uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+
+ StatsdConfig config = buildGoodConfig();
+ config.add_whitelisted_atom_ids(3);
+ config.add_whitelisted_atom_ids(4);
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+
+ LogEvent event(0 /* uid */, 0 /* pid */);
+ CreateNoValuesLogEvent(&event, 10 /* atom id */, 0 /* timestamp */);
+ EXPECT_FALSE(metricsManager.checkLogCredentials(event));
+
+ CreateNoValuesLogEvent(&event, 3 /* atom id */, 0 /* timestamp */);
+ EXPECT_TRUE(metricsManager.checkLogCredentials(event));
+
+ CreateNoValuesLogEvent(&event, 4 /* atom id */, 0 /* timestamp */);
+ EXPECT_TRUE(metricsManager.checkLogCredentials(event));
+}
+
+TEST(MetricsManagerTest, TestWhitelistedAtomStateTracker) {
+ sp<UidMap> uidMap;
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> periodicAlarmMonitor;
+
+ StatsdConfig config = buildGoodConfig();
+ config.add_allowed_log_source("AID_SYSTEM");
+ config.add_whitelisted_atom_ids(3);
+ config.add_whitelisted_atom_ids(4);
+
+ State state;
+ state.set_id(1);
+ state.set_atom_id(3);
+
+ *config.add_state() = state;
+
+ config.mutable_count_metric(0)->add_slice_by_state(state.id());
+
+ StateManager::getInstance().clear();
+
+ MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+ pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+
+ EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_FALSE(metricsManager.isConfigValid());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index fe25a25..1e6680c 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,18 +25,14 @@
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
#include "packages/UidMap.h"
+#include "statslog_statsdtest.h"
#include "storage/StorageManager.h"
-#include "statslog.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
#include "tests/statsd_test_util.h"
-#include <stdio.h>
-
using namespace android;
using namespace testing;
+using ::ndk::SharedRefBase;
+using std::shared_ptr;
namespace android {
namespace os {
@@ -49,10 +50,12 @@
MockMetricsManager()
: MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(),
new StatsPullerManager(),
- new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
- [](const sp<IStatsCompanionService>&) {}),
- new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
- [](const sp<IStatsCompanionService>&) {})) {
+ new AlarmMonitor(10,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {}),
+ new AlarmMonitor(10,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+ [](const shared_ptr<IStatsCompanionService>&) {})) {
}
MOCK_METHOD0(byteSize, size_t());
@@ -76,9 +79,9 @@
// Expect only the first flush to trigger a check for byte size since the last two are
// rate-limited.
EXPECT_CALL(mockMetricsManager, byteSize()).Times(1);
- p.flushIfNecessaryLocked(99, key, mockMetricsManager);
- p.flushIfNecessaryLocked(100, key, mockMetricsManager);
- p.flushIfNecessaryLocked(101, key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
}
TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
@@ -103,7 +106,7 @@
StatsdStats::kMaxMetricsBytesPerConfig * .95)));
// Expect only one broadcast despite always returning a size that should trigger broadcast.
- p.flushIfNecessaryLocked(1, key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
EXPECT_EQ(1, broadcastCount);
// b/73089712
@@ -136,7 +139,7 @@
EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1);
// Expect to call the onDumpReport and skip the broadcast.
- p.flushIfNecessaryLocked(1, key, mockMetricsManager);
+ p.flushIfNecessaryLocked(key, mockMetricsManager);
EXPECT_EQ(0, broadcastCount);
}
@@ -183,7 +186,7 @@
EXPECT_TRUE(output.reports_size() > 0);
auto uidmap = output.reports(0).uid_map();
EXPECT_TRUE(uidmap.snapshots_size() > 0);
- EXPECT_EQ(2, uidmap.snapshots(0).package_info_size());
+ ASSERT_EQ(2, uidmap.snapshots(0).package_info_size());
}
TEST(StatsLogProcessorTest, TestEmptyConfigHasNoUidMap) {
@@ -244,7 +247,7 @@
output.ParseFromArray(bytes.data(), bytes.size());
EXPECT_TRUE(output.reports_size() > 0);
auto report = output.reports(0);
- EXPECT_EQ(1, report.annotation_size());
+ ASSERT_EQ(1, report.annotation_size());
EXPECT_EQ(1, report.annotation(0).field_int64());
EXPECT_EQ(2, report.annotation(0).field_int32());
}
@@ -252,7 +255,7 @@
TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
// Setup a simple config.
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = wakelockAcquireMatcher;
@@ -264,37 +267,97 @@
ConfigKey cfgKey;
sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
- auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 2);
+ std::vector<int> attributionUids = {111};
+ std::vector<string> attributionTags = {"App1"};
+ std::unique_ptr<LogEvent> event =
+ CreateAcquireWakelockEvent(2 /*timestamp*/, attributionUids, attributionTags, "wl1");
processor->OnLogEvent(event.get());
vector<uint8_t> bytes;
ConfigMetricsReportList output;
// Dump report WITHOUT erasing data.
- processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST, &bytes);
+ processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST,
+ &bytes);
output.ParseFromArray(bytes.data(), bytes.size());
- EXPECT_EQ(output.reports_size(), 1);
- EXPECT_EQ(output.reports(0).metrics_size(), 1);
- EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+ ASSERT_EQ(output.reports_size(), 1);
+ ASSERT_EQ(output.reports(0).metrics_size(), 1);
+ ASSERT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
// Dump report WITH erasing data. There should be data since we didn't previously erase it.
processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
output.ParseFromArray(bytes.data(), bytes.size());
- EXPECT_EQ(output.reports_size(), 1);
- EXPECT_EQ(output.reports(0).metrics_size(), 1);
- EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+ ASSERT_EQ(output.reports_size(), 1);
+ ASSERT_EQ(output.reports(0).metrics_size(), 1);
+ ASSERT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
// Dump report again. There should be no data since we erased it.
processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
output.ParseFromArray(bytes.data(), bytes.size());
// We don't care whether statsd has a report, as long as it has no count metrics in it.
- bool noData = output.reports_size() == 0
- || output.reports(0).metrics_size() == 0
- || output.reports(0).metrics(0).count_metrics().data_size() == 0;
+ bool noData = output.reports_size() == 0 || output.reports(0).metrics_size() == 0 ||
+ output.reports(0).metrics(0).count_metrics().data_size() == 0;
EXPECT_TRUE(noData);
}
+TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate) {
+ // Setup simple config key corresponding to empty config.
+ sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ 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(false);
+ p.OnConfigUpdated(0, key, config);
+ EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end());
+
+ config.add_default_pull_packages("AID_STATSD");
+ p.OnConfigUpdated(5, key, config);
+ EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end());
+
+ p.OnConfigRemoved(key);
+ 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;
@@ -392,15 +455,16 @@
long timeBase1 = 1;
int broadcastCount = 0;
- StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
- timeBase1, [](const ConfigKey& key) { return true; },
+ StatsLogProcessor processor(
+ m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, timeBase1,
+ [](const ConfigKey& key) { return true; },
[&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
+ const vector<int64_t>& activeConfigs) {
broadcastCount++;
EXPECT_EQ(broadcastUid, uid);
activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
- activeConfigs.begin(), activeConfigs.end());
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
+ activeConfigs.end());
return true;
});
@@ -408,7 +472,7 @@
processor.OnConfigUpdated(2, cfgKey2, config2);
processor.OnConfigUpdated(3, cfgKey3, config3);
- EXPECT_EQ(3, processor.mMetricsManagers.size());
+ ASSERT_EQ(3, processor.mMetricsManagers.size());
// Expect the first config and both metrics in it to be active.
auto it = processor.mMetricsManagers.find(cfgKey1);
@@ -492,8 +556,10 @@
EXPECT_EQ(broadcastCount, 0);
// Activate all 3 metrics that were not active.
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
- auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
+ std::vector<int> attributionUids = {111};
+ std::vector<string> attributionTags = {"App1"};
+ std::unique_ptr<LogEvent> event =
+ CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
processor.OnLogEvent(event.get());
// Assert that all 3 configs are active.
@@ -503,23 +569,23 @@
// A broadcast should have happened, and all 3 configs should be active in the broadcast.
EXPECT_EQ(broadcastCount, 1);
- EXPECT_EQ(activeConfigsBroadcast.size(), 3);
- EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1)
- != activeConfigsBroadcast.end());
- EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2)
- != activeConfigsBroadcast.end());
- EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3)
- != activeConfigsBroadcast.end());
+ ASSERT_EQ(activeConfigsBroadcast.size(), 3);
+ EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) !=
+ activeConfigsBroadcast.end());
+ EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) !=
+ activeConfigsBroadcast.end());
+ EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) !=
+ activeConfigsBroadcast.end());
// When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
processor.SaveActiveConfigsToDisk(shutDownTime);
const int64_t ttl3 = event->GetElapsedTimestampNs() +
- metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
const int64_t ttl5 = event->GetElapsedTimestampNs() +
- metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
const int64_t ttl6 = event->GetElapsedTimestampNs() +
- metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
// Create a second StatsLogProcessor and push the same 3 configs.
long timeBase2 = 1000;
@@ -528,7 +594,7 @@
processor2->OnConfigUpdated(timeBase2, cfgKey2, config2);
processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
- EXPECT_EQ(3, processor2->mMetricsManagers.size());
+ ASSERT_EQ(3, processor2->mMetricsManagers.size());
// First config and both metrics are active.
it = processor2->mMetricsManagers.find(cfgKey1);
@@ -587,7 +653,7 @@
EXPECT_TRUE(it != processor2->mMetricsManagers.end());
auto& metricsManager1003 = it->second;
EXPECT_FALSE(metricsManager1003->isActive());
- EXPECT_EQ(2, metricsManager1003->mAllMetricProducers.size());
+ ASSERT_EQ(2, metricsManager1003->mAllMetricProducers.size());
metricIt = metricsManager1003->mAllMetricProducers.begin();
for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
@@ -670,7 +736,7 @@
sp<StatsLogProcessor> processor =
CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
- EXPECT_EQ(1, processor->mMetricsManagers.size());
+ ASSERT_EQ(1, processor->mMetricsManagers.size());
auto it = processor->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor->mMetricsManagers.end());
auto& metricsManager1 = it->second;
@@ -701,8 +767,10 @@
EXPECT_EQ(0, activation1->start_ns);
EXPECT_EQ(kNotActive, activation1->state);
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
- auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
+ std::vector<int> attributionUids = {111};
+ std::vector<string> attributionTags = {"App1"};
+ std::unique_ptr<LogEvent> event =
+ CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
processor->OnLogEvent(event.get());
EXPECT_FALSE(metricProducer1->isActive());
@@ -718,7 +786,7 @@
sp<StatsLogProcessor> processor2 =
CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
- EXPECT_EQ(1, processor2->mMetricsManagers.size());
+ ASSERT_EQ(1, processor2->mMetricsManagers.size());
it = processor2->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor2->mMetricsManagers.end());
auto& metricsManager1001 = it->second;
@@ -801,7 +869,7 @@
// Metric 1 is not active.
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor->mMetricsManagers.size());
+ ASSERT_EQ(1, processor->mMetricsManagers.size());
auto it = processor->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor->mMetricsManagers.end());
auto& metricsManager1 = it->second;
@@ -830,7 +898,7 @@
int i = 0;
for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
if (metricsManager1->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
+ metric1ActivationTrigger1->atom_matcher_id()) {
break;
}
}
@@ -842,7 +910,7 @@
i = 0;
for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
if (metricsManager1->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
+ metric1ActivationTrigger2->atom_matcher_id()) {
break;
}
}
@@ -853,8 +921,10 @@
// }}}------------------------------------------------------------------------------
// Trigger Activation 1 for Metric 1
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
- auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
+ std::vector<int> attributionUids = {111};
+ std::vector<string> attributionTags = {"App1"};
+ std::unique_ptr<LogEvent> event =
+ CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
processor->OnLogEvent(event.get());
// Metric 1 is not active; Activation 1 set to kActiveOnBoot
@@ -884,7 +954,7 @@
// Metric 1 is not active.
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor2->mMetricsManagers.size());
+ ASSERT_EQ(1, processor2->mMetricsManagers.size());
it = processor2->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor2->mMetricsManagers.end());
auto& metricsManager1001 = it->second;
@@ -913,7 +983,7 @@
i = 0;
for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
+ metric1ActivationTrigger1->atom_matcher_id()) {
break;
}
}
@@ -925,7 +995,7 @@
i = 0;
for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
+ metric1ActivationTrigger2->atom_matcher_id()) {
break;
}
}
@@ -952,10 +1022,8 @@
// }}}--------------------------------------------------------------------------------
// Trigger Activation 2 for Metric 1.
- auto screenOnEvent = CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_ON,
- timeBase2 + 200
- );
+ auto screenOnEvent =
+ CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON);
processor2->OnLogEvent(screenOnEvent.get());
// Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot
@@ -987,7 +1055,7 @@
// Metric 1 is not active.
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor3->mMetricsManagers.size());
+ ASSERT_EQ(1, processor3->mMetricsManagers.size());
it = processor3->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor3->mMetricsManagers.end());
auto& metricsManagerTimeBase3 = it->second;
@@ -1016,7 +1084,7 @@
i = 0;
for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
+ metric1ActivationTrigger1->atom_matcher_id()) {
break;
}
}
@@ -1028,7 +1096,7 @@
i = 0;
for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
+ metric1ActivationTrigger2->atom_matcher_id()) {
break;
}
}
@@ -1057,10 +1125,8 @@
// }}}-------------------------------------------------------------------------------
// Trigger Activation 2 for Metric 1 again.
- screenOnEvent = CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_ON,
- timeBase3 + 100 * NS_PER_SEC
- );
+ screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC,
+ android::view::DISPLAY_STATE_ON);
processor3->OnLogEvent(screenOnEvent.get());
// Metric 1 active; Activation 1 is not active, Activation 2 is set to active
@@ -1091,7 +1157,7 @@
// Metric 1 is not active.
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor4->mMetricsManagers.size());
+ ASSERT_EQ(1, processor4->mMetricsManagers.size());
it = processor4->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor4->mMetricsManagers.end());
auto& metricsManagerTimeBase4 = it->second;
@@ -1120,7 +1186,7 @@
i = 0;
for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
+ metric1ActivationTrigger1->atom_matcher_id()) {
break;
}
}
@@ -1132,7 +1198,7 @@
i = 0;
for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
+ metric1ActivationTrigger2->atom_matcher_id()) {
break;
}
}
@@ -1199,87 +1265,68 @@
ConfigKey cfgKey1(uid, 12341);
long timeBase1 = 1;
- sp<StatsLogProcessor> processor =
+ sp<StatsLogProcessor> processor1 =
CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
// Metric 1 is not active.
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor->mMetricsManagers.size());
- auto it = processor->mMetricsManagers.find(cfgKey1);
- EXPECT_TRUE(it != processor->mMetricsManagers.end());
+ ASSERT_EQ(1, processor1->mMetricsManagers.size());
+ auto it = processor1->mMetricsManagers.find(cfgKey1);
+ EXPECT_TRUE(it != processor1->mMetricsManagers.end());
auto& metricsManager1 = it->second;
EXPECT_TRUE(metricsManager1->isActive());
- auto metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer1 = *metricIt;
- EXPECT_FALSE(metricProducer1->isActive());
+ ASSERT_EQ(metricsManager1->mAllMetricProducers.size(), 2);
+ // We assume that the index of a MetricProducer within the mAllMetricProducers
+ // array follows the order in which metrics are added to the config.
+ auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1);
+ EXPECT_FALSE(metricProducer1_1->isActive()); // inactive due to associated MetricActivation
- metricIt = metricsManager1->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
- auto& metricProducer2 = *metricIt;
- EXPECT_TRUE(metricProducer2->isActive());
+ auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1];
+ EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2);
+ EXPECT_TRUE(metricProducer1_2->isActive());
- int i = 0;
- for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
- if (metricsManager1->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kNotActive, activation1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
+ ASSERT_EQ(metricProducer1_1->mEventActivationMap.size(), 2);
+ // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+ // that matchers are indexed in the order that they are added to the config.
+ const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns);
+ EXPECT_EQ(0, activation1_1_1->start_ns);
+ EXPECT_EQ(kNotActive, activation1_1_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType);
- i = 0;
- for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
- if (metricsManager1->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
+ const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns);
+ EXPECT_EQ(0, activation1_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1_1_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType);
// }}}------------------------------------------------------------------------------
// Trigger Activation 1 for Metric 1
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
- auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
- processor->OnLogEvent(event.get());
+ std::vector<int> attributionUids = {111};
+ std::vector<string> attributionTags = {"App1"};
+ std::unique_ptr<LogEvent> event =
+ CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
+ processor1->OnLogEvent(event.get());
// Metric 1 is not active; Activation 1 set to kActiveOnBoot
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_FALSE(metricProducer1->isActive());
- EXPECT_EQ(0, activation1->start_ns);
- EXPECT_EQ(kActiveOnBoot, activation1->state);
- EXPECT_EQ(0, activation2->start_ns);
- EXPECT_EQ(kNotActive, activation2->state);
+ EXPECT_FALSE(metricProducer1_1->isActive());
+ EXPECT_EQ(0, activation1_1_1->start_ns);
+ EXPECT_EQ(kActiveOnBoot, activation1_1_1->state);
+ EXPECT_EQ(0, activation1_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation1_1_2->state);
- EXPECT_TRUE(metricProducer2->isActive());
+ EXPECT_TRUE(metricProducer1_2->isActive());
// }}}-----------------------------------------------------------------------------
// Simulate shutdown by saving state to disk
int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
- processor->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_FALSE(metricProducer1->isActive());
- int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+ processor1->SaveActiveConfigsToDisk(shutDownTime);
+ EXPECT_FALSE(metricProducer1_1->isActive());
// Simulate device restarted state by creating new instance of StatsLogProcessor with the
// same config.
@@ -1290,58 +1337,37 @@
// Metric 1 is not active.
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor2->mMetricsManagers.size());
+ ASSERT_EQ(1, processor2->mMetricsManagers.size());
it = processor2->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor2->mMetricsManagers.end());
- auto& metricsManager1001 = it->second;
- EXPECT_TRUE(metricsManager1001->isActive());
+ auto& metricsManager2 = it->second;
+ EXPECT_TRUE(metricsManager2->isActive());
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1001 = *metricIt;
- EXPECT_FALSE(metricProducer1001->isActive());
+ ASSERT_EQ(metricsManager2->mAllMetricProducers.size(), 2);
+ // We assume that the index of a MetricProducer within the mAllMetricProducers
+ // array follows the order in which metrics are added to the config.
+ auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1);
+ EXPECT_FALSE(metricProducer2_1->isActive());
- metricIt = metricsManager1001->mAllMetricProducers.begin();
- for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
- auto& metricProducer1002 = *metricIt;
- EXPECT_TRUE(metricProducer1002->isActive());
+ auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1];
+ EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2);
+ EXPECT_TRUE(metricProducer2_2->isActive());
- i = 0;
- for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
- EXPECT_EQ(0, activation1001_1->start_ns);
- EXPECT_EQ(kNotActive, activation1001_1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001_1->activationType);
+ ASSERT_EQ(metricProducer2_1->mEventActivationMap.size(), 2);
+ // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+ // that matchers are indexed in the order that they are added to the config.
+ const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns);
+ EXPECT_EQ(0, activation2_1_1->start_ns);
+ EXPECT_EQ(kNotActive, activation2_1_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType);
- i = 0;
- for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
- if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
-
- const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
- EXPECT_EQ(0, activation1001_2->start_ns);
- EXPECT_EQ(kNotActive, activation1001_2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1001_2->activationType);
+ const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns);
+ EXPECT_EQ(0, activation2_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation2_1_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType);
// }}}-----------------------------------------------------------------------------------
// Load saved state from disk.
@@ -1350,42 +1376,41 @@
// Metric 1 active; Activation 1 is active, Activation 2 is not active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
- EXPECT_EQ(kActive, activation1001_1->state);
- EXPECT_EQ(0, activation1001_2->start_ns);
- EXPECT_EQ(kNotActive, activation1001_2->state);
+ EXPECT_TRUE(metricProducer2_1->isActive());
+ int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+ EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+ EXPECT_EQ(kActive, activation2_1_1->state);
+ EXPECT_EQ(0, activation2_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation2_1_2->state);
- EXPECT_TRUE(metricProducer1002->isActive());
+ EXPECT_TRUE(metricProducer2_2->isActive());
// }}}--------------------------------------------------------------------------------
// Trigger Activation 2 for Metric 1.
- auto screenOnEvent = CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_ON,
- timeBase2 + 200
- );
+ auto screenOnEvent =
+ CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON);
processor2->OnLogEvent(screenOnEvent.get());
// Metric 1 active; Activation 1 is active, Activation 2 is active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
- EXPECT_EQ(kActive, activation1001_1->state);
- EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation1001_2->start_ns);
- EXPECT_EQ(kActive, activation1001_2->state);
+ EXPECT_TRUE(metricProducer2_1->isActive());
+ EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+ EXPECT_EQ(kActive, activation2_1_1->state);
+ EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns);
+ EXPECT_EQ(kActive, activation2_1_2->state);
- EXPECT_TRUE(metricProducer1002->isActive());
+ EXPECT_TRUE(metricProducer2_2->isActive());
// }}}---------------------------------------------------------------------------
// Simulate shutdown by saving state to disk
shutDownTime = timeBase2 + 50 * NS_PER_SEC;
processor2->SaveActiveConfigsToDisk(shutDownTime);
- EXPECT_TRUE(metricProducer1001->isActive());
- EXPECT_TRUE(metricProducer1002->isActive());
- ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
- int64_t ttl2 = screenOnEvent->GetElapsedTimestampNs() +
- metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
+ EXPECT_TRUE(metricProducer2_1->isActive());
+ EXPECT_TRUE(metricProducer2_2->isActive());
+ ttl1 -= shutDownTime - timeBase2;
+ int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC -
+ (shutDownTime - screenOnEvent->GetElapsedTimestampNs());
// Simulate device restarted state by creating new instance of StatsLogProcessor with the
// same config.
@@ -1396,58 +1421,37 @@
// Metric 1 is not active.
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor3->mMetricsManagers.size());
+ ASSERT_EQ(1, processor3->mMetricsManagers.size());
it = processor3->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor3->mMetricsManagers.end());
- auto& metricsManagerTimeBase3 = it->second;
- EXPECT_TRUE(metricsManagerTimeBase3->isActive());
+ auto& metricsManager3 = it->second;
+ EXPECT_TRUE(metricsManager3->isActive());
- metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId1) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
- auto& metricProducerTimeBase3_1 = *metricIt;
- EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
+ ASSERT_EQ(metricsManager3->mAllMetricProducers.size(), 2);
+ // We assume that the index of a MetricProducer within the mAllMetricProducers
+ // array follows the order in which metrics are added to the config.
+ auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1);
+ EXPECT_FALSE(metricProducer3_1->isActive());
- metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
- for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
- if ((*metricIt)->getMetricId() == metricId2) {
- break;
- }
- }
- EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
- auto& metricProducerTimeBase3_2 = *metricIt;
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1];
+ EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2);
+ EXPECT_TRUE(metricProducer3_2->isActive());
- i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger1->atom_matcher_id()) {
- break;
- }
- }
- const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
- EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
- EXPECT_EQ(0, activationTimeBase3_1->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
- EXPECT_EQ(ACTIVATE_ON_BOOT, activationTimeBase3_1->activationType);
+ ASSERT_EQ(metricProducer3_1->mEventActivationMap.size(), 2);
+ // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+ // that matchers are indexed in the order that they are added to the config.
+ const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0);
+ EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns);
+ EXPECT_EQ(0, activation3_1_1->start_ns);
+ EXPECT_EQ(kNotActive, activation3_1_1->state);
+ EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType);
- i = 0;
- for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
- if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
- metric1ActivationTrigger2->atom_matcher_id()) {
- break;
- }
- }
-
- const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
- EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
- EXPECT_EQ(0, activationTimeBase3_2->start_ns);
- EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
- EXPECT_EQ(ACTIVATE_IMMEDIATELY, activationTimeBase3_2->activationType);
+ const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1);
+ EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns);
+ EXPECT_EQ(0, activation3_1_2->start_ns);
+ EXPECT_EQ(kNotActive, activation3_1_2->state);
+ EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType);
// }}}----------------------------------------------------------------------------------
// Load saved state from disk.
@@ -1456,32 +1460,30 @@
// Metric 1 active: Activation 1 is active, Activation 2 is active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
- EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_1->state);
- EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_2->state);
+ EXPECT_TRUE(metricProducer3_1->isActive());
+ EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns);
+ EXPECT_EQ(kActive, activation3_1_1->state);
+ EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns);
+ EXPECT_EQ(kActive, activation3_1_2->state);
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ EXPECT_TRUE(metricProducer3_2->isActive());
// }}}-------------------------------------------------------------------------------
-
// Trigger Activation 2 for Metric 1 again.
- screenOnEvent = CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_ON,
- timeBase3 + 100 * NS_PER_SEC
- );
+ screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC,
+ android::view::DISPLAY_STATE_ON);
processor3->OnLogEvent(screenOnEvent.get());
- // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
+ // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire),
+ // Activation 2 is set to active
// Metric 2 is active.
// {{{---------------------------------------------------------------------------
- EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
- EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
- EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activationTimeBase3_2->start_ns);
- EXPECT_EQ(kActive, activationTimeBase3_2->state);
+ EXPECT_TRUE(metricProducer3_1->isActive());
+ EXPECT_EQ(kNotActive, activation3_1_1->state);
+ EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns);
+ EXPECT_EQ(kActive, activation3_1_2->state);
- EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+ EXPECT_TRUE(metricProducer3_2->isActive());
// }}}---------------------------------------------------------------------------
}
@@ -1549,9 +1551,9 @@
metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
// Send the config.
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
string serialized = config1.SerializeAsString();
- service.addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
+ service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
// Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
StatsdConfig tmpConfig;
@@ -1562,13 +1564,13 @@
// Metric 2 is not active.
// Metric 3 is active.
// {{{---------------------------------------------------------------------------
- sp<StatsLogProcessor> processor = service.mProcessor;
- EXPECT_EQ(1, processor->mMetricsManagers.size());
+ sp<StatsLogProcessor> processor = service->mProcessor;
+ ASSERT_EQ(1, processor->mMetricsManagers.size());
auto it = processor->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor->mMetricsManagers.end());
auto& metricsManager1 = it->second;
EXPECT_TRUE(metricsManager1->isActive());
- EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size());
+ ASSERT_EQ(3, metricsManager1->mAllMetricProducers.size());
auto& metricProducer1 = metricsManager1->mAllMetricProducers[0];
EXPECT_EQ(metricId1, metricProducer1->getMetricId());
@@ -1583,7 +1585,7 @@
EXPECT_TRUE(metricProducer3->isActive());
// Check event activations.
- EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
+ ASSERT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
metric1ActivationTrigger1->atom_matcher_id());
const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
@@ -1619,13 +1621,16 @@
// Trigger Activation 1 for Metric 1. Should activate on boot.
// Trigger Activation 4 for Metric 2. Should activate immediately.
- long configAddedTimeNs = metricsManager1->mLastReportTimeNs;
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
- auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 1 + configAddedTimeNs);
- processor->OnLogEvent(event.get());
+ int64_t configAddedTimeNs = metricsManager1->mLastReportTimeNs;
+ std::vector<int> attributionUids = {111};
+ std::vector<string> attributionTags = {"App1"};
+ std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(
+ 1 + configAddedTimeNs, attributionUids, attributionTags, "wl1");
+ processor->OnLogEvent(event1.get());
- event = CreateFinishScheduledJobEvent(attributions1, "finish1", 2 + configAddedTimeNs);
- processor->OnLogEvent(event.get());
+ std::unique_ptr<LogEvent> event2 = CreateFinishScheduledJobEvent(
+ 2 + configAddedTimeNs, attributionUids, attributionTags, "finish1");
+ processor->OnLogEvent(event2.get());
// Metric 1 is not active; Activation 1 set to kActiveOnBoot
// Metric 2 is active. Activation 4 set to kActive
@@ -1653,16 +1658,16 @@
EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
// System server dies.
- service.binderDied(nullptr);
+ service->statsCompanionServiceDiedImpl();
// We should have a new metrics manager. Lets get it and ensure activation status is restored.
// {{{---------------------------------------------------------------------------
- EXPECT_EQ(1, processor->mMetricsManagers.size());
+ ASSERT_EQ(1, processor->mMetricsManagers.size());
it = processor->mMetricsManagers.find(cfgKey1);
EXPECT_TRUE(it != processor->mMetricsManagers.end());
auto& metricsManager2 = it->second;
EXPECT_TRUE(metricsManager2->isActive());
- EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size());
+ ASSERT_EQ(3, metricsManager2->mAllMetricProducers.size());
auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0];
EXPECT_EQ(metricId1, metricProducer1001->getMetricId());
@@ -1680,7 +1685,7 @@
// Activation 1 is kActiveOnBoot.
// Activation 2 and 3 are not active.
// Activation 4 is active.
- EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
+ ASSERT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
metric1ActivationTrigger1->atom_matcher_id());
const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
@@ -1713,6 +1718,163 @@
EXPECT_EQ(kActive, activation1004->state);
EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
// }}}------------------------------------------------------------------------------
+
+ // Clear the data stored on disk as a result of the system server death.
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true, ADB_DUMP, FAST,
+ &buffer);
+}
+
+TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUid) {
+ int hostUid = 20;
+ int isolatedUid = 30;
+ uint64_t eventTimeNs = 12355;
+ int atomId = 89;
+ int field1 = 90;
+ int field2 = 28;
+ sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+ ConfigKey cfgKey;
+ StatsdConfig config = MakeConfig(false);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
+
+ shared_ptr<LogEvent> logEvent = makeUidLogEvent(atomId, eventTimeNs, hostUid, field1, field2);
+
+ processor->OnLogEvent(logEvent.get());
+
+ const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value);
+}
+
+TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUid) {
+ int hostUid = 20;
+ int isolatedUid = 30;
+ uint64_t eventTimeNs = 12355;
+ int atomId = 89;
+ int field1 = 90;
+ int field2 = 28;
+ sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+ ConfigKey cfgKey;
+ StatsdConfig config = MakeConfig(false);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
+
+ shared_ptr<LogEvent> logEvent =
+ makeUidLogEvent(atomId, eventTimeNs, isolatedUid, field1, field2);
+
+ processor->OnLogEvent(logEvent.get());
+
+ const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(field1, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(field2, actualFieldValues->at(2).mValue.int_value);
+}
+
+TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogHostUidAttributionChain) {
+ int hostUid = 20;
+ int isolatedUid = 30;
+ uint64_t eventTimeNs = 12355;
+ int atomId = 89;
+ int field1 = 90;
+ int field2 = 28;
+ sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+ ConfigKey cfgKey;
+ StatsdConfig config = MakeConfig(false);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
+
+ shared_ptr<LogEvent> logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {hostUid, 200},
+ {"tag1", "tag2"}, field1, field2);
+
+ processor->OnLogEvent(logEvent.get());
+
+ const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value);
+ EXPECT_EQ(field2, actualFieldValues->at(5).mValue.int_value);
+}
+
+TEST(StatsLogProcessorTest_mapIsolatedUidToHostUid, LogIsolatedUidAttributionChain) {
+ int hostUid = 20;
+ int isolatedUid = 30;
+ uint64_t eventTimeNs = 12355;
+ int atomId = 89;
+ int field1 = 90;
+ int field2 = 28;
+ sp<MockUidMap> mockUidMap = makeMockUidMapForOneHost(hostUid, {isolatedUid});
+ ConfigKey cfgKey;
+ StatsdConfig config = MakeConfig(false);
+ sp<StatsLogProcessor> processor =
+ CreateStatsLogProcessor(1, 1, config, cfgKey, nullptr, 0, mockUidMap);
+
+ shared_ptr<LogEvent> logEvent = makeAttributionLogEvent(atomId, eventTimeNs, {isolatedUid, 200},
+ {"tag1", "tag2"}, field1, field2);
+
+ processor->OnLogEvent(logEvent.get());
+
+ const vector<FieldValue>* actualFieldValues = &logEvent->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(200, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(field1, actualFieldValues->at(4).mValue.int_value);
+ 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
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
index 7c00531..cc38c4a 100644
--- a/cmds/statsd/tests/StatsService_test.cpp
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -16,6 +16,7 @@
#include "config/ConfigKey.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include <android/binder_interface_utils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -29,33 +30,34 @@
namespace statsd {
using android::util::ProtoOutputStream;
+using ::ndk::SharedRefBase;
#ifdef __ANDROID__
TEST(StatsServiceTest, TestAddConfig_simple) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
StatsdConfig config;
config.set_id(12345);
string serialized = config.SerializeAsString();
EXPECT_TRUE(
- service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+ service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
}
TEST(StatsServiceTest, TestAddConfig_empty) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
string serialized = "";
EXPECT_TRUE(
- service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+ service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
}
TEST(StatsServiceTest, TestAddConfig_invalid) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
string serialized = "Invalid config!";
EXPECT_FALSE(
- service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+ service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
}
TEST(StatsServiceTest, TestGetUidFromArgs) {
@@ -63,38 +65,34 @@
args.push(String8("-1"));
args.push(String8("0"));
args.push(String8("1"));
- args.push(String8("9999999999999999999999999999999999"));
args.push(String8("a1"));
args.push(String8(""));
int32_t uid;
- StatsService service(nullptr, nullptr);
- service.mEngBuild = true;
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ service->mEngBuild = true;
// "-1"
- EXPECT_FALSE(service.getUidFromArgs(args, 0, uid));
+ EXPECT_FALSE(service->getUidFromArgs(args, 0, uid));
// "0"
- EXPECT_TRUE(service.getUidFromArgs(args, 1, uid));
+ EXPECT_TRUE(service->getUidFromArgs(args, 1, uid));
EXPECT_EQ(0, uid);
// "1"
- EXPECT_TRUE(service.getUidFromArgs(args, 2, uid));
+ EXPECT_TRUE(service->getUidFromArgs(args, 2, uid));
EXPECT_EQ(1, uid);
- // "999999999999999999"
- EXPECT_FALSE(service.getUidFromArgs(args, 3, uid));
-
// "a1"
- EXPECT_FALSE(service.getUidFromArgs(args, 4, uid));
+ EXPECT_FALSE(service->getUidFromArgs(args, 3, uid));
// ""
- EXPECT_FALSE(service.getUidFromArgs(args, 5, uid));
+ EXPECT_FALSE(service->getUidFromArgs(args, 4, uid));
// For a non-userdebug, uid "1" cannot be impersonated.
- service.mEngBuild = false;
- EXPECT_FALSE(service.getUidFromArgs(args, 2, uid));
+ service->mEngBuild = false;
+ EXPECT_FALSE(service->getUidFromArgs(args, 2, uid));
}
#else
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index d9fa4e9..293e8ed 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -18,7 +18,7 @@
#include "guardrail/StatsdStats.h"
#include "logd/LogEvent.h"
#include "hash.h"
-#include "statslog.h"
+#include "statslog_statsdtest.h"
#include "statsd_test_util.h"
#include <android/util/ProtoOutputStream.h>
@@ -45,26 +45,20 @@
sp<AlarmMonitor> anomalyAlarmMonitor;
sp<AlarmMonitor> subscriberAlarmMonitor;
// Construct the processor with a dummy sendBroadcast function that does nothing.
- StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
- [](const ConfigKey& key) { return true; },
- [](const int&, const vector<int64_t>&) {return true;});
- LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
- addEvent.write(100); // parent UID
- addEvent.write(101); // isolated UID
- addEvent.write(1); // Indicates creation.
- addEvent.init();
+ StatsLogProcessor p(
+ m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+ [](const ConfigKey& key) { return true; },
+ [](const int&, const vector<int64_t>&) { return true; });
+ std::unique_ptr<LogEvent> addEvent = CreateIsolatedUidChangedEvent(
+ 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 1 /*is_create*/);
EXPECT_EQ(101, m->getHostUidOrSelf(101));
-
- p.OnLogEvent(&addEvent);
+ p.OnLogEvent(addEvent.get());
EXPECT_EQ(100, m->getHostUidOrSelf(101));
- LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1);
- removeEvent.write(100); // parent UID
- removeEvent.write(101); // isolated UID
- removeEvent.write(0); // Indicates removal.
- removeEvent.init();
- p.OnLogEvent(&removeEvent);
+ std::unique_ptr<LogEvent> removeEvent = CreateIsolatedUidChangedEvent(
+ 1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 0 /*is_create*/);
+ p.OnLogEvent(removeEvent.get());
EXPECT_EQ(101, m->getHostUidOrSelf(101));
}
@@ -92,7 +86,7 @@
EXPECT_FALSE(m.hasApp(1000, "not.app"));
std::set<string> name_set = m.getAppNamesFromUid(1000u, true /* returnNormalized */);
- EXPECT_EQ(name_set.size(), 2u);
+ ASSERT_EQ(name_set.size(), 2u);
EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
@@ -121,7 +115,7 @@
m.updateMap(1, uids, versions, versionStrings, apps, installers);
std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- EXPECT_EQ(name_set.size(), 2u);
+ ASSERT_EQ(name_set.size(), 2u);
EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
@@ -130,7 +124,7 @@
EXPECT_EQ(40, m.getAppVersion(1000, kApp1));
name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- EXPECT_EQ(name_set.size(), 2u);
+ ASSERT_EQ(name_set.size(), 2u);
EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
@@ -138,7 +132,7 @@
EXPECT_FALSE(m.hasApp(1000, kApp1));
EXPECT_TRUE(m.hasApp(1000, kApp2));
name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- EXPECT_EQ(name_set.size(), 1u);
+ ASSERT_EQ(name_set.size(), 1u);
EXPECT_TRUE(name_set.find(kApp1) == name_set.end());
EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
@@ -155,14 +149,14 @@
m.updateMap(1, {1000, 1000}, {4, 5}, {String16("v4"), String16("v5")},
{String16(kApp1.c_str()), String16(kApp2.c_str())}, {String16(""), String16("")});
std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- EXPECT_EQ(name_set.size(), 2u);
+ ASSERT_EQ(name_set.size(), 2u);
EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
// Adds a new name for uid 1000.
m.updateApp(2, String16("NeW_aPP1_NAmE"), 1000, 40, String16("v40"), String16(""));
name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
- EXPECT_EQ(name_set.size(), 3u);
+ ASSERT_EQ(name_set.size(), 3u);
EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end());
@@ -171,7 +165,7 @@
// This name is also reused by another uid 2000.
m.updateApp(3, String16("NeW_aPP1_NAmE"), 2000, 1, String16("v1"), String16(""));
name_set = m.getAppNamesFromUid(2000, true /* returnNormalized */);
- EXPECT_EQ(name_set.size(), 1u);
+ ASSERT_EQ(name_set.size(), 1u);
EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end());
EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
}
@@ -218,7 +212,7 @@
// Check there's still a uidmap attached this one.
UidMapping results;
protoOutputStreamToUidMapping(&proto, &results);
- EXPECT_EQ(1, results.snapshots_size());
+ ASSERT_EQ(1, results.snapshots_size());
EXPECT_EQ("v1", results.snapshots(0).package_info(0).version_string());
}
@@ -246,7 +240,7 @@
// Snapshot should still contain this item as deleted.
UidMapping results;
protoOutputStreamToUidMapping(&proto, &results);
- EXPECT_EQ(1, results.snapshots(0).package_info_size());
+ ASSERT_EQ(1, results.snapshots(0).package_info_size());
EXPECT_EQ(true, results.snapshots(0).package_info(0).deleted());
}
@@ -275,7 +269,7 @@
ProtoOutputStream proto;
m.appendUidMap(3, config1, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
- EXPECT_EQ(maxDeletedApps + 10, results.snapshots(0).package_info_size());
+ ASSERT_EQ(maxDeletedApps + 10, results.snapshots(0).package_info_size());
// Now remove all the apps.
m.updateMap(1, uids, versions, versionStrings, apps, installers);
@@ -287,7 +281,7 @@
m.appendUidMap(5, config1, nullptr, true, true, &proto);
// Snapshot drops the first nine items.
protoOutputStreamToUidMapping(&proto, &results);
- EXPECT_EQ(maxDeletedApps, results.snapshots(0).package_info_size());
+ ASSERT_EQ(maxDeletedApps, results.snapshots(0).package_info_size());
}
TEST(UidMapTest, TestClearingOutput) {
@@ -319,44 +313,44 @@
m.appendUidMap(2, config1, nullptr, true, true, &proto);
UidMapping results;
protoOutputStreamToUidMapping(&proto, &results);
- EXPECT_EQ(1, results.snapshots_size());
+ ASSERT_EQ(1, results.snapshots_size());
// We have to keep at least one snapshot in memory at all times.
proto.clear();
m.appendUidMap(2, config1, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
- EXPECT_EQ(1, results.snapshots_size());
+ ASSERT_EQ(1, results.snapshots_size());
// Now add another configuration.
m.OnConfigUpdated(config2);
m.updateApp(5, String16(kApp1.c_str()), 1000, 40, String16("v40"), String16(""));
- EXPECT_EQ(1U, m.mChanges.size());
+ ASSERT_EQ(1U, m.mChanges.size());
proto.clear();
m.appendUidMap(6, config1, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
- EXPECT_EQ(1, results.snapshots_size());
- EXPECT_EQ(1, results.changes_size());
- EXPECT_EQ(1U, m.mChanges.size());
+ ASSERT_EQ(1, results.snapshots_size());
+ ASSERT_EQ(1, results.changes_size());
+ ASSERT_EQ(1U, m.mChanges.size());
// Add another delta update.
m.updateApp(7, String16(kApp2.c_str()), 1001, 41, String16("v41"), String16(""));
- EXPECT_EQ(2U, m.mChanges.size());
+ ASSERT_EQ(2U, m.mChanges.size());
// We still can't remove anything.
proto.clear();
m.appendUidMap(8, config1, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
- EXPECT_EQ(1, results.snapshots_size());
- EXPECT_EQ(1, results.changes_size());
- EXPECT_EQ(2U, m.mChanges.size());
+ ASSERT_EQ(1, results.snapshots_size());
+ ASSERT_EQ(1, results.changes_size());
+ ASSERT_EQ(2U, m.mChanges.size());
proto.clear();
m.appendUidMap(9, config2, nullptr, true, true, &proto);
protoOutputStreamToUidMapping(&proto, &results);
- EXPECT_EQ(1, results.snapshots_size());
- EXPECT_EQ(2, results.changes_size());
+ ASSERT_EQ(1, results.snapshots_size());
+ ASSERT_EQ(2, results.changes_size());
// At this point both should be cleared.
- EXPECT_EQ(0U, m.mChanges.size());
+ ASSERT_EQ(0U, m.mChanges.size());
}
TEST(UidMapTest, TestMemoryComputed) {
@@ -414,13 +408,13 @@
m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2,
String16("v2"), String16(""));
- EXPECT_EQ(1U, m.mChanges.size());
+ ASSERT_EQ(1U, m.mChanges.size());
// Now force deletion by limiting the memory to hold one delta change.
m.maxBytesOverride = 120; // Since the app string alone requires >45 characters.
m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4,
String16("v4"), String16(""));
- EXPECT_EQ(1U, m.mChanges.size());
+ ASSERT_EQ(1U, m.mChanges.size());
}
#else
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
index e664023..64ea219 100644
--- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -15,12 +15,14 @@
#include "src/anomaly/AlarmTracker.h"
#include <gtest/gtest.h>
+#include <log/log_time.h>
#include <stdio.h>
#include <vector>
using namespace testing;
using android::sp;
using std::set;
+using std::shared_ptr;
using std::unordered_map;
using std::vector;
@@ -34,8 +36,9 @@
TEST(AlarmTrackerTest, TestTriggerTimestamp) {
sp<AlarmMonitor> subscriberAlarmMonitor =
- new AlarmMonitor(100, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){});
+ new AlarmMonitor(100,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+ [](const shared_ptr<IStatsCompanionService>&){});
Alarm alarm;
alarm.set_offset_millis(15 * MS_PER_SEC);
alarm.set_period_millis(60 * 60 * MS_PER_SEC); // 1hr
@@ -56,7 +59,7 @@
currentTimeSec = startMillis / MS_PER_SEC + 7000;
nextAlarmTime = startMillis / MS_PER_SEC + 15 + 2 * 60 * 60;
firedAlarmSet = subscriberAlarmMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec));
- EXPECT_EQ(firedAlarmSet.size(), 1u);
+ ASSERT_EQ(firedAlarmSet.size(), 1u);
tracker.informAlarmsFired(currentTimeSec * NS_PER_SEC, firedAlarmSet);
EXPECT_TRUE(firedAlarmSet.empty());
EXPECT_EQ(tracker.mAlarmSec, nextAlarmTime);
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index c10703c..0cc8af1 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -13,13 +13,15 @@
// limitations under the License.
#include "src/anomaly/AnomalyTracker.h"
-#include "../metrics/metrics_test_helper.h"
#include <gtest/gtest.h>
#include <math.h>
#include <stdio.h>
+
#include <vector>
+#include "tests/statsd_test_util.h"
+
using namespace testing;
using android::sp;
using std::set;
@@ -147,7 +149,7 @@
std::shared_ptr<DimToValMap> bucket6 = MockBucket({{keyA, 2}});
// Start time with no events.
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
// Event from bucket #0 occurs.
@@ -158,7 +160,7 @@
// Adds past bucket #0
anomalyTracker.addPastBucket(bucket0, 0);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
@@ -172,7 +174,7 @@
// Adds past bucket #0 again. The sum does not change.
anomalyTracker.addPastBucket(bucket0, 0);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
@@ -185,7 +187,7 @@
// Adds past bucket #1.
anomalyTracker.addPastBucket(bucket1, 1);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
@@ -199,7 +201,7 @@
// Adds past bucket #1 again. Nothing changes.
anomalyTracker.addPastBucket(bucket1, 1);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
@@ -212,7 +214,7 @@
// Adds past bucket #2.
anomalyTracker.addPastBucket(bucket2, 2);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
@@ -225,7 +227,7 @@
// Adds bucket #3.
anomalyTracker.addPastBucket(bucket3, 3L);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
@@ -238,7 +240,7 @@
// Adds bucket #4.
anomalyTracker.addPastBucket(bucket4, 4);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
@@ -251,7 +253,7 @@
// Adds bucket #5.
anomalyTracker.addPastBucket(bucket5, 5);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 5LL);
@@ -292,7 +294,7 @@
int64_t eventTimestamp6 = bucketSizeNs * 27 + 3;
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, -1LL);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 9, bucket9, {}, {keyA, keyB, keyC, keyD}));
detectAndDeclareAnomalies(anomalyTracker, 9, bucket9, eventTimestamp1);
checkRefractoryTimes(anomalyTracker, eventTimestamp1, refractoryPeriodSec,
@@ -301,15 +303,15 @@
// Add past bucket #9
anomalyTracker.addPastBucket(bucket9, 9);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 16, bucket16, {keyB}, {keyA, keyC, keyD}));
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
detectAndDeclareAnomalies(anomalyTracker, 16, bucket16, eventTimestamp2);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
checkRefractoryTimes(anomalyTracker, eventTimestamp2, refractoryPeriodSec,
{{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
@@ -317,27 +319,27 @@
// Add past bucket #16
anomalyTracker.addPastBucket(bucket16, 16);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 18, bucket18, {keyB}, {keyA, keyC, keyD}));
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
// Within refractory period.
detectAndDeclareAnomalies(anomalyTracker, 18, bucket18, eventTimestamp3);
checkRefractoryTimes(anomalyTracker, eventTimestamp3, refractoryPeriodSec,
{{keyA, -1}, {keyB, eventTimestamp2}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
// Add past bucket #18
anomalyTracker.addPastBucket(bucket18, 18);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4);
@@ -347,11 +349,11 @@
// Add bucket #18 again. Nothing changes.
anomalyTracker.addPastBucket(bucket18, 18);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 20, bucket20, {keyB}, {keyA, keyC, keyD}));
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
detectAndDeclareAnomalies(anomalyTracker, 20, bucket20, eventTimestamp4 + 1);
@@ -362,12 +364,12 @@
// Add past bucket #20
anomalyTracker.addPastBucket(bucket20, 20);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 3LL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 25, bucket25, {}, {keyA, keyB, keyC, keyD}));
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
detectAndDeclareAnomalies(anomalyTracker, 25, bucket25, eventTimestamp5);
checkRefractoryTimes(anomalyTracker, eventTimestamp5, refractoryPeriodSec,
{{keyA, -1}, {keyB, eventTimestamp4}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
@@ -375,14 +377,14 @@
// Add past bucket #25
anomalyTracker.addPastBucket(bucket25, 25);
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyD), 1LL);
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {},
{keyA, keyB, keyC, keyD, keyE}));
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
{{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, -1}});
@@ -391,9 +393,9 @@
EXPECT_TRUE(detectAnomaliesPass(anomalyTracker, 28, bucket28, {keyE},
{keyA, keyB, keyC, keyD}));
EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
detectAndDeclareAnomalies(anomalyTracker, 28, bucket28, eventTimestamp6 + 7);
- EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
+ ASSERT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
checkRefractoryTimes(anomalyTracker, eventTimestamp6, refractoryPeriodSec,
{{keyA, -1}, {keyB, -1}, {keyC, -1}, {keyD, -1}, {keyE, eventTimestamp6 + 7}});
}
diff --git a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
index 6529d65..1d501fd 100644
--- a/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
@@ -24,6 +24,7 @@
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 e826a52..07b5311b 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "src/condition/SimpleConditionTracker.h"
+#include "stats_event.h"
#include "tests/statsd_test_util.h"
#include <gmock/gmock.h>
@@ -31,6 +32,8 @@
namespace os {
namespace statsd {
+namespace {
+
const ConfigKey kConfigKey(0, 12345);
const int ATTRIBUTION_NODE_FIELD_ID = 1;
@@ -57,23 +60,23 @@
return simplePredicate;
}
-void writeAttributionNodesToEvent(LogEvent* event, const std::vector<int> &uids) {
- std::vector<AttributionNodeInternal> nodes;
- for (size_t i = 0; i < uids.size(); ++i) {
- AttributionNodeInternal node;
- node.set_uid(uids[i]);
- nodes.push_back(node);
- }
- event->write(nodes); // attribution chain.
+void makeWakeLockEvent(LogEvent* logEvent, uint32_t atomId, uint64_t timestamp,
+ const vector<int>& uids, const string& wl, int acquire) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+ vector<std::string> tags(uids.size()); // vector of empty strings
+ writeAttribution(statsEvent, uids, tags);
+
+ AStatsEvent_writeString(statsEvent, wl.c_str());
+ AStatsEvent_writeInt32(statsEvent, acquire);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-void makeWakeLockEvent(
- LogEvent* event, const std::vector<int> &uids, const string& wl, int acquire) {
- writeAttributionNodesToEvent(event, uids);
- event->write(wl);
- event->write(acquire);
- event->init();
-}
+} // anonymous namespace
+
std::map<int64_t, HashableDimensionKey> getWakeLockQueryKey(
const Position position,
@@ -109,6 +112,114 @@
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"));
@@ -124,7 +235,9 @@
simplePredicate, trackerNameIndexMap);
EXPECT_FALSE(conditionTracker.isSliced());
- LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ // This event is not accessed in this test besides dimensions which is why this is okay.
+ // This is technically an invalid LogEvent because we do not call parseBuffer.
+ LogEvent event(/*uid=*/0, /*pid=*/0);
vector<MatchingState> matcherState;
matcherState.push_back(MatchingState::kNotMatched);
@@ -209,7 +322,9 @@
trackerNameIndexMap);
EXPECT_FALSE(conditionTracker.isSliced());
- LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ // This event is not accessed in this test besides dimensions which is why this is okay.
+ // This is technically an invalid LogEvent because we do not call parseBuffer.
+ LogEvent event(/*uid=*/0, /*pid=*/0);
// one matched start
vector<MatchingState> matcherState;
@@ -266,11 +381,7 @@
TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
std::vector<sp<ConditionTracker>> allConditions;
- for (Position position :
- { Position::FIRST, Position::LAST}) {
- vector<Matcher> dimensionInCondition;
- std::unordered_set<HashableDimensionKey> dimensionKeys;
-
+ for (Position position : {Position::FIRST, Position::LAST}) {
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
position);
@@ -287,8 +398,8 @@
std::vector<int> uids = {111, 222, 333};
- LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event, uids, "wl1", 1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/1);
// one matched start
vector<MatchingState> matcherState;
@@ -299,36 +410,33 @@
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
vector<bool> changedCache(1, false);
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+ conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
changedCache);
- if (position == Position::FIRST ||
- position == Position::LAST) {
- EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ if (position == Position::FIRST || position == Position::LAST) {
+ ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
} else {
- EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(changedCache[0]);
- if (position == Position::FIRST ||
- position == Position::LAST) {
- EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
+ if (position == Position::FIRST || position == Position::LAST) {
+ ASSERT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
} else {
- EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), uids.size());
+ EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(),
+ uids.size());
}
// Now test query
const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
- false, false,
- conditionCache, dimensionKeys);
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by this uid
- LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event2, uids, "wl2", 1);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/1);
matcherState.clear();
matcherState.push_back(MatchingState::kMatched);
matcherState.push_back(MatchingState::kNotMatched);
@@ -337,19 +445,18 @@
conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
changedCache);
EXPECT_FALSE(changedCache[0]);
- if (position == Position::FIRST ||
- position == Position::LAST) {
- EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ if (position == Position::FIRST || position == Position::LAST) {
+ ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
} else {
- EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
// wake lock 1 release
- LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event3, uids, "wl1", 0); // now release it.
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0);
matcherState.clear();
matcherState.push_back(MatchingState::kNotMatched);
matcherState.push_back(MatchingState::kMatched);
@@ -359,17 +466,16 @@
changedCache);
// nothing changes, because wake lock 2 is still held for this uid
EXPECT_FALSE(changedCache[0]);
- if (position == Position::FIRST ||
- position == Position::LAST) {
- EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ if (position == Position::FIRST || position == Position::LAST) {
+ ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
} else {
- EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+ ASSERT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
- LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event4, uids, "wl2", 0); // now release it.
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids, "wl2", /*acquire=*/0);
matcherState.clear();
matcherState.push_back(MatchingState::kNotMatched);
matcherState.push_back(MatchingState::kMatched);
@@ -377,21 +483,19 @@
changedCache[0] = false;
conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
changedCache);
- EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
EXPECT_TRUE(changedCache[0]);
- if (position == Position::FIRST ||
- position == Position::LAST) {
- EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
+ if (position == Position::FIRST || position == Position::LAST) {
+ ASSERT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
} else {
- EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), uids.size());
+ EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(),
+ uids.size());
}
// query again
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
- false, false,
- conditionCache, dimensionKeys);
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
@@ -399,12 +503,10 @@
TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
std::vector<sp<ConditionTracker>> allConditions;
- vector<Matcher> dimensionInCondition;
- std::unordered_set<HashableDimensionKey> dimensionKeys;
- SimplePredicate simplePredicate = getWakeLockHeldCondition(
- true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
- Position::ANY /* position */);
+ SimplePredicate simplePredicate =
+ getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/,
+ false /*slice output by uid*/, Position::ANY /* position */);
string conditionName = "WL_HELD";
unordered_map<int64_t, int> trackerNameIndexMap;
@@ -418,13 +520,13 @@
EXPECT_FALSE(conditionTracker.isSliced());
- std::vector<int> uid_list1 = {111, 1111, 11111};
+ std::vector<int> uids1 = {111, 1111, 11111};
string uid1_wl1 = "wl1_1";
- std::vector<int> uid_list2 = {222, 2222, 22222};
+ std::vector<int> uids2 = {222, 2222, 22222};
string uid2_wl1 = "wl2_1";
- LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event, uid_list1, uid1_wl1, 1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1, /*acquire=*/1);
// one matched start for uid1
vector<MatchingState> matcherState;
@@ -435,24 +537,23 @@
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
vector<bool> changedCache(1, false);
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+ conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
changedCache);
- EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
EXPECT_TRUE(changedCache[0]);
// Now test query
ConditionKey queryKey;
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
- true, true,
- conditionCache, dimensionKeys);
+ conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by this uid
- LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event2, uid_list2, uid2_wl1, 1);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1, /*acquire=*/1);
+
matcherState.clear();
matcherState.push_back(MatchingState::kMatched);
matcherState.push_back(MatchingState::kNotMatched);
@@ -463,8 +564,10 @@
EXPECT_FALSE(changedCache[0]);
// uid1 wake lock 1 release
- LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event3, uid_list1, uid1_wl1, 0); // now release it.
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids1, uid1_wl1,
+ /*release=*/0); // now release it.
+
matcherState.clear();
matcherState.push_back(MatchingState::kNotMatched);
matcherState.push_back(MatchingState::kMatched);
@@ -475,8 +578,9 @@
// nothing changes, because uid2 is still holding wl.
EXPECT_FALSE(changedCache[0]);
- LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event4, uid_list2, uid2_wl1, 0); // now release it.
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event4, /*atomId=*/1, /*timestamp=*/0, uids2, uid2_wl1,
+ /*acquire=*/0); // now release it.
matcherState.clear();
matcherState.push_back(MatchingState::kNotMatched);
matcherState.push_back(MatchingState::kMatched);
@@ -484,27 +588,21 @@
changedCache[0] = false;
conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
changedCache);
- EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
EXPECT_TRUE(changedCache[0]);
// query again
conditionCache[0] = ConditionState::kNotEvaluated;
- dimensionKeys.clear();
- conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
- true, true,
- conditionCache, dimensionKeys);
+ conditionTracker.isConditionMet(queryKey, allPredicates, true, conditionCache);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
TEST(SimpleConditionTrackerTest, TestStopAll) {
std::vector<sp<ConditionTracker>> allConditions;
- for (Position position :
- { Position::FIRST, Position::LAST }) {
- vector<Matcher> dimensionInCondition;
- std::unordered_set<HashableDimensionKey> dimensionKeys;
- SimplePredicate simplePredicate = getWakeLockHeldCondition(
- true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
- position);
+ for (Position position : {Position::FIRST, Position::LAST}) {
+ SimplePredicate simplePredicate =
+ getWakeLockHeldCondition(true /*nesting*/, true /*default to false*/,
+ true /*output slice by uid*/, position);
string conditionName = "WL_HELD_BY_UID3";
unordered_map<int64_t, int> trackerNameIndexMap;
@@ -516,11 +614,11 @@
0 /*condition tracker index*/, simplePredicate,
trackerNameIndexMap);
- std::vector<int> uid_list1 = {111, 1111, 11111};
- std::vector<int> uid_list2 = {222, 2222, 22222};
+ std::vector<int> uids1 = {111, 1111, 11111};
+ std::vector<int> uids2 = {222, 2222, 22222};
- LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event, uid_list1, "wl1", 1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event1, /*atomId=*/1, /*timestamp=*/0, uids1, "wl1", /*acquire=*/1);
// one matched start
vector<MatchingState> matcherState;
@@ -531,38 +629,36 @@
vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
vector<bool> changedCache(1, false);
- conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+ conditionTracker.evaluateCondition(event1, matcherState, allPredicates, conditionCache,
changedCache);
- if (position == Position::FIRST ||
- position == Position::LAST) {
- EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ if (position == Position::FIRST || position == Position::LAST) {
+ ASSERT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
} else {
- EXPECT_EQ(uid_list1.size(), conditionTracker.mSlicedConditionState.size());
+ ASSERT_EQ(uids1.size(), conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(changedCache[0]);
{
- if (position == Position::FIRST ||
- position == Position::LAST) {
- EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
+ if (position == Position::FIRST || position == Position::LAST) {
+ ASSERT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
} else {
- EXPECT_EQ(uid_list1.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size());
+ EXPECT_EQ(uids1.size(),
+ conditionTracker.getChangedToTrueDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
}
}
// Now test query
- const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
+ const auto queryKey = getWakeLockQueryKey(position, uids1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
- false, false,
- conditionCache, dimensionKeys);
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by uid2
- LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
- makeWakeLockEvent(&event2, uid_list2, "wl2", 1);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event2, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1);
+
matcherState.clear();
matcherState.push_back(MatchingState::kMatched);
matcherState.push_back(MatchingState::kNotMatched);
@@ -571,38 +667,34 @@
changedCache[0] = false;
conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
changedCache);
- if (position == Position::FIRST ||
- position == Position::LAST) {
- EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
+ if (position == Position::FIRST || position == Position::LAST) {
+ ASSERT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
} else {
- EXPECT_EQ(uid_list1.size() + uid_list2.size(),
- conditionTracker.mSlicedConditionState.size());
+ ASSERT_EQ(uids1.size() + uids2.size(), conditionTracker.mSlicedConditionState.size());
}
EXPECT_TRUE(changedCache[0]);
{
- if (position == Position::FIRST ||
- position == Position::LAST) {
- EXPECT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
+ if (position == Position::FIRST || position == Position::LAST) {
+ ASSERT_EQ(1UL, conditionTracker.getChangedToTrueDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
} else {
- EXPECT_EQ(uid_list2.size(), conditionTracker.getChangedToTrueDimensions(allConditions)->size());
+ EXPECT_EQ(uids2.size(),
+ conditionTracker.getChangedToTrueDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
}
}
-
// TEST QUERY
- const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
+ const auto queryKey2 = getWakeLockQueryKey(position, uids2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
- false, false,
- conditionCache, dimensionKeys);
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-
// stop all event
- LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids2, "wl2", /*acquire=*/1);
+
matcherState.clear();
matcherState.push_back(MatchingState::kNotMatched);
matcherState.push_back(MatchingState::kNotMatched);
@@ -613,32 +705,28 @@
conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
changedCache);
EXPECT_TRUE(changedCache[0]);
- EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ ASSERT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
{
if (position == Position::FIRST || position == Position::LAST) {
- EXPECT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size());
+ ASSERT_EQ(2UL, conditionTracker.getChangedToFalseDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
} else {
- EXPECT_EQ(uid_list1.size() + uid_list2.size(),
+ EXPECT_EQ(uids1.size() + uids2.size(),
conditionTracker.getChangedToFalseDimensions(allConditions)->size());
EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
}
}
// TEST QUERY
- const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
+ const auto queryKey3 = getWakeLockQueryKey(position, uids1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
- false, false,
- conditionCache, dimensionKeys);
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
// TEST QUERY
- const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
+ const auto queryKey4 = getWakeLockQueryKey(position, uids2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
- false, false,
- conditionCache, dimensionKeys);
+ conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
}
diff --git a/cmds/statsd/tests/condition/StateTracker_test.cpp b/cmds/statsd/tests/condition/StateTracker_test.cpp
deleted file mode 100644
index 9a66254..0000000
--- a/cmds/statsd/tests/condition/StateTracker_test.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/condition/StateTracker.h"
-#include "tests/statsd_test_util.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <stdio.h>
-#include <numeric>
-#include <vector>
-
-using std::map;
-using std::unordered_map;
-using std::vector;
-
-#ifdef __ANDROID__
-namespace android {
-namespace os {
-namespace statsd {
-
-const int kUidProcTag = 27;
-
-SimplePredicate getUidProcStatePredicate() {
- SimplePredicate simplePredicate;
- simplePredicate.set_start(StringToId("UidProcState"));
-
- simplePredicate.mutable_dimensions()->set_field(kUidProcTag);
- simplePredicate.mutable_dimensions()->add_child()->set_field(1);
- simplePredicate.mutable_dimensions()->add_child()->set_field(2);
-
- simplePredicate.set_count_nesting(false);
- return simplePredicate;
-}
-
-void makeUidProcStateEvent(int32_t uid, int32_t state, LogEvent* event) {
- event->write(uid);
- event->write(state);
- event->init();
-}
-
-TEST(StateTrackerTest, TestStateChange) {
- int uid1 = 111;
- int uid2 = 222;
-
- int state1 = 1001;
- int state2 = 1002;
- unordered_map<int64_t, int> trackerNameIndexMap;
- trackerNameIndexMap[StringToId("UidProcState")] = 0;
- vector<Matcher> primaryFields;
- primaryFields.push_back(getSimpleMatcher(kUidProcTag, 1));
- StateTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(),
- trackerNameIndexMap, primaryFields);
-
- LogEvent event(kUidProcTag, 0 /*timestamp*/);
- makeUidProcStateEvent(uid1, state1, &event);
-
- vector<MatchingState> matcherState;
- matcherState.push_back(MatchingState::kMatched);
- vector<sp<ConditionTracker>> allPredicates;
- vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
- vector<bool> changedCache(1, false);
-
- tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache);
- EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
- EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
- EXPECT_TRUE(changedCache[0]);
-
- changedCache[0] = false;
- conditionCache[0] = ConditionState::kNotEvaluated;
- tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache);
- EXPECT_EQ(0ULL, tracker.mLastChangedToTrueDimensions.size());
- EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
- EXPECT_FALSE(changedCache[0]);
-
- LogEvent event2(kUidProcTag, 0 /*timestamp*/);
- makeUidProcStateEvent(uid1, state2, &event2);
-
- changedCache[0] = false;
- conditionCache[0] = ConditionState::kNotEvaluated;
- tracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, changedCache);
- EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
- EXPECT_EQ(1ULL, tracker.mLastChangedToFalseDimensions.size());
- EXPECT_TRUE(changedCache[0]);
-
- LogEvent event3(kUidProcTag, 0 /*timestamp*/);
- makeUidProcStateEvent(uid2, state1, &event3);
- changedCache[0] = false;
- conditionCache[0] = ConditionState::kNotEvaluated;
- tracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, changedCache);
- EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
- EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
- EXPECT_TRUE(changedCache[0]);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp b/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
index 9ea0b81..93b2783 100644
--- a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
@@ -52,9 +52,9 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- EXPECT_EQ(2u, processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers.size());
+ ASSERT_EQ(2u, processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers.size());
auto alarmTracker1 = processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers[0];
auto alarmTracker2 = processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers[1];
@@ -68,7 +68,7 @@
const int64_t alarmFiredTimestampSec0 = alarmTimestampSec1 + 5;
auto alarmSet = processor->getPeriodicAlarmMonitor()->popSoonerThan(
static_cast<uint32_t>(alarmFiredTimestampSec0));
- EXPECT_EQ(1u, alarmSet.size());
+ ASSERT_EQ(1u, alarmSet.size());
processor->onPeriodicAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet);
EXPECT_EQ(alarmTimestampSec0, alarmTracker1->getAlarmTimestampSec());
EXPECT_EQ(alarmTimestampSec1 + 30 * 60, alarmTracker2->getAlarmTimestampSec());
@@ -77,7 +77,7 @@
const int64_t alarmFiredTimestampSec1 = alarmTimestampSec0 + 2 * 60 * 60 + 125;
alarmSet = processor->getPeriodicAlarmMonitor()->popSoonerThan(
static_cast<uint32_t>(alarmFiredTimestampSec1));
- EXPECT_EQ(2u, alarmSet.size());
+ ASSERT_EQ(2u, alarmSet.size());
processor->onPeriodicAlarmFired(alarmFiredTimestampSec1 * NS_PER_SEC, alarmSet);
EXPECT_EQ(alarmTimestampSec0 + 60 * 60 * 3, alarmTracker1->getAlarmTimestampSec());
EXPECT_EQ(alarmTimestampSec1 + 30 * 60 * 5, alarmTracker2->getAlarmTimestampSec());
diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
index c78d99e..af9436b 100644
--- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
+#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h"
#include "src/StatsLogProcessor.h"
#include "src/stats_log_util.h"
#include "tests/statsd_test_util.h"
@@ -28,7 +29,7 @@
namespace {
-StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) {
+StatsdConfig CreateStatsdConfig(int num_buckets, int threshold, int refractory_period_sec) {
StatsdConfig config;
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
@@ -39,14 +40,14 @@
countMetric->set_id(123456);
countMetric->set_what(wakelockAcquireMatcher.id());
*countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
countMetric->set_bucket(FIVE_MINUTES);
auto alert = config.add_alert();
alert->set_id(StringToId("alert"));
alert->set_metric_id(123456);
alert->set_num_buckets(num_buckets);
- alert->set_refractory_period_secs(10);
+ alert->set_refractory_period_secs(refractory_period_sec);
alert->set_trigger_if_sum_gt(threshold);
return config;
}
@@ -56,101 +57,115 @@
TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) {
const int num_buckets = 1;
const int threshold = 3;
- auto config = CreateStatsdConfig(num_buckets, threshold);
+ const int refractory_period_sec = 10;
+ auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
const uint64_t alert_id = config.alert(0).id();
- const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+ ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
- std::vector<AttributionNodeInternal> attributions3 = {
- CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
- std::vector<AttributionNodeInternal> attributions4 = {
- CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
- std::vector<AttributionNodeInternal> attributions5 = {
- CreateAttribution(222, "GMSCoreModule1") };
+ std::vector<int> attributionUids1 = {111};
+ std::vector<string> attributionTags1 = {"App1"};
+ std::vector<int> attributionUids2 = {111, 222};
+ std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
+ std::vector<int> attributionUids3 = {111, 333};
+ std::vector<string> attributionTags3 = {"App1", "App3"};
+ std::vector<int> attributionUids4 = {222, 333};
+ std::vector<string> attributionTags4 = {"GMSCoreModule1", "App3"};
+ std::vector<int> attributionUids5 = {222};
+ std::vector<string> attributionTags5 = {"GMSCoreModule1"};
- FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
Value((int32_t)111));
HashableDimensionKey whatKey1({fieldValue1});
MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
- FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ FieldValue fieldValue2(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
Value((int32_t)222));
HashableDimensionKey whatKey2({fieldValue2});
MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
- auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
+ auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+ attributionTags1, "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + 2);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids4, attributionTags4,
+ "wl2");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
- event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
+ "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 3);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids5, attributionTags5,
+ "wl2");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
- event = CreateAcquireWakelockEvent(attributions3, "wl1", bucketStartTimeNs + 4);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids3, attributionTags3,
+ "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 4);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids5, attributionTags5,
+ "wl2");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
// Fired alarm and refractory period end timestamp updated.
- event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 5);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 5, attributionUids1, attributionTags1,
+ "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 100);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 100, attributionUids1, attributionTags1,
+ "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids1,
+ attributionTags1, "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
+ attributionTags1, "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + bucketSizeNs + 1);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids4,
+ attributionTags4, "wl2");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
- event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 2);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids5,
+ attributionTags5, "wl2");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
- event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 3);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 3, attributionUids5,
+ attributionTags5, "wl2");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
- event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 4);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 4, attributionUids5,
+ attributionTags5, "wl2");
processor->OnLogEvent(event.get());
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
@@ -159,79 +174,213 @@
TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) {
const int num_buckets = 3;
const int threshold = 3;
- auto config = CreateStatsdConfig(num_buckets, threshold);
+ const int refractory_period_sec = 10;
+ auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
const uint64_t alert_id = config.alert(0).id();
- const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+ ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
- std::vector<AttributionNodeInternal> attributions3 = {
- CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
- std::vector<AttributionNodeInternal> attributions4 = {
- CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
- std::vector<AttributionNodeInternal> attributions5 = {
- CreateAttribution(222, "GMSCoreModule1") };
+ std::vector<int> attributionUids1 = {111};
+ std::vector<string> attributionTags1 = {"App1"};
+ std::vector<int> attributionUids2 = {111, 222};
+ std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
- FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
Value((int32_t)111));
HashableDimensionKey whatKey1({fieldValue1});
MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
- FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
- Value((int32_t)222));
- HashableDimensionKey whatKey2({fieldValue2});
- MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
-
- auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
+ auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+ attributionTags1, "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
+ "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Fired alarm and refractory period end timestamp updated.
- event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 4);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids1, attributionTags1,
+ "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
+ attributionTags1, "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids2,
+ attributionTags2, "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 1);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, attributionUids2,
+ attributionTags2, "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- event = CreateAcquireWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 2);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 2, attributionUids2,
+ attributionTags2, "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
}
+TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written) {
+ const int num_buckets = 1;
+ const int threshold = 0;
+ const int refractory_period_sec = 86400 * 365; // 1 year
+ auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
+ const int64_t alert_id = config.alert(0).id();
+
+ int64_t bucketStartTimeNs = 10000000000;
+
+ int configUid = 2000;
+ int64_t configId = 1000;
+ ConfigKey cfgKey(configUid, configId);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+ metadata::StatsMetadataList result;
+ int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
+ int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
+ processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result);
+
+ ASSERT_EQ(result.stats_metadata_size(), 0);
+}
+
+TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk) {
+ const int num_buckets = 1;
+ const int threshold = 0;
+ const int refractory_period_sec = 86400 * 365; // 1 year
+ auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
+ const int64_t alert_id = config.alert(0).id();
+
+ int64_t bucketStartTimeNs = 10000000000;
+
+ int configUid = 2000;
+ int64_t configId = 1000;
+ ConfigKey cfgKey(configUid, configId);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+ sp<AnomalyTracker> anomalyTracker =
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+ std::vector<int> attributionUids1 = {111};
+ std::vector<string> attributionTags1 = {"App1"};
+ std::vector<int> attributionUids2 = {111, 222};
+ std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
+
+ FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ Value((int32_t)111));
+ HashableDimensionKey whatKey1({fieldValue1});
+ MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
+
+ auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+ attributionTags1, "wl1");
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ metadata::StatsMetadataList result;
+ int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
+ int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
+ processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result);
+
+ metadata::StatsMetadata statsMetadata = result.stats_metadata(0);
+ ASSERT_EQ(result.stats_metadata_size(), 1);
+ EXPECT_EQ(statsMetadata.config_key().config_id(), configId);
+ EXPECT_EQ(statsMetadata.config_key().uid(), configUid);
+
+ metadata::AlertMetadata alertMetadata = statsMetadata.alert_metadata(0);
+ ASSERT_EQ(statsMetadata.alert_metadata_size(), 1);
+ EXPECT_EQ(alertMetadata.alert_id(), alert_id);
+ metadata::AlertDimensionKeyedData keyedData = alertMetadata.alert_dim_keyed_data(0);
+ ASSERT_EQ(alertMetadata.alert_dim_keyed_data_size(), 1);
+ EXPECT_EQ(keyedData.last_refractory_ends_sec(),
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) -
+ mockElapsedTimeNs / NS_PER_SEC +
+ mockWallClockNs / NS_PER_SEC);
+
+ metadata::MetricDimensionKey metadataDimKey = keyedData.dimension_key();
+ metadata::FieldValue dimKeyInWhat = metadataDimKey.dimension_key_in_what(0);
+ EXPECT_EQ(dimKeyInWhat.field().tag(), fieldValue1.mField.getTag());
+ EXPECT_EQ(dimKeyInWhat.field().field(), fieldValue1.mField.getField());
+ EXPECT_EQ(dimKeyInWhat.value_int(), fieldValue1.mValue.int_value);
+}
+
+TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk) {
+ const int num_buckets = 1;
+ const int threshold = 0;
+ const int refractory_period_sec = 86400 * 365; // 1 year
+ auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
+ const int64_t alert_id = config.alert(0).id();
+
+ int64_t bucketStartTimeNs = 10000000000;
+
+ int configUid = 2000;
+ int64_t configId = 1000;
+ ConfigKey cfgKey(configUid, configId);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+
+ sp<AnomalyTracker> anomalyTracker =
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+
+ std::vector<int> attributionUids1 = {111};
+ std::vector<string> attributionTags1 = {"App1"};
+ std::vector<int> attributionUids2 = {111, 222};
+ std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
+
+ FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ Value((int32_t)111));
+ HashableDimensionKey whatKey1({fieldValue1});
+ MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
+
+ auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+ attributionTags1, "wl1");
+ processor->OnLogEvent(event.get());
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+
+ int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
+ int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
+ processor->SaveMetadataToDisk(mockWallClockNs, mockElapsedTimeNs);
+
+ auto processor2 = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ int64_t mockElapsedTimeSinceBoot = 10 * NS_PER_SEC;
+ processor2->LoadMetadataFromDisk(mockWallClockNs, mockElapsedTimeSinceBoot);
+
+ sp<AnomalyTracker> anomalyTracker2 =
+ processor2->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+ EXPECT_EQ(anomalyTracker2->getRefractoryPeriodEndsSec(dimensionKey1) -
+ mockElapsedTimeSinceBoot / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) -
+ mockElapsedTimeNs / NS_PER_SEC);
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
index 50da9e2..95e30100 100644
--- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
@@ -45,7 +45,7 @@
auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
FieldMatcher dimensions = CreateAttributionUidDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock.
*holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
holdingWakelockPredicate.mutable_simple_predicate()->set_count_nesting(nesting);
@@ -57,7 +57,7 @@
durationMetric->set_condition(screenIsOffPredicate.id());
durationMetric->set_aggregation_type(aggregationType);
*durationMetric->mutable_dimensions_in_what() =
- CreateAttributionUidDimensions(android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
durationMetric->set_bucket(FIVE_MINUTES);
auto alert = config.add_alert();
@@ -69,26 +69,28 @@
return config;
}
-} // namespace
+std::vector<int> attributionUids1 = {111, 222};
+std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1"};
-std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
- CreateAttribution(222, "GMSCoreModule1")};
+std::vector<int> attributionUids2 = {111, 222};
+std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1"};
-std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"),
- CreateAttribution(222, "GMSCoreModule1")};
+std::vector<int> attributionUids3 = {222};
+std::vector<string> attributionTags3 = {"GMSCoreModule1"};
-std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1")};
-
-MetricDimensionKey dimensionKey(
- HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
- (int32_t)0x02010101), Value((int32_t)111))}),
- DEFAULT_DIMENSION_KEY);
+MetricDimensionKey dimensionKey1(
+ HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED,
+ (int32_t)0x02010101),
+ Value((int32_t)111))}),
+ DEFAULT_DIMENSION_KEY);
MetricDimensionKey dimensionKey2(
- HashableDimensionKey({FieldValue(Field(android::util::WAKELOCK_STATE_CHANGED,
+ HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED,
(int32_t)0x02010101), Value((int32_t)222))}),
DEFAULT_DIMENSION_KEY);
+} // namespace
+
TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) {
const int num_buckets = 1;
const uint64_t threshold_ns = NS_PER_SEC;
@@ -98,172 +100,176 @@
int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+ ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
auto screen_on_event = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1);
+ bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
auto screen_off_event = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 10);
+ bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
processor->OnLogEvent(screen_on_event.get());
processor->OnLogEvent(screen_off_event.get());
// Acquire wakelock wl1.
- auto acquire_event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 11);
+ auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1,
+ attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event.
- auto release_event = CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 101);
+ auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1,
+ attributionTags1, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire wakelock wl1 within bucket #0.
- acquire_event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 110);
+ acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids2,
+ attributionTags2, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Release wakelock wl1. One anomaly detected.
- release_event = CreateReleaseWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + NS_PER_SEC + 109);
+ release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 109,
+ attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire wakelock wl1.
- acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + NS_PER_SEC + 112);
+ acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 112,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
// Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the
// end of the refractory period.
- const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey);
+ const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1,
(uint32_t)alarmFiredTimestampSec0);
// Anomaly alarm fired.
auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
static_cast<uint32_t>(alarmFiredTimestampSec0));
- EXPECT_EQ(1u, alarmSet.size());
+ ASSERT_EQ(1u, alarmSet.size());
processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Release wakelock wl1.
- release_event = CreateReleaseWakelockEvent(
- attributions1, "wl1", alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1);
+ release_event =
+ CreateReleaseWakelockEvent(alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Within refractory period. No more anomaly detected.
EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire wakelock wl1.
- acquire_event = CreateAcquireWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11);
+ acquire_event =
+ CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11,
+ attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(acquire_event.get());
- const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey);
+ const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1);
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC,
(uint64_t)alarmFiredTimestampSec1);
// Release wakelock wl1.
- release_event = CreateReleaseWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10);
+ release_event =
+ CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10,
+ attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec +
- (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
- static_cast<uint32_t>(alarmFiredTimestampSec1));
- EXPECT_EQ(0u, alarmSet.size());
+ static_cast<uint32_t>(alarmFiredTimestampSec1));
+ ASSERT_EQ(0u, alarmSet.size());
// Acquire wakelock wl1 near the end of bucket #0.
- acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 2);
+ acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 2,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Release the event at early bucket #1.
- release_event = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1);
+ release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Anomaly detected when stopping the alarm. The refractory period does not change.
- EXPECT_EQ(refractory_period_sec +
- (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Condition changes to false.
- screen_on_event = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- bucketStartTimeNs + 2 * bucketSizeNs + 20);
+ screen_on_event =
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 20,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON);
processor->OnLogEvent(screen_on_event.get());
- EXPECT_EQ(refractory_period_sec +
- (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- acquire_event = CreateAcquireWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 30);
+ acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 30,
+ attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(acquire_event.get());
// The condition is false. Do not start the alarm.
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(refractory_period_sec +
- (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Condition turns true.
- screen_off_event = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC);
+ screen_off_event =
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
processor->OnLogEvent(screen_off_event.get());
EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Condition turns to false.
- screen_on_event = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1);
+ screen_on_event =
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON);
processor->OnLogEvent(screen_on_event.get());
// Condition turns to false. Cancelled the alarm.
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Detected one anomaly.
EXPECT_EQ(refractory_period_sec +
- (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Condition turns to true again.
- screen_off_event = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2);
+ screen_off_event =
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
processor->OnLogEvent(screen_off_event.get());
EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- release_event = CreateReleaseWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC);
+ release_event =
+ CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC,
+ attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(release_event.get());
EXPECT_EQ(refractory_period_sec +
- (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
}
TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) {
@@ -275,107 +281,115 @@
int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+ ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
auto screen_off_event = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1);
+ bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
processor->OnLogEvent(screen_off_event.get());
// Acquire wakelock "wc1" in bucket #0.
- auto acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1);
+ auto acquire_event =
+ CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Release wakelock "wc1" in bucket #0.
- auto release_event = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1);
+ auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire wakelock "wc1" in bucket #1.
- acquire_event = CreateAcquireWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
+ acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1,
+ attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- release_event = CreateReleaseWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 100);
+ release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 100,
+ attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire wakelock "wc2" in bucket #2.
- acquire_event = CreateAcquireWakelockEvent(
- attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
+ attributionUids3, attributionTags3, "wl2");
processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2,
+ EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2,
anomalyTracker->getAlarmTimestampSec(dimensionKey2));
EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
// Release wakelock "wc2" in bucket #2.
- release_event = CreateReleaseWakelockEvent(
- attributions3, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC);
+ release_event =
+ CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC,
+ attributionUids3, attributionTags3, "wl2");
processor->OnLogEvent(release_event.get());
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
EXPECT_EQ(refractory_period_sec +
- (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC,
+ (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC,
anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
// Acquire wakelock "wc1" in bucket #2.
- acquire_event = CreateAcquireWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC);
+ acquire_event =
+ CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC,
+ attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Release wakelock "wc1" in bucket #2.
- release_event = CreateReleaseWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC);
+ release_event =
+ CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC,
+ attributionUids2, attributionTags2, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec +
- (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) /
+ NS_PER_SEC +
+ 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- acquire_event = CreateAcquireWakelockEvent(
- attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4);
+ acquire_event =
+ CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4,
+ attributionUids3, attributionTags3, "wl2");
processor->OnLogEvent(acquire_event.get());
- acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5);
+ acquire_event =
+ CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
anomalyTracker->getAlarmTimestampSec(dimensionKey2));
- release_event = CreateReleaseWakelockEvent(
- attributions3, "wl2", bucketStartTimeNs + 6 * bucketSizeNs + 2);
+ release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 2,
+ attributionUids3, attributionTags3, "wl2");
processor->OnLogEvent(release_event.get());
- release_event = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 6 * bucketSizeNs + 6);
+ release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 6,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2));
// The buckets are not messed up across dimensions. Only one dimension has anomaly triggered.
- EXPECT_EQ(refractory_period_sec +
- (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ EXPECT_EQ(refractory_period_sec + (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC +
+ 1,
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
}
TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) {
@@ -384,7 +398,7 @@
auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false);
int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
const uint64_t alert_id = config.alert(0).id();
const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC;
@@ -392,89 +406,94 @@
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
+ ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
sp<AnomalyTracker> anomalyTracker =
- processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
+ processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
auto screen_off_event = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 1);
+ bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
processor->OnLogEvent(screen_off_event.get());
// Acquire wakelock "wc1" in bucket #0.
- auto acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 100);
+ auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 100,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Acquire the wakelock "wc1" again.
- acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1);
+ acquire_event =
+ CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
// The alarm does not change.
EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
- EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
+ EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// Anomaly alarm fired late.
const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC;
auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan(
static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC));
- EXPECT_EQ(1u, alarmSet.size());
+ ASSERT_EQ(1u, alarmSet.size());
processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet);
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs - 100);
+ acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- auto release_event = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 1);
+ auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
// Within the refractory period. No anomaly.
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
// A new wakelock, but still within refractory period.
- acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC);
+ acquire_event =
+ CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- release_event = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC);
+ release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC,
+ attributionUids1, attributionTags1, "wl1");
// Still in the refractory period. No anomaly.
processor->OnLogEvent(release_event.get());
EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC,
- anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey));
+ anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
- acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5);
+ acquire_event =
+ CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- release_event = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4);
+ release_event =
+ CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(release_event.get());
- EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1));
- acquire_event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3);
+ acquire_event =
+ CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3,
+ attributionUids1, attributionTags1, "wl1");
processor->OnLogEvent(acquire_event.get());
EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC,
- anomalyTracker->getAlarmTimestampSec(dimensionKey));
+ anomalyTracker->getAlarmTimestampSec(dimensionKey1));
}
#else
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 3382525..4c2caa9 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -48,22 +48,60 @@
countMetric->set_what(wakelockAcquireMatcher.id());
*countMetric->mutable_dimensions_in_what() =
CreateAttributionUidAndTagDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {position});
+ util::WAKELOCK_STATE_CHANGED, {position});
countMetric->set_bucket(FIVE_MINUTES);
return config;
}
+// GMS core node is in the middle.
+std::vector<int> attributionUids1 = {111, 222, 333};
+std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "App3"};
+
+// GMS core node is the last one.
+std::vector<int> attributionUids2 = {111, 333, 222};
+std::vector<string> attributionTags2 = {"App1", "App3", "GMSCoreModule1"};
+
+// GMS core node is the first one.
+std::vector<int> attributionUids3 = {222, 333};
+std::vector<string> attributionTags3 = {"GMSCoreModule1", "App3"};
+
+// Single GMS core node.
+std::vector<int> attributionUids4 = {222};
+std::vector<string> attributionTags4 = {"GMSCoreModule1"};
+
+// GMS core has another uid.
+std::vector<int> attributionUids5 = {111, 444, 333};
+std::vector<string> attributionTags5 = {"App1", "GMSCoreModule2", "App3"};
+
+// Multiple GMS core nodes.
+std::vector<int> attributionUids6 = {444, 222};
+std::vector<string> attributionTags6 = {"GMSCoreModule2", "GMSCoreModule1"};
+
+// No GMS core nodes
+std::vector<int> attributionUids7 = {111, 333};
+std::vector<string> attributionTags7 = {"App1", "App3"};
+
+std::vector<int> attributionUids8 = {111};
+std::vector<string> attributionTags8 = {"App1"};
+
+// GMS core node with isolated uid.
+const int isolatedUid = 666;
+std::vector<int> attributionUids9 = {isolatedUid};
+std::vector<string> attributionTags9 = {"GMSCoreModule3"};
+
+std::vector<int> attributionUids10 = {isolatedUid};
+std::vector<string> attributionTags10 = {"GMSCoreModule1"};
+
} // namespace
TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid) {
auto config = CreateStatsdConfig(Position::FIRST);
int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
// Here it assumes that GMS core has two uids.
@@ -74,70 +112,34 @@
String16("APP3")},
{String16(""), String16(""), String16(""), String16("")});
- // GMS core node is in the middle.
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
- CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(333, "App3")};
-
- // GMS core node is the last one.
- std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"),
- CreateAttribution(333, "App3"),
- CreateAttribution(222, "GMSCoreModule1")};
-
- // GMS core node is the first one.
- std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(333, "App3")};
-
- // Single GMS core node.
- std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")};
-
- // GMS core has another uid.
- std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"),
- CreateAttribution(444, "GMSCoreModule2"),
- CreateAttribution(333, "App3")};
-
- // Multiple GMS core nodes.
- std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"),
- CreateAttribution(222, "GMSCoreModule1")};
-
- // No GMS core nodes.
- std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"),
- CreateAttribution(333, "App3")};
- std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")};
-
- // GMS core node with isolated uid.
- const int isolatedUid = 666;
- std::vector<AttributionNodeInternal> attributions9 = {
- CreateAttribution(isolatedUid, "GMSCoreModule3")};
-
std::vector<std::unique_ptr<LogEvent>> events;
// Events 1~4 are in the 1st bucket.
- events.push_back(CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 2));
- events.push_back(CreateAcquireWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + 200));
- events.push_back(CreateAcquireWakelockEvent(
- attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1));
- events.push_back(CreateAcquireWakelockEvent(
- attributions4, "wl1", bucketStartTimeNs + bucketSizeNs));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+ attributionTags1, "wl1"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2,
+ attributionTags2, "wl1"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1,
+ attributionUids3, attributionTags3, "wl1"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4,
+ attributionTags4, "wl1"));
// Events 5~8 are in the 3rd bucket.
- events.push_back(CreateAcquireWakelockEvent(
- attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1));
- events.push_back(CreateAcquireWakelockEvent(
- attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100));
- events.push_back(CreateAcquireWakelockEvent(
- attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2));
- events.push_back(CreateAcquireWakelockEvent(
- attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs));
- events.push_back(CreateAcquireWakelockEvent(
- attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1));
- events.push_back(CreateAcquireWakelockEvent(
- attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100));
- events.push_back(CreateIsolatedUidChangedEvent(
- isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1));
- events.push_back(CreateIsolatedUidChangedEvent(
- isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
+ attributionUids5, attributionTags5, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
+ attributionUids6, attributionTags6, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2,
+ attributionUids7, attributionTags7, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs,
+ attributionUids8, attributionTags8, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1,
+ attributionUids9, attributionTags9, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100,
+ attributionUids9, attributionTags9, "wl2"));
+ events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222,
+ isolatedUid, true /*is_create*/));
+ events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222,
+ isolatedUid, false /*is_create*/));
sortLogEventsByTimestamp(&events);
@@ -146,37 +148,37 @@
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
StatsLogReport::CountMetricDataWrapper countMetrics;
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- EXPECT_EQ(countMetrics.data_size(), 4);
+ ASSERT_EQ(countMetrics.data_size(), 4);
auto data = countMetrics.data(0);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 111,
- "App1");
- EXPECT_EQ(data.bucket_info_size(), 2);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+ util::WAKELOCK_STATE_CHANGED, 111, "App1");
+ ASSERT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).count(), 2);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).count(), 1);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
+ bucketStartTimeNs + 2 * bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
data = countMetrics.data(1);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222,
- "GMSCoreModule1");
- EXPECT_EQ(data.bucket_info_size(), 2);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+ util::WAKELOCK_STATE_CHANGED, 222,
+ "GMSCoreModule1");
+ ASSERT_EQ(data.bucket_info_size(), 2);
EXPECT_EQ(data.bucket_info(0).count(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
@@ -185,33 +187,34 @@
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
data = countMetrics.data(2);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222,
- "GMSCoreModule3");
- EXPECT_EQ(data.bucket_info_size(), 1);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+ util::WAKELOCK_STATE_CHANGED, 222,
+ "GMSCoreModule3");
+ ASSERT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
+ bucketStartTimeNs + 3 * bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 4 * bucketSizeNs);
data = countMetrics.data(3);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 444,
- "GMSCoreModule2");
- EXPECT_EQ(data.bucket_info_size(), 1);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+ util::WAKELOCK_STATE_CHANGED, 444,
+ "GMSCoreModule2");
+ ASSERT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
+ bucketStartTimeNs + 2 * bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 3 * bucketSizeNs);
}
TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain) {
auto config = CreateStatsdConfig(Position::ALL);
int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
// Here it assumes that GMS core has two uids.
@@ -222,70 +225,34 @@
String16("APP3")},
{String16(""), String16(""), String16(""), String16("")});
- // GMS core node is in the middle.
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
- CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(333, "App3")};
-
- // GMS core node is the last one.
- std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"),
- CreateAttribution(333, "App3"),
- CreateAttribution(222, "GMSCoreModule1")};
-
- // GMS core node is the first one.
- std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(333, "App3")};
-
- // Single GMS core node.
- std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")};
-
- // GMS core has another uid.
- std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"),
- CreateAttribution(444, "GMSCoreModule2"),
- CreateAttribution(333, "App3")};
-
- // Multiple GMS core nodes.
- std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"),
- CreateAttribution(222, "GMSCoreModule1")};
-
- // No GMS core nodes.
- std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"),
- CreateAttribution(333, "App3")};
- std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")};
-
- // GMS core node with isolated uid.
- const int isolatedUid = 666;
- std::vector<AttributionNodeInternal> attributions9 = {
- CreateAttribution(isolatedUid, "GMSCoreModule1")};
-
std::vector<std::unique_ptr<LogEvent>> events;
// Events 1~4 are in the 1st bucket.
- events.push_back(CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 2));
- events.push_back(CreateAcquireWakelockEvent(
- attributions2, "wl1", bucketStartTimeNs + 200));
- events.push_back(CreateAcquireWakelockEvent(
- attributions3, "wl1", bucketStartTimeNs + bucketSizeNs - 1));
- events.push_back(CreateAcquireWakelockEvent(
- attributions4, "wl1", bucketStartTimeNs + bucketSizeNs));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+ attributionTags1, "wl1"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 200, attributionUids2,
+ attributionTags2, "wl1"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1,
+ attributionUids3, attributionTags3, "wl1"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs, attributionUids4,
+ attributionTags4, "wl1"));
// Events 5~8 are in the 3rd bucket.
- events.push_back(CreateAcquireWakelockEvent(
- attributions5, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 1));
- events.push_back(CreateAcquireWakelockEvent(
- attributions6, "wl2", bucketStartTimeNs + 2 * bucketSizeNs + 100));
- events.push_back(CreateAcquireWakelockEvent(
- attributions7, "wl2", bucketStartTimeNs + 3 * bucketSizeNs - 2));
- events.push_back(CreateAcquireWakelockEvent(
- attributions8, "wl2", bucketStartTimeNs + 3 * bucketSizeNs));
- events.push_back(CreateAcquireWakelockEvent(
- attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 1));
- events.push_back(CreateAcquireWakelockEvent(
- attributions9, "wl2", bucketStartTimeNs + 3 * bucketSizeNs + 100));
- events.push_back(CreateIsolatedUidChangedEvent(
- isolatedUid, 222, true/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs - 1));
- events.push_back(CreateIsolatedUidChangedEvent(
- isolatedUid, 222, false/* is_create*/, bucketStartTimeNs + 3 * bucketSizeNs + 10));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1,
+ attributionUids5, attributionTags5, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
+ attributionUids6, attributionTags6, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - 2,
+ attributionUids7, attributionTags7, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs,
+ attributionUids8, attributionTags8, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1,
+ attributionUids10, attributionTags10, "wl2"));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 100,
+ attributionUids10, attributionTags10, "wl2"));
+ events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs - 1, 222,
+ isolatedUid, true /*is_create*/));
+ events.push_back(CreateIsolatedUidChangedEvent(bucketStartTimeNs + 3 * bucketSizeNs + 10, 222,
+ isolatedUid, false /*is_create*/));
sortLogEventsByTimestamp(&events);
@@ -294,124 +261,109 @@
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
StatsLogReport::CountMetricDataWrapper countMetrics;
sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- EXPECT_EQ(countMetrics.data_size(), 6);
+ ASSERT_EQ(countMetrics.data_size(), 6);
auto data = countMetrics.data(0);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
- EXPECT_EQ(2, data.bucket_info_size());
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(),
+ util::WAKELOCK_STATE_CHANGED, 222,
+ "GMSCoreModule1");
+ ASSERT_EQ(2, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
- data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_EQ(1, data.bucket_info(1).count());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
data.bucket_info(1).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs,
- data.bucket_info(1).end_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
data = countMetrics.data(1);
- ValidateUidDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
- ValidateUidDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
- EXPECT_EQ(data.bucket_info_size(), 1);
+ ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 222);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+ util::WAKELOCK_STATE_CHANGED, 222,
+ "GMSCoreModule1");
+ ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 333);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+ util::WAKELOCK_STATE_CHANGED, 333, "App3");
+ ASSERT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
data = countMetrics.data(2);
- ValidateUidDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2");
- ValidateUidDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
- EXPECT_EQ(data.bucket_info_size(), 1);
+ ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 444);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+ util::WAKELOCK_STATE_CHANGED, 444,
+ "GMSCoreModule2");
+ ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 222);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+ util::WAKELOCK_STATE_CHANGED, 222,
+ "GMSCoreModule1");
+ ASSERT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
- data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(3);
- ValidateUidDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
- ValidateUidDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
- ValidateUidDimension(
- data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
- EXPECT_EQ(data.bucket_info_size(), 1);
+ ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 111);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+ util::WAKELOCK_STATE_CHANGED, 111, "App1");
+ ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 222);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+ util::WAKELOCK_STATE_CHANGED, 222,
+ "GMSCoreModule1");
+ ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 333);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2,
+ util::WAKELOCK_STATE_CHANGED, 333, "App3");
+ ASSERT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(bucketStartTimeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
- data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(4);
- ValidateUidDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
- ValidateUidDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
- ValidateUidDimension(
- data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 222, "GMSCoreModule1");
- EXPECT_EQ(data.bucket_info_size(), 1);
+ ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 111);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+ util::WAKELOCK_STATE_CHANGED, 111, "App1");
+ ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 333);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+ util::WAKELOCK_STATE_CHANGED, 333, "App3");
+ ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 222);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2,
+ util::WAKELOCK_STATE_CHANGED, 222,
+ "GMSCoreModule1");
+ ASSERT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(bucketStartTimeNs,
- data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
- data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(5);
- ValidateUidDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 0, android::util::WAKELOCK_STATE_CHANGED, 111, "App1");
- ValidateUidDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 1, android::util::WAKELOCK_STATE_CHANGED, 444, "GMSCoreModule2");
- ValidateUidDimension(
- data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333);
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_what(), 2, android::util::WAKELOCK_STATE_CHANGED, 333, "App3");
- EXPECT_EQ(data.bucket_info_size(), 1);
+ ValidateUidDimension(data.dimensions_in_what(), 0, util::WAKELOCK_STATE_CHANGED, 111);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 0,
+ util::WAKELOCK_STATE_CHANGED, 111, "App1");
+ ValidateUidDimension(data.dimensions_in_what(), 1, util::WAKELOCK_STATE_CHANGED, 444);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 1,
+ util::WAKELOCK_STATE_CHANGED, 444,
+ "GMSCoreModule2");
+ ValidateUidDimension(data.dimensions_in_what(), 2, util::WAKELOCK_STATE_CHANGED, 333);
+ ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), 2,
+ util::WAKELOCK_STATE_CHANGED, 333, "App3");
+ ASSERT_EQ(data.bucket_info_size(), 1);
EXPECT_EQ(data.bucket_info(0).count(), 1);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
- data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
}
#else
diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
index b98dc60..0bce0ba 100644
--- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
@@ -39,7 +39,7 @@
countMetric->set_id(123456);
countMetric->set_what(wakelockAcquireMatcher.id());
*countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
countMetric->set_bucket(FIVE_MINUTES);
auto alert = config.add_alert();
@@ -64,40 +64,46 @@
const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
+ std::vector<int> attributionUids1 = {111};
+ std::vector<string> attributionTags1 = {"App1"};
- FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
Value((int32_t)111));
HashableDimensionKey whatKey1({fieldValue1});
MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
- FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
+ FieldValue fieldValue2(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
Value((int32_t)222));
HashableDimensionKey whatKey2({fieldValue2});
MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
- auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
+ auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+ attributionTags1, "wl1");
processor->OnLogEvent(event.get());
- event = CreateAcquireWakelockEvent(attributions1, "wl2", bucketStartTimeNs + bucketSizeNs + 2);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids1,
+ attributionTags1, "wl2");
processor->OnLogEvent(event.get());
- event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 25 * bucketSizeNs + 2);
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * bucketSizeNs + 2, attributionUids1,
+ attributionTags1, "wl1");
processor->OnLogEvent(event.get());
EXPECT_EQ((int64_t)(bucketStartTimeNs + 25 * bucketSizeNs + 2 + 2 * 3600 * NS_PER_SEC),
processor->mMetricsManagers.begin()->second->getTtlEndNs());
-}
+ // Clear the data stored on disk as a result of the ttl.
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 25 * bucketSizeNs + 3, false, true,
+ ADB_DUMP, FAST, &buffer);
+}
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
new file mode 100644
index 0000000..04eb400
--- /dev/null
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -0,0 +1,901 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/state/StateManager.h"
+#include "src/state/StateTracker.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+/**
+ * Tests the initial condition and condition after the first log events for
+ * count metrics with either a combination condition or simple condition.
+ *
+ * Metrics should be initialized with condition kUnknown (given that the
+ * predicate is using the default InitialValue of UNKNOWN). The condition should
+ * be updated to either kFalse or kTrue if a condition event is logged for all
+ * children conditions.
+ */
+TEST(CountMetricE2eTest, TestInitialConditionChanges) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
+
+ auto syncStartMatcher = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = syncStartMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateBatteryStateNoneMatcher();
+ *config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
+
+ auto screenOnPredicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = screenOnPredicate;
+
+ auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate();
+ *config.add_predicate() = deviceUnpluggedPredicate;
+
+ auto screenOnOnBatteryPredicate = config.add_predicate();
+ screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate"));
+ screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
+ addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate);
+ addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate);
+
+ // CountSyncStartWhileScreenOnOnBattery (CombinationCondition)
+ CountMetric* countMetric1 = config.add_count_metric();
+ countMetric1->set_id(StringToId("CountSyncStartWhileScreenOnOnBattery"));
+ countMetric1->set_what(syncStartMatcher.id());
+ countMetric1->set_condition(screenOnOnBatteryPredicate->id());
+ countMetric1->set_bucket(FIVE_MINUTES);
+
+ // CountSyncStartWhileOnBattery (SimpleCondition)
+ CountMetric* countMetric2 = config.add_count_metric();
+ countMetric2->set_id(StringToId("CountSyncStartWhileOnBatterySliceScreen"));
+ countMetric2->set_what(syncStartMatcher.id());
+ countMetric2->set_condition(deviceUnpluggedPredicate.id());
+ countMetric2->set_bucket(FIVE_MINUTES);
+
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(2, metricsManager->mAllMetricProducers.size());
+
+ sp<MetricProducer> metricProducer1 = metricsManager->mAllMetricProducers[0];
+ sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[1];
+
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
+
+ auto screenOnEvent =
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 30, android::view::DISPLAY_STATE_ON);
+ processor->OnLogEvent(screenOnEvent.get());
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
+
+ auto pluggedUsbEvent = CreateBatteryStateChangedEvent(
+ bucketStartTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
+ processor->OnLogEvent(pluggedUsbEvent.get());
+ EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition);
+ EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition);
+
+ auto pluggedNoneEvent = CreateBatteryStateChangedEvent(
+ bucketStartTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE);
+ processor->OnLogEvent(pluggedNoneEvent.get());
+ EXPECT_EQ(ConditionState::kTrue, metricProducer1->mCondition);
+ EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition);
+}
+
+/**
+* Test a count metric that has one slice_by_state with no primary fields.
+*
+* Once the CountMetricProducer is initialized, it has one atom id in
+* mSlicedStateAtoms and no entries in mStateGroupMap.
+
+* One StateTracker tracks the state atom, and it has one listener which is the
+* CountMetricProducer that was initialized.
+*/
+TEST(CountMetricE2eTest, TestSlicedState) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto syncStartMatcher = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = syncStartMatcher;
+
+ auto state = CreateScreenState();
+ *config.add_state() = state;
+
+ // Create count metric that slices by screen state.
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(syncStartMatcher.id());
+ countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+ countMetric->add_slice_by_state(state.id());
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ // Check that CountMetricProducer was initialized correctly.
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
+ ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
+
+ // Check that StateTrackers were initialized correctly.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+ /*
+ bucket #1 bucket #2
+ | 1 2 3 4 5 6 7 8 9 10 (minutes)
+ |-----------------------------|-----------------------------|--
+ x x x x x x (syncStartEvents)
+ | | (ScreenIsOnEvent)
+ | | (ScreenIsOffEvent)
+ | (ScreenDozeEvent)
+ */
+ // Initialize log events - first bucket.
+ std::vector<int> attributionUids1 = {123};
+ std::vector<string> attributionTags1 = {"App1"};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 50 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 1:00
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 75 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 1:25
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 150 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 2:40
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 200 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:30
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 4:20
+
+ // Initialize log events - second bucket.
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 350 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 6:00
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 6:50
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 450 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 7:40
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 475 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 8:05
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 500 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN)); // 8:30
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 520 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 8:50
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(3, countMetrics.data_size());
+
+ // For each CountMetricData, check StateValue info is correct and buckets
+ // have correct counts.
+ auto data = countMetrics.data(0);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+
+ data = countMetrics.data(1);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(2, data.bucket_info(1).count());
+
+ data = countMetrics.data(2);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(1, data.bucket_info(1).count());
+}
+
+/**
+ * Test a count metric that has one slice_by_state with a mapping and no
+ * primary fields.
+ *
+ * Once the CountMetricProducer is initialized, it has one atom id in
+ * mSlicedStateAtoms and has one entry per state value in mStateGroupMap.
+ *
+ * One StateTracker tracks the state atom, and it has one listener which is the
+ * CountMetricProducer that was initialized.
+ */
+TEST(CountMetricE2eTest, TestSlicedStateWithMap) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto syncStartMatcher = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = syncStartMatcher;
+
+ int64_t screenOnId = 4444;
+ int64_t screenOffId = 9876;
+ auto state = CreateScreenStateWithOnOffMap(screenOnId, screenOffId);
+ *config.add_state() = state;
+
+ // Create count metric that slices by screen state with on/off map.
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(syncStartMatcher.id());
+ countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+ countMetric->add_slice_by_state(state.id());
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were initialized correctly.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+ // Check that CountMetricProducer was initialized correctly.
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
+ ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+ StateMap map = state.map();
+ for (auto group : map.group()) {
+ for (auto value : group.value()) {
+ EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
+ group.group_id());
+ }
+ }
+
+ /*
+ bucket #1 bucket #2
+ | 1 2 3 4 5 6 7 8 9 10 (minutes)
+ |-----------------------------|-----------------------------|--
+ x x x x x x x x x (syncStartEvents)
+ -----------------------------------------------------------SCREEN_OFF events
+ | | (ScreenStateOffEvent = 1)
+ | | (ScreenStateDozeEvent = 3)
+ | (ScreenStateDozeSuspendEvent =
+ 4)
+ -----------------------------------------------------------SCREEN_ON events
+ | | (ScreenStateOnEvent = 2)
+ | (ScreenStateVrEvent = 5)
+ | (ScreenStateOnSuspendEvent = 6)
+ */
+ // Initialize log events - first bucket.
+ std::vector<int> attributionUids1 = {123};
+ std::vector<string> attributionTags1 = {"App1"};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 20 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 0:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 30 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 0:40
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 1:10
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 90 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 120 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 2:10
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 150 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:40
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 180 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:10
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 3:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 210 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 250 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 4:20
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 280 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:50
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 285 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 4:55
+
+ // Initialize log events - second bucket.
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 360 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 6:10
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 390 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 430 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND)); // 7:20
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 440 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 7:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 540 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 9:10
+ events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 570 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "sync_name")); // 9:40
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(3, countMetrics.data_size());
+
+ // For each CountMetricData, check StateValue info is correct and buckets
+ // have correct counts.
+ auto data = countMetrics.data(0);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+
+ data = countMetrics.data(1);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+ EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(1, data.bucket_info(1).count());
+
+ data = countMetrics.data(2);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+ EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(4, data.bucket_info(0).count());
+ EXPECT_EQ(2, data.bucket_info(1).count());
+}
+
+/**
+* Test a count metric that has one slice_by_state with a primary field.
+
+* Once the CountMetricProducer is initialized, it should have one
+* MetricStateLink stored. State querying using a non-empty primary key
+* should also work as intended.
+*/
+TEST(CountMetricE2eTest, TestSlicedStateWithPrimaryFields) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto appCrashMatcher =
+ CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
+ *config.add_atom_matcher() = appCrashMatcher;
+
+ auto state = CreateUidProcessState();
+ *config.add_state() = state;
+
+ // Create count metric that slices by uid process state.
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(appCrashMatcher.id());
+ countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+ countMetric->add_slice_by_state(state.id());
+ MetricStateLink* stateLink = countMetric->add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/});
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were initialized correctly.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+ // Check that CountMetricProducer was initialized correctly.
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID);
+ ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
+ ASSERT_EQ(metricProducer->mMetric2StateLinks.size(), 1);
+
+ /*
+ NOTE: "1" or "2" represents the uid associated with the state/app crash event
+ bucket #1 bucket #2
+ | 1 2 3 4 5 6 7 8 9 10
+ |------------------------|-------------------------|--
+ 1 1 1 1 1 2 1 1 2 (AppCrashEvents)
+ -----------------------------------------------------PROCESS STATE events
+ 1 2 (TopEvent = 1002)
+ 1 1 (ForegroundServiceEvent = 1003)
+ 2 (ImportantBackgroundEvent = 1006)
+ 1 1 1 (ImportantForegroundEvent = 1005)
+
+ Based on the diagram above, an AppCrashEvent querying for process state value would return:
+ - StateTracker::kStateUnknown
+ - Important foreground
+ - Top
+ - Important foreground
+ - Foreground service
+ - Top (both the app crash and state still have matching uid = 2)
+
+ - Foreground service
+ - Foreground service
+ - Important background
+ */
+ // Initialize log events - first bucket.
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:40
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 210 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 3:40
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55
+
+ // Initialize log events - second bucket.
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 390 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:40
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 430 * NS_PER_SEC, 2 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:20
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 440 * NS_PER_SEC, 1 /*uid*/)); // 7:30
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(5, countMetrics.data_size());
+
+ // For each CountMetricData, check StateValue info is correct and buckets
+ // have correct counts.
+ auto data = countMetrics.data(0);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+
+ data = countMetrics.data(1);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::PROCESS_STATE_TOP, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(2, data.bucket_info(0).count());
+
+ data = countMetrics.data(2);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+ EXPECT_EQ(2, data.bucket_info(1).count());
+
+ data = countMetrics.data(3);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(2, data.bucket_info(0).count());
+
+ data = countMetrics.data(4);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+}
+
+TEST(CountMetricE2eTest, TestMultipleSlicedStates) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto appCrashMatcher =
+ CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
+ *config.add_atom_matcher() = appCrashMatcher;
+
+ int64_t screenOnId = 4444;
+ int64_t screenOffId = 9876;
+ auto state1 = CreateScreenStateWithOnOffMap(screenOnId, screenOffId);
+ *config.add_state() = state1;
+ auto state2 = CreateUidProcessState();
+ *config.add_state() = state2;
+
+ // Create count metric that slices by screen state with on/off map and
+ // slices by uid process state.
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(appCrashMatcher.id());
+ countMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+ countMetric->add_slice_by_state(state1.id());
+ countMetric->add_slice_by_state(state2.id());
+ MetricStateLink* stateLink = countMetric->add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /*uid*/});
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were properly initialized.
+ EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+ // Check that CountMetricProducer was initialized correctly.
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 2);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), UID_PROCESS_STATE_ATOM_ID);
+ ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1);
+ ASSERT_EQ(metricProducer->mMetric2StateLinks.size(), 1);
+
+ StateMap map = state1.map();
+ for (auto group : map.group()) {
+ for (auto value : group.value()) {
+ EXPECT_EQ(metricProducer->mStateGroupMap.at(SCREEN_STATE_ATOM_ID).at(value),
+ group.group_id());
+ }
+ }
+
+ /*
+ bucket #1 bucket #2
+ | 1 2 3 4 5 6 7 8 9 10 (minutes)
+ |------------------------|------------------------|--
+ 1 1 1 1 1 2 1 1 2 (AppCrashEvents)
+ ---------------------------------------------------SCREEN_OFF events
+ | | (ScreenOffEvent = 1)
+ | | (ScreenDozeEvent = 3)
+ ---------------------------------------------------SCREEN_ON events
+ | | (ScreenOnEvent = 2)
+ | (ScreenOnSuspendEvent = 6)
+ ---------------------------------------------------PROCESS STATE events
+ 1 2 (TopEvent = 1002)
+ 1 (ForegroundServiceEvent = 1003)
+ 2 (ImportantBackgroundEvent = 1006)
+ 1 1 1 (ImportantForegroundEvent = 1005)
+
+ Based on the diagram above, Screen State / Process State pairs for each
+ AppCrashEvent are:
+ - StateTracker::kStateUnknown / important foreground
+ - off / important foreground
+ - off / Top
+ - on / important foreground
+ - off / important foreground
+ - off / top
+
+ - off / important foreground
+ - off / foreground service
+ - on / important background
+
+ */
+ // Initialize log events - first bucket.
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 5 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:15
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /*uid*/)); // 0:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 30 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 0:40
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 60 * NS_PER_SEC, 1 /*uid*/)); // 1:10
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 90 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 1:40
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 90 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:40
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 120 * NS_PER_SEC, 1 /*uid*/)); // 2:10
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 150 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 2:40
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 160 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:50
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 200 * NS_PER_SEC, 1 /*uid*/)); // 3:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 210 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:40
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 250 * NS_PER_SEC, 1 /*uid*/)); // 4:20
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 280 * NS_PER_SEC, 2 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP)); // 4:50
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 285 * NS_PER_SEC, 2 /*uid*/)); // 4:55
+
+ // Initialize log events - second bucket.
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 360 * NS_PER_SEC, 1 /*uid*/)); // 6:10
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 380 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 6:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 390 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 420 * NS_PER_SEC, 2 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 7:10
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 440 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 7:30
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 450 * NS_PER_SEC, 1 /*uid*/)); // 7:40
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 520 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 8:50
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 540 * NS_PER_SEC, 1 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 9:10
+ events.push_back(
+ CreateAppCrashOccurredEvent(bucketStartTimeNs + 570 * NS_PER_SEC, 2 /*uid*/)); // 9:40
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs * 2 + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(6, countMetrics.data_size());
+
+ // For each CountMetricData, check StateValue info is correct and buckets
+ // have correct counts.
+ auto data = countMetrics.data(0);
+ ASSERT_EQ(2, data.slice_by_state_size());
+ 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, data.slice_by_state(0).value());
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+ EXPECT_TRUE(data.slice_by_state(1).has_value());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+
+ data = countMetrics.data(1);
+ ASSERT_EQ(2, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+ EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id());
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+ EXPECT_TRUE(data.slice_by_state(1).has_value());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+
+ data = countMetrics.data(2);
+ ASSERT_EQ(2, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+ EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id());
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+ EXPECT_TRUE(data.slice_by_state(1).has_value());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, data.slice_by_state(1).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+
+ data = countMetrics.data(3);
+ ASSERT_EQ(2, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+ EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+ EXPECT_TRUE(data.slice_by_state(1).has_value());
+ EXPECT_EQ(android::app::PROCESS_STATE_TOP, data.slice_by_state(1).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(2, data.bucket_info(0).count());
+
+ data = countMetrics.data(4);
+ ASSERT_EQ(2, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+ EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+ EXPECT_TRUE(data.slice_by_state(1).has_value());
+ EXPECT_EQ(android::app::PROCESS_STATE_FOREGROUND_SERVICE, data.slice_by_state(1).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(1, data.bucket_info(0).count());
+
+ data = countMetrics.data(5);
+ ASSERT_EQ(2, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+ EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(1).atom_id());
+ EXPECT_TRUE(data.slice_by_state(1).has_value());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, data.slice_by_state(1).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(2, data.bucket_info(0).count());
+ EXPECT_EQ(1, data.bucket_info(1).count());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
deleted file mode 100644
index e4186b7..0000000
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ /dev/null
@@ -1,961 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
- DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition,
- bool hashStringInReport) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- auto scheduledJobPredicate = CreateScheduledJobPredicate();
- auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
- dimensions->add_child()->set_field(2); // job name field.
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
- {Position::FIRST});
- if (addExtraDimensionInCondition) {
- syncDimension->add_child()->set_field(2 /* name field*/);
- }
-
- config.set_hash_strings_in_metric_report(hashStringInReport);
- *config.add_predicate() = scheduledJobPredicate;
- *config.add_predicate() = screenIsOffPredicate;
- *config.add_predicate() = isSyncingPredicate;
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(StringToId("CombinationPredicate"));
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("scheduledJob"));
- metric->set_what(scheduledJobPredicate.id());
- metric->set_condition(combinationPredicate->id());
- metric->set_aggregation_type(aggregationType);
- auto dimensionWhat = metric->mutable_dimensions_in_what();
- dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(2); // job name field.
- *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-} // namespace
-
-/*
- The following test has the following input.
-
-{ 10000000002 10000000002 (8)9999[I], [S], job0[S], 1[I], }
-{ 10000000010 10000000010 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I], }
-{ 10000000011 10000000011 (29)1[I], }
-{ 10000000040 10000000040 (29)2[I], }
-{ 10000000050 10000000050 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I], }
-{ 10000000101 10000000101 (8)9999[I], [S], job0[S], 0[I], }
-{ 10000000102 10000000102 (29)1[I], }
-{ 10000000200 10000000200 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I], }
-{ 10000000201 10000000201 (8)9999[I], [S], job2[S], 1[I], }
-{ 10000000400 10000000400 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 1[I], }
-{ 10000000401 10000000401 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 1[I], }
-{ 10000000450 10000000450 (29)2[I], }
-{ 10000000500 10000000500 (8)9999[I], [S], job2[S], 0[I], }
-{ 10000000600 10000000600 (8)8888[I], [S], job2[S], 1[I], }
-{ 10000000650 10000000650 (29)1[I], }
-{ 309999999999 309999999999 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 0[I], }
-{ 310000000100 310000000100 (29)2[I], }
-{ 310000000300 310000000300 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I], }
-{ 310000000600 310000000600 (8)8888[I], [S], job1[S], 1[I], }
-{ 310000000640 310000000640 (29)1[I], }
-{ 310000000650 310000000650 (29)2[I], }
-{ 310000000700 310000000700 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 0[I], }
-{ 310000000850 310000000850 (8)8888[I], [S], job2[S], 0[I], }
-{ 310000000900 310000000900 (8)8888[I], [S], job1[S], 0[I], }
-*/
-TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition) {
- for (const bool hashStringInReport : { true, false }) {
- for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : { true, false }) {
- for (auto aggregationType : {DurationMetric::MAX_SPARSE, DurationMetric::SUM}) {
- ConfigKey cfgKey;
- auto config = CreateDurationMetricConfig_NoLink_AND_CombinationCondition(
- aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension,
- hashStringInReport);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(
- config.duration_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 11));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 40));
-
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 102));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 450));
-
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 650));
- events.push_back(CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 100));
-
- events.push_back(CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + bucketSizeNs + 640));
- events.push_back(CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 650));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job2",
- bucketStartTimeNs + bucketSizeNs + 850));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job1",
- bucketStartTimeNs + bucketSizeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job1",
- bucketStartTimeNs + bucketSizeNs + 900));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 10));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 200));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 300));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
- bucketStartTimeNs + 400));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
-
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + 401));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
- true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::DurationMetricDataWrapper metrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).duration_metrics(), &metrics);
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(metrics.data_size(), 4);
- auto data = metrics.data(0);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().
- dimensions_value(0).value_str(),
- "job0"); // job name
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40 - 11);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
-
- data = metrics.data(1);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().
- dimensions_value(0).value_str(),
- "job1"); // job name
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 10);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().
- dimensions_value(0).value_str(),
- "job2"); // job name
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 650);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(3);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().
- dimensions_value(0).value_str(),
- "job2"); // job name
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 650);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100 + 650 - 640);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- } else {
- EXPECT_EQ(metrics.data_size(), 4);
- auto data = metrics.data(0);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().
- dimensions_value(0).value_str(),
- "job0"); // job name
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40 - 11);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().
- dimensions_value(0).value_str(),
- "job1"); // job name
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 10);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
- "job2"); // job name
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 100);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(3);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().
- dimensions_value(0).value_str(),
- "job2"); // job name
- ValidateAttributionUidAndTagDimension(
- data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 110);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
- }
- }
- }
-}
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_Link_AND_CombinationCondition(
- DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- auto scheduledJobPredicate = CreateScheduledJobPredicate();
- auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- *dimensions = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- dimensions->add_child()->set_field(2); // job name field.
-
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- if (addExtraDimensionInCondition) {
- syncDimension->add_child()->set_field(2 /* name field*/);
- }
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-
- *config.add_predicate() = scheduledJobPredicate;
- *config.add_predicate() = screenIsOffPredicate;
- *config.add_predicate() = isSyncingPredicate;
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(StringToId("CombinationPredicate"));
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("scheduledJob"));
- metric->set_what(scheduledJobPredicate.id());
- metric->set_condition(combinationPredicate->id());
- metric->set_aggregation_type(aggregationType);
- *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-
- auto links = metric->add_links();
- links->set_condition(isSyncingPredicate.id());
- *links->mutable_fields_in_what() =
- CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- *links->mutable_fields_in_condition() =
- CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-} // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_AND_CombinationCondition) {
- for (bool isFullLink : {true, false}) {
- for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
- ConfigKey cfgKey;
- auto config = CreateDurationMetricConfig_Link_AND_CombinationCondition(
- aggregationType, !isFullLink);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions3 = {
- CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 55));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 120));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 121));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 450));
-
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 501));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 100));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2",
- bucketStartTimeNs + bucketSizeNs + 850));
-
- events.push_back(
- CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs - 2));
- events.push_back(
- CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs + 900));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 110));
-
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + 300));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
- events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + 400));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
-
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 550));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 800));
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs + 700));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
- true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::DurationMetricDataWrapper metrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(metrics.data_size(), 3);
- auto data = metrics.data(0);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300 + bucketSizeNs - 600);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- } else {
- EXPECT_EQ(metrics.data_size(), 3);
- auto data = metrics.data(0);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
- }
- }
-}
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_PartialLink_AND_CombinationCondition(
- DurationMetric::AggregationType aggregationType, bool hashStringInReport) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
-
- auto scheduledJobPredicate = CreateScheduledJobPredicate();
- auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- *dimensions = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- dimensions->add_child()->set_field(2); // job name field.
-
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- syncDimension->add_child()->set_field(2 /* name field*/);
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
-
- config.set_hash_strings_in_metric_report(hashStringInReport);
- *config.add_predicate() = scheduledJobPredicate;
- *config.add_predicate() = screenIsOffPredicate;
- *config.add_predicate() = isSyncingPredicate;
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(StringToId("CombinationPredicate"));
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("scheduledJob"));
- metric->set_what(scheduledJobPredicate.id());
- metric->set_condition(combinationPredicate->id());
- metric->set_aggregation_type(aggregationType);
- *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- *metric->mutable_dimensions_in_condition() = *syncDimension;
-
-
- auto links = metric->add_links();
- links->set_condition(isSyncingPredicate.id());
- *links->mutable_fields_in_what() =
- CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- *links->mutable_fields_in_condition() =
- CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-} // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_AND_CombinationCondition) {
- for (const bool hashStringInReport : {true, false}) {
- for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
- ConfigKey cfgKey;
- auto config =
- CreateDurationMetricConfig_PartialLink_AND_CombinationCondition(
- aggregationType, hashStringInReport);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions3 = {
- CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 55));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 120));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 121));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 450));
-
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 501));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 100));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2",
- bucketStartTimeNs + bucketSizeNs + 850));
-
- events.push_back(
- CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs - 2));
- events.push_back(
- CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs + 900));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 110));
-
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + 300));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
- events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + 400));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
-
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 550));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 800));
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs + 700));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
- true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::DurationMetricDataWrapper metrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).duration_metrics(), &metrics);
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(metrics.data_size(), 4);
- auto data = metrics.data(0);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
- EXPECT_EQ("ReadEmail",
- data.dimensions_in_condition().value_tuple().
- dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
- EXPECT_EQ("ReadDoc",
- data.dimensions_in_condition().value_tuple().
- dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 1 - 600 + 50);
-
- data = metrics.data(2);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
- EXPECT_EQ("ReadEmail",
- data.dimensions_in_condition().value_tuple().
- dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300 + bucketSizeNs - 600);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(3);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
- EXPECT_EQ("ReadDoc",
- data.dimensions_in_condition().value_tuple().
- dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- } else {
- EXPECT_EQ(metrics.data_size(), 4);
- auto data = metrics.data(0);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
- EXPECT_EQ("ReadEmail",
- data.dimensions_in_condition().value_tuple().
- dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 55);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
- EXPECT_EQ("ReadDoc",
- data.dimensions_in_condition().value_tuple().
- dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 50);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
- EXPECT_EQ("ReadEmail",
- data.dimensions_in_condition().value_tuple().
- dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 300);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(3);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
- EXPECT_EQ("ReadDoc",
- data.dimensions_in_condition().value_tuple().
- dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
- }
- }
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
deleted file mode 100644
index f3ecd56..0000000
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ /dev/null
@@ -1,816 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateCountMetric_NoLink_CombinationCondition_Config() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher();
- *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher;
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
- *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
- *config.add_predicate() = screenIsOffPredicate;
-
- auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
- // The predicate is dimensioning by any attribution node and both by uid and tag.
- *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateAttributionUidAndTagDimensions(android::util::WAKELOCK_STATE_CHANGED,
- {Position::FIRST});
- *config.add_predicate() = holdingWakelockPredicate;
-
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(987654);
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(holdingWakelockPredicate, combinationPredicate);
-
- auto metric = config.add_count_metric();
- metric->set_id(StringToId("ScreenBrightnessChangeMetric"));
- metric->set_what(screenBrightnessChangeAtomMatcher.id());
- metric->set_condition(combinationPredicate->id());
- *metric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
- *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
- metric->set_bucket(FIVE_MINUTES);
- return config;
-}
-
-} // namespace
-
-TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition) {
- ConfigKey cfgKey;
- auto config = CreateCountMetric_NoLink_CombinationCondition_Config();
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
- CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(333, "App2"),
- CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(
- CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 100));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 1));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 2 * bucketSizeNs - 10));
-
- events.push_back(CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 200));
- events.push_back(
- CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1));
-
- events.push_back(CreateAcquireWakelockEvent(attributions2, "wl2",
- bucketStartTimeNs + bucketSizeNs - 100));
- events.push_back(CreateReleaseWakelockEvent(attributions2, "wl2",
- bucketStartTimeNs + 2 * bucketSizeNs - 50));
-
- events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 11));
- events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 101));
- events.push_back(CreateScreenBrightnessChangedEvent(123, bucketStartTimeNs + 201));
- events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + 203));
- events.push_back(
- CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 99));
- events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs - 2));
- events.push_back(CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + bucketSizeNs - 1));
- events.push_back(CreateScreenBrightnessChangedEvent(456, bucketStartTimeNs + bucketSizeNs + 2));
- events.push_back(
- CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 11));
- events.push_back(
- CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 9));
- events.push_back(
- CreateScreenBrightnessChangedEvent(789, bucketStartTimeNs + 2 * bucketSizeNs - 1));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-
- EXPECT_EQ(countMetrics.data_size(), 7);
- auto data = countMetrics.data(0);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
- EXPECT_FALSE(data.dimensions_in_condition().has_field());
-
- data = countMetrics.data(1);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
- ValidateAttributionUidDimension(data.dimensions_in_condition(),
- android::util::WAKELOCK_STATE_CHANGED, 111);
-
- data = countMetrics.data(2);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 3);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
- ValidateAttributionUidDimension(data.dimensions_in_condition(),
- android::util::WAKELOCK_STATE_CHANGED, 111);
-
- data = countMetrics.data(3);
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).count(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).count(), 1);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
- ValidateAttributionUidDimension(data.dimensions_in_condition(),
- android::util::WAKELOCK_STATE_CHANGED, 333);
-
- data = countMetrics.data(4);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
- EXPECT_FALSE(data.dimensions_in_condition().has_field());
-
- data = countMetrics.data(5);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
- ValidateAttributionUidDimension(data.dimensions_in_condition(),
- android::util::WAKELOCK_STATE_CHANGED, 111);
-
- data = countMetrics.data(6);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
- ValidateAttributionUidDimension(data.dimensions_in_condition(),
- android::util::WAKELOCK_STATE_CHANGED, 333);
-}
-
-namespace {
-
-StatsdConfig CreateCountMetric_Link_CombinationCondition() {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto appCrashMatcher = CreateProcessCrashAtomMatcher();
- *config.add_atom_matcher() = appCrashMatcher;
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
- {Position::FIRST});
- syncDimension->add_child()->set_field(2 /* name field*/);
-
- *config.add_predicate() = screenIsOffPredicate;
- *config.add_predicate() = isSyncingPredicate;
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(987654);
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
- auto metric = config.add_count_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("AppCrashMetric"));
- metric->set_what(appCrashMatcher.id());
- metric->set_condition(combinationPredicate->id());
- *metric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */});
- *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-
- // Links between crash atom and condition of app is in syncing.
- auto links = metric->add_links();
- links->set_condition(isSyncingPredicate.id());
- auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(1); // uid field.
- *links->mutable_fields_in_condition() =
- CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-} // namespace
-
-TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition) {
- ConfigKey cfgKey;
- auto config = CreateCountMetric_Link_CombinationCondition();
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
- CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(333, "App2"),
- CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 11));
- events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 101));
- events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 101));
-
- events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 201));
- events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 211));
- events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 211));
-
- events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 401));
- events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 401));
- events.push_back(CreateAppCrashEvent(555, bucketStartTimeNs + 401));
-
- events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + bucketSizeNs + 301));
- events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + bucketSizeNs + 301));
-
- events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701));
-
- events.push_back(
- CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 100));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 202));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + bucketSizeNs + 700));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
- events.push_back(
- CreateSyncEndEvent(attributions1, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
- events.push_back(
- CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1));
-
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400));
- events.push_back(
- CreateSyncEndEvent(attributions2, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 600));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
-
- EXPECT_EQ(countMetrics.data_size(), 5);
- auto data = countMetrics.data(0);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
- EXPECT_FALSE(data.dimensions_in_condition().has_field());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-
- data = countMetrics.data(1);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-
- data = countMetrics.data(2);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 222);
- EXPECT_FALSE(data.dimensions_in_condition().has_field());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-
- data = countMetrics.data(3);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).count(), 1);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = countMetrics.data(4);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 777);
- EXPECT_FALSE(data.dimensions_in_condition().has_field());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).count(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
-}
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_NoLink_CombinationCondition(
- DurationMetric::AggregationType aggregationType) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
- *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
- auto inBatterySaverModePredicate = CreateBatterySaverModePredicate();
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
- {Position::FIRST});
- syncDimension->add_child()->set_field(2 /* name field */);
-
- *config.add_predicate() = inBatterySaverModePredicate;
- *config.add_predicate() = screenIsOffPredicate;
- *config.add_predicate() = isSyncingPredicate;
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(987654);
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("BatterySaverModeDurationMetric"));
- metric->set_what(inBatterySaverModePredicate.id());
- metric->set_condition(combinationPredicate->id());
- metric->set_aggregation_type(aggregationType);
- *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-} // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition) {
- for (auto aggregationType : { DurationMetric::MAX_SPARSE, DurationMetric::SUM}) {
- ConfigKey cfgKey;
- auto config = CreateDurationMetricConfig_NoLink_CombinationCondition(aggregationType);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 1));
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 101));
- events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 110));
-
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 201));
- events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 500));
-
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 600));
- events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 850));
-
- events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870));
- events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900));
-
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 10));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 100));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 202));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + bucketSizeNs + 800));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 300));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
- events.push_back(
- CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1));
-
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::DurationMetricDataWrapper metrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
- EXPECT_EQ(metrics.data_size(), 3);
- auto data = metrics.data(0);
- EXPECT_FALSE(data.dimensions_in_what().has_field());
- EXPECT_FALSE(data.dimensions_in_condition().has_field());
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- } else {
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
-
- data = metrics.data(1);
- EXPECT_FALSE(data.dimensions_in_what().has_field());
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 2);
-
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
- } else {
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 300);
- }
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- EXPECT_FALSE(data.dimensions_in_what().has_field());
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 2);
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
- } else {
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs + 700 - 600);
- }
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
-}
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_Link_CombinationCondition(
- DurationMetric::AggregationType aggregationType) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
- *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
- *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
- auto screenIsOffPredicate = CreateScreenIsOffPredicate();
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
- {Position::FIRST});
- syncDimension->add_child()->set_field(2 /* name field */);
-
- auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
- *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */});
-
- *config.add_predicate() = screenIsOffPredicate;
- *config.add_predicate() = isSyncingPredicate;
- *config.add_predicate() = isInBackgroundPredicate;
- auto combinationPredicate = config.add_predicate();
- combinationPredicate->set_id(987654);
- combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
- addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
- addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("AppInBackgroundMetric"));
- metric->set_what(isInBackgroundPredicate.id());
- metric->set_condition(combinationPredicate->id());
- metric->set_aggregation_type(aggregationType);
- *metric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */});
- *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
-
- // Links between crash atom and condition of app is in syncing.
- auto links = metric->add_links();
- links->set_condition(isSyncingPredicate.id());
- auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(1); // uid field.
- *links->mutable_fields_in_condition() =
- CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-} // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_OR_CombinationCondition) {
- for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
- ConfigKey cfgKey;
- auto config = CreateDurationMetricConfig_Link_CombinationCondition(aggregationType);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 101));
- events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + 110));
-
- events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 201));
- events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + bucketSizeNs + 100));
-
- events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399));
- events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800));
-
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 10));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 100));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 202));
- events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- bucketStartTimeNs + bucketSizeNs + 801));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 300));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
- events.push_back(
- CreateSyncEndEvent(attributions1, "ReadDoc", bucketStartTimeNs + bucketSizeNs - 1));
-
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::DurationMetricDataWrapper metrics;
- sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
- EXPECT_EQ(metrics.data_size(), 3);
- auto data = metrics.data(0);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
- EXPECT_FALSE(data.dimensions_in_condition().has_field());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- } else {
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs + 100 - 201);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
-
- data = metrics.data(2);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- } else {
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs + 299);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
- }
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
deleted file mode 100644
index 489bb0b..0000000
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+++ /dev/null
@@ -1,814 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <gtest/gtest.h>
-
-#include "src/StatsLogProcessor.h"
-#include "src/stats_log_util.h"
-#include "tests/statsd_test_util.h"
-
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-#ifdef __ANDROID__
-
-namespace {
-
-StatsdConfig CreateDurationMetricConfig_NoLink_SimpleCondition(
- DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
- auto scheduledJobPredicate = CreateScheduledJobPredicate();
- auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
- dimensions->add_child()->set_field(2); // job name field.
-
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
- {Position::FIRST});
- if (addExtraDimensionInCondition) {
- syncDimension->add_child()->set_field(2 /* name field*/);
- }
-
- *config.add_predicate() = scheduledJobPredicate;
- *config.add_predicate() = isSyncingPredicate;
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("scheduledJob"));
- metric->set_what(scheduledJobPredicate.id());
- metric->set_condition(isSyncingPredicate.id());
- metric->set_aggregation_type(aggregationType);
- auto dimensionWhat = metric->mutable_dimensions_in_what();
- dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
- dimensionWhat->add_child()->set_field(2); // job name field.
- *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-} // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_SimpleCondition) {
- for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : {true, false}) {
- for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
- ConfigKey cfgKey;
- auto config = CreateDurationMetricConfig_NoLink_SimpleCondition(
- aggregationType, isDimensionInConditionSubSetOfConditionTrackerDimension);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 1));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 10));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 200));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 300));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
- bucketStartTimeNs + 400));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
-
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + 401));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
- true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::DurationMetricDataWrapper metrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).duration_metrics(), &metrics);
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(metrics.data_size(), 4);
- auto data = metrics.data(0);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
- "job0"); // job name
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
- "job1"); // job name
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
- "job2"); // job name
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(3);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
- "job2"); // job name
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- } else {
- EXPECT_EQ(metrics.data_size(), 4);
- auto data = metrics.data(0);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
- "job0"); // job name
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 40);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
- "job1"); // job name
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
- "job2"); // job name
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 111, "App1");
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 300);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(3);
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::SCHEDULED_JOB_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(),
- 2); // job name field
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
- "job2"); // job name
- ValidateAttributionUidAndTagDimension(data.dimensions_in_condition(),
- android::util::SYNC_STATE_CHANGED, 333, "App2");
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 );
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
- }
- }
-}
-
-namespace {
-
-StatsdConfig createDurationMetric_Link_SimpleConditionConfig(
- DurationMetric::AggregationType aggregationType, bool addExtraDimensionInCondition) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
- auto scheduledJobPredicate = CreateScheduledJobPredicate();
- auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- *dimensions = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- dimensions->add_child()->set_field(2); // job name field.
-
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- if (addExtraDimensionInCondition) {
- syncDimension->add_child()->set_field(2 /* name field*/);
- }
-
- *config.add_predicate() = scheduledJobPredicate;
- *config.add_predicate() = isSyncingPredicate;
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("scheduledJob"));
- metric->set_what(scheduledJobPredicate.id());
- metric->set_condition(isSyncingPredicate.id());
- metric->set_aggregation_type(aggregationType);
- *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
-
- auto links = metric->add_links();
- links->set_condition(isSyncingPredicate.id());
- *links->mutable_fields_in_what() =
- CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- *links->mutable_fields_in_condition() =
- CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-} // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_Link_SimpleCondition) {
- for (bool isFullLink : {true, false}) {
- for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
- ConfigKey cfgKey;
- auto config = createDurationMetric_Link_SimpleConditionConfig(
- aggregationType, !isFullLink);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions3 = {
- CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
- events.push_back(
- CreateFinishScheduledJobEvent({CreateAttribution(333, "App2")}, "job2",
- bucketStartTimeNs + bucketSizeNs + 850));
-
- events.push_back(
- CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs - 2));
- events.push_back(
- CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs + 900));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 110));
-
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + 300));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
- events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + 400));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
-
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 550));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 800));
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs + 700));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
- true, ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::DurationMetricDataWrapper metrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(metrics.data_size(), 3);
- auto data = metrics.data(0);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- } else {
- EXPECT_EQ(metrics.data_size(), 3);
- auto data = metrics.data(0);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(2);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
- }
- }
-}
-
-namespace {
-
-StatsdConfig createDurationMetric_PartialLink_SimpleConditionConfig(
- DurationMetric::AggregationType aggregationType) {
- StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- *config.add_atom_matcher() = CreateStartScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateFinishScheduledJobAtomMatcher();
- *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
- *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
-
- auto scheduledJobPredicate = CreateScheduledJobPredicate();
- auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
- *dimensions = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- dimensions->add_child()->set_field(2); // job name field.
-
- auto isSyncingPredicate = CreateIsSyncingPredicate();
- auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
- *syncDimension = CreateAttributionUidDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- syncDimension->add_child()->set_field(2 /* name field*/);
-
- *config.add_predicate() = scheduledJobPredicate;
- *config.add_predicate() = isSyncingPredicate;
-
- auto metric = config.add_duration_metric();
- metric->set_bucket(FIVE_MINUTES);
- metric->set_id(StringToId("scheduledJob"));
- metric->set_what(scheduledJobPredicate.id());
- metric->set_condition(isSyncingPredicate.id());
- metric->set_aggregation_type(aggregationType);
- *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- *metric->mutable_dimensions_in_condition() = *syncDimension;
-
- auto links = metric->add_links();
- links->set_condition(isSyncingPredicate.id());
- *links->mutable_fields_in_what() =
- CreateAttributionUidDimensions(
- android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
- *links->mutable_fields_in_condition() =
- CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
- return config;
-}
-
-} // namespace
-
-TEST(DimensionInConditionE2eTest, TestDurationMetric_PartialLink_SimpleCondition) {
- for (auto aggregationType : {DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
- ConfigKey cfgKey;
- auto config = createDurationMetric_PartialLink_SimpleConditionConfig(
- aggregationType);
- int64_t bucketStartTimeNs = 10000000000;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
- EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-
- std::vector<AttributionNodeInternal> attributions1 = {
- CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions2 = {
- CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<AttributionNodeInternal> attributions3 = {
- CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(555, "GMSCoreModule2")};
-
- std::vector<std::unique_ptr<LogEvent>> events;
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
-
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
- events.push_back(CreateStartScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
- events.push_back(CreateFinishScheduledJobEvent(
- {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + bucketSizeNs + 850));
-
- events.push_back(
- CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs - 2));
- events.push_back(
- CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
- bucketStartTimeNs + bucketSizeNs + 900));
-
- events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 50));
- events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
- bucketStartTimeNs + 110));
-
- events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + 300));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
- bucketStartTimeNs + bucketSizeNs + 700));
- events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + 400));
- events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
-
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 550));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + 800));
- events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs - 1));
- events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
- bucketStartTimeNs + bucketSizeNs + 700));
-
- sortLogEventsByTimestamp(&events);
-
- for (const auto& event : events) {
- processor->OnLogEvent(event.get());
- }
-
- ConfigMetricsReportList reports;
- vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_TRUE(buffer.size() > 0);
- EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
- backfillDimensionPath(&reports);
- backfillStringInReport(&reports);
- backfillStartEndTimestamp(&reports);
-
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- StatsLogReport::DurationMetricDataWrapper metrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).duration_metrics(), &metrics);
-
- if (aggregationType == DurationMetric::SUM) {
- EXPECT_EQ(4, metrics.data_size());
- auto data = metrics.data(0);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
- EXPECT_EQ("ReadEmail",
- data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
- EXPECT_EQ("ReadDoc",
- data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 1 - 400 - 100);
-
- data = metrics.data(2);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
- EXPECT_EQ("ReadEmail",
- data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300 + bucketSizeNs - 600);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(3);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
- EXPECT_EQ("ReadDoc",
- data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 1);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- } else {
- EXPECT_EQ(metrics.data_size(), 4);
- auto data = metrics.data(0);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 111);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111);
- EXPECT_EQ("ReadEmail",
- data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 101 - 50);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
-
- data = metrics.data(1);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
- EXPECT_EQ("ReadDoc",
- data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 100);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 1 - 600);
-
- data = metrics.data(2);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 333);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333);
- EXPECT_EQ("ReadEmail",
- data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 300);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 700);
- EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
-
- data = metrics.data(3);
- ValidateAttributionUidDimension(
- data.dimensions_in_what(), android::util::SCHEDULED_JOB_STATE_CHANGED, 444);
- ValidateAttributionUidDimension(
- data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 444);
- EXPECT_EQ("ReadDoc",
- data.dimensions_in_condition().value_tuple().dimensions_value(1).value_str());
- EXPECT_EQ(data.bucket_info_size(), 1);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 701);
- EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(),
- bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
- bucketStartTimeNs + 2 * bucketSizeNs);
- }
- }
-}
-
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
index 5da0fca..4efb038 100644
--- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
@@ -14,12 +14,13 @@
#include <gtest/gtest.h>
+#include <vector>
+
#include "src/StatsLogProcessor.h"
+#include "src/state/StateTracker.h"
#include "src/stats_log_util.h"
#include "tests/statsd_test_util.h"
-#include <vector>
-
namespace android {
namespace os {
namespace statsd {
@@ -28,7 +29,7 @@
TEST(DurationMetricE2eTest, TestOneBucket) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
@@ -45,9 +46,8 @@
durationMetric->set_bucket(FIVE_MINUTES);
durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-
- const int64_t baseTimeNs = 0; // 0:00
- const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
const int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
@@ -57,10 +57,10 @@
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
@@ -68,42 +68,43 @@
std::unique_ptr<LogEvent> event;
// Screen is off at start of bucket.
- event = CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01
+ event = CreateScreenStateChangedEvent(configAddedTimeNs,
+ android::view::DISPLAY_STATE_OFF); // 0:01
processor->OnLogEvent(event.get());
// Turn screen on.
- const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
+ const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
+ event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(event.get());
// Turn off screen 30 seconds after turning on.
- const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
+ const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
+ event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(event.get());
- event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42
+ event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42
processor->OnLogEvent(event.get());
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
- ADB_DUMP, FAST, &buffer); // 5:01
+ ADB_DUMP, FAST, &buffer); // 5:01
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
- const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
- reports.reports(0).metrics(0).duration_metrics();
- EXPECT_EQ(1, durationMetrics.data_size());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(1, durationMetrics.data_size());
- auto data = durationMetrics.data(0);
- EXPECT_EQ(1, data.bucket_info_size());
+ DurationMetricData data = durationMetrics.data(0);
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos());
EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
@@ -111,7 +112,7 @@
TEST(DurationMetricE2eTest, TestTwoBuckets) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
@@ -128,9 +129,8 @@
durationMetric->set_bucket(FIVE_MINUTES);
durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-
- const int64_t baseTimeNs = 0; // 0:00
- const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
const int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
@@ -140,10 +140,10 @@
auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
@@ -151,42 +151,43 @@
std::unique_ptr<LogEvent> event;
// Screen is off at start of bucket.
- event = CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01
+ event = CreateScreenStateChangedEvent(configAddedTimeNs,
+ android::view::DISPLAY_STATE_OFF); // 0:01
processor->OnLogEvent(event.get());
// Turn screen on.
- const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
+ const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
+ event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(event.get());
// Turn off screen 30 seconds after turning on.
- const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
+ const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
+ event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(event.get());
- event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42
+ event = CreateScreenBrightnessChangedEvent(durationEndNs + 1 * NS_PER_SEC, 64); // 0:42
processor->OnLogEvent(event.get());
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false, true,
- ADB_DUMP, FAST, &buffer); // 10:01
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false,
+ true, ADB_DUMP, FAST, &buffer); // 10:01
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
- const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
- reports.reports(0).metrics(0).duration_metrics();
- EXPECT_EQ(1, durationMetrics.data_size());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(1, durationMetrics.data_size());
- auto data = durationMetrics.data(0);
- EXPECT_EQ(1, data.bucket_info_size());
+ DurationMetricData data = durationMetrics.data(0);
+ ASSERT_EQ(1, data.bucket_info_size());
auto bucketInfo = data.bucket_info(0);
EXPECT_EQ(0, bucketInfo.bucket_num());
@@ -197,7 +198,7 @@
TEST(DurationMetricE2eTest, TestWithActivation) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
@@ -220,7 +221,7 @@
metric_activation1->set_metric_id(metricId);
auto event_activation1 = metric_activation1->add_event_activation();
event_activation1->set_atom_matcher_id(crashMatcher.id());
- event_activation1->set_ttl_seconds(30); // 30 secs.
+ event_activation1->set_ttl_seconds(30); // 30 secs.
const int64_t bucketStartTimeNs = 10000000000;
const int64_t bucketSizeNs =
@@ -237,30 +238,31 @@
vector<int64_t> activeConfigsBroadcast;
int broadcastCount = 0;
- StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
- bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+ StatsLogProcessor processor(
+ m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
+ [](const ConfigKey& key) { return true; },
[&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
+ const vector<int64_t>& activeConfigs) {
broadcastCount++;
EXPECT_EQ(broadcastUid, uid);
activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
- activeConfigs.begin(), activeConfigs.end());
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
+ activeConfigs.end());
return true;
});
- processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); // 0:00
+ processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); // 0:00
- EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap.size(), 1u);
+ ASSERT_EQ(eventActivationMap.size(), 1u);
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
@@ -269,25 +271,25 @@
std::unique_ptr<LogEvent> event;
// Turn screen off.
- event = CreateScreenStateChangedEvent(
- android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * NS_PER_SEC); // 0:02
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * NS_PER_SEC,
+ android::view::DISPLAY_STATE_OFF); // 0:02
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 2 * NS_PER_SEC);
// Turn screen on.
- const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
- processor.OnLogEvent(event.get());
+ const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05
+ event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), durationStartNs);
// Activate metric.
- const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10
+ const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10
const int64_t activationEndNs =
- activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40
- event = CreateAppCrashEvent(111, activationStartNs);
- processor.OnLogEvent(event.get());
+ activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40
+ event = CreateAppCrashEvent(activationStartNs, 111);
+ processor.OnLogEvent(event.get(), activationStartNs);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
@@ -295,43 +297,43 @@
// Expire activation.
const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC;
- event = CreateScreenBrightnessChangedEvent(64, expirationNs); // 0:47
- processor.OnLogEvent(event.get());
+ event = CreateScreenBrightnessChangedEvent(expirationNs, 64); // 0:47
+ processor.OnLogEvent(event.get(), expirationNs);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 2);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
- EXPECT_EQ(eventActivationMap.size(), 1u);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(eventActivationMap.size(), 1u);
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
// Turn off screen 10 seconds after activation expiration.
- const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
- processor.OnLogEvent(event.get());
+ const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50
+ event = CreateScreenStateChangedEvent(durationEndNs, android::view::DISPLAY_STATE_OFF);
+ processor.OnLogEvent(event.get(), durationEndNs);
// Turn screen on.
- const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs);
- processor.OnLogEvent(event.get());
+ const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55
+ event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), duration2StartNs);
// Turn off screen.
- const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, duration2EndNs);
- processor.OnLogEvent(event.get());
+ const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05
+ event = CreateScreenStateChangedEvent(duration2EndNs, android::view::DISPLAY_STATE_OFF);
+ processor.OnLogEvent(event.get(), duration2EndNs);
// Activate metric.
- const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10
+ const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10
const int64_t activation2EndNs =
- activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40
- event = CreateAppCrashEvent(211, activation2StartNs);
- processor.OnLogEvent(event.get());
+ activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40
+ event = CreateAppCrashEvent(activation2StartNs, 211);
+ processor.OnLogEvent(event.get(), activation2StartNs);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs);
@@ -340,22 +342,23 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
- ADB_DUMP, FAST, &buffer); // 5:01
+ ADB_DUMP, FAST, &buffer); // 5:01
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
- const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
- reports.reports(0).metrics(0).duration_metrics();
- EXPECT_EQ(1, durationMetrics.data_size());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(1, durationMetrics.data_size());
- auto data = durationMetrics.data(0);
- EXPECT_EQ(1, data.bucket_info_size());
+ DurationMetricData data = durationMetrics.data(0);
+ ASSERT_EQ(1, data.bucket_info_size());
auto bucketInfo = data.bucket_info(0);
EXPECT_EQ(0, bucketInfo.bucket_num());
@@ -366,7 +369,7 @@
TEST(DurationMetricE2eTest, TestWithCondition) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
*config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
@@ -390,10 +393,10 @@
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
EXPECT_TRUE(metricsManager->isActive());
@@ -401,41 +404,47 @@
EXPECT_TRUE(eventActivationMap.empty());
int appUid = 123;
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
+ vector<int> attributionUids1 = {appUid};
+ vector<string> attributionTags1 = {"App1"};
- auto event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10
+ auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1,
+ attributionTags1,
+ "wl1"); // 0:10
processor->OnLogEvent(event.get());
- event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22
+ event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22
processor->OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(
- appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC,
+ appUid); // 3:15
processor->OnLogEvent(event.get());
- event = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 4 * 60 * NS_PER_SEC); // 4:00
+ event = CreateReleaseWakelockEvent(bucketStartTimeNs + 4 * 60 * NS_PER_SEC, attributionUids1,
+ attributionTags1,
+ "wl1"); // 4:00
processor->OnLogEvent(event.get());
vector<uint8_t> buffer;
ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_GT(buffer.size(), 0);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(1, durationMetrics.data_size());
- auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ DurationMetricData data = durationMetrics.data(0);
// Validate bucket info.
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
auto bucketInfo = data.bucket_info(0);
EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
@@ -445,7 +454,7 @@
TEST(DurationMetricE2eTest, TestWithSlicedCondition) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
@@ -454,14 +463,14 @@
auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
// The predicate is dimensioning by first attribution node by uid.
- FieldMatcher dimensions = CreateAttributionUidDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ FieldMatcher dimensions = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED,
+ {Position::FIRST});
*holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
*config.add_predicate() = holdingWakelockPredicate;
auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
*isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
*config.add_predicate() = isInBackgroundPredicate;
auto durationMetric = config.add_duration_metric();
@@ -470,29 +479,28 @@
durationMetric->set_condition(isInBackgroundPredicate.id());
durationMetric->set_aggregation_type(DurationMetric::SUM);
// The metric is dimensioning by first attribution node and only by uid.
- *durationMetric->mutable_dimensions_in_what() =
- CreateAttributionUidDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
+ util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
durationMetric->set_bucket(FIVE_MINUTES);
// Links between wakelock state atom and condition of app is in background.
auto links = durationMetric->add_links();
links->set_condition(isInBackgroundPredicate.id());
auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED);
+ dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
*links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
- android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST });
+ util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
ConfigKey cfgKey;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
EXPECT_TRUE(metricsManager->isActive());
@@ -500,44 +508,47 @@
EXPECT_TRUE(eventActivationMap.empty());
int appUid = 123;
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
+ std::vector<int> attributionUids1 = {appUid};
+ std::vector<string> attributionTags1 = {"App1"};
- auto event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10
+ auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "wl1"); // 0:10
processor->OnLogEvent(event.get());
- event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22
+ event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22
processor->OnLogEvent(event.get());
- event = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 60 * NS_PER_SEC); // 1:00
+ event = CreateReleaseWakelockEvent(bucketStartTimeNs + 60 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "wl1"); // 1:00
processor->OnLogEvent(event.get());
-
- event = CreateMoveToForegroundEvent(
- appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC,
+ appUid); // 3:15
processor->OnLogEvent(event.get());
vector<uint8_t> buffer;
ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_GT(buffer.size(), 0);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(1, durationMetrics.data_size());
- auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ DurationMetricData data = durationMetrics.data(0);
// Validate dimension value.
ValidateAttributionUidDimension(data.dimensions_in_what(),
- android::util::WAKELOCK_STATE_CHANGED, appUid);
+ util::WAKELOCK_STATE_CHANGED, appUid);
// Validate bucket info.
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
auto bucketInfo = data.bucket_info(0);
EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
@@ -547,7 +558,7 @@
TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) {
StatsdConfig config;
- config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
*config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
@@ -557,14 +568,14 @@
auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
// The predicate is dimensioning by first attribution node by uid.
- FieldMatcher dimensions = CreateAttributionUidDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ FieldMatcher dimensions = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED,
+ {Position::FIRST});
*holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
*config.add_predicate() = holdingWakelockPredicate;
auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
*isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
*config.add_predicate() = isInBackgroundPredicate;
auto durationMetric = config.add_duration_metric();
@@ -573,19 +584,18 @@
durationMetric->set_condition(isInBackgroundPredicate.id());
durationMetric->set_aggregation_type(DurationMetric::SUM);
// The metric is dimensioning by first attribution node and only by uid.
- *durationMetric->mutable_dimensions_in_what() =
- CreateAttributionUidDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
+ util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
durationMetric->set_bucket(FIVE_MINUTES);
// Links between wakelock state atom and condition of app is in background.
auto links = durationMetric->add_links();
links->set_condition(isInBackgroundPredicate.id());
auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED);
+ dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
*links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
- android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST });
+ util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
auto metric_activation1 = config.add_metric_activation();
metric_activation1->set_metric_id(durationMetric->id());
@@ -598,25 +608,26 @@
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
- EXPECT_EQ(eventActivationMap.size(), 1u);
+ ASSERT_EQ(eventActivationMap.size(), 1u);
EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
int appUid = 123;
- std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
+ std::vector<int> attributionUids1 = {appUid};
+ std::vector<string> attributionTags1 = {"App1"};
- auto event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10
+ auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, attributionUids1,
+ attributionTags1, "wl1"); // 0:10
processor->OnLogEvent(event.get());
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
@@ -624,7 +635,7 @@
EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
- event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22
+ event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, appUid); // 0:22
processor->OnLogEvent(event.get());
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
@@ -632,8 +643,8 @@
EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
- const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
+ const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30
+ event = CreateScreenStateChangedEvent(durationStartNs, android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(event.get());
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
@@ -642,8 +653,8 @@
EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
const int64_t durationEndNs =
- durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00
- event = CreateAppCrashEvent(333, durationEndNs);
+ durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00
+ event = CreateAppCrashEvent(durationEndNs, 333);
processor->OnLogEvent(event.get());
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
@@ -651,24 +662,24 @@
EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs);
EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
- event = CreateMoveToForegroundEvent(
- appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC,
+ appUid); // 3:15
processor->OnLogEvent(event.get());
- event = CreateReleaseWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC); // 4:17
+ event = CreateReleaseWakelockEvent(bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC,
+ attributionUids1, attributionTags1, "wl1"); // 4:17
processor->OnLogEvent(event.get());
- event = CreateMoveToBackgroundEvent(
- appUid, bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC); // 4:20
+ event = CreateMoveToBackgroundEvent(bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC,
+ appUid); // 4:20
processor->OnLogEvent(event.get());
- event = CreateAcquireWakelockEvent(
- attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC); // 4:25
+ event = CreateAcquireWakelockEvent(bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC,
+ attributionUids1, attributionTags1, "wl1"); // 4:25
processor->OnLogEvent(event.get());
- const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs);
+ const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30
+ event = CreateScreenStateChangedEvent(duration2StartNs, android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(event.get());
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
@@ -678,24 +689,27 @@
vector<uint8_t> buffer;
ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
- EXPECT_GT(buffer.size(), 0);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
+ ASSERT_GT(buffer.size(), 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(1, durationMetrics.data_size());
- auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ DurationMetricData data = durationMetrics.data(0);
// Validate dimension value.
ValidateAttributionUidDimension(data.dimensions_in_what(),
- android::util::WAKELOCK_STATE_CHANGED, appUid);
+ util::WAKELOCK_STATE_CHANGED, appUid);
// Validate bucket info.
- EXPECT_EQ(2, data.bucket_info_size());
+ ASSERT_EQ(2, data.bucket_info_size());
auto bucketInfo = data.bucket_info(0);
EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
@@ -708,6 +722,748 @@
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos());
}
+TEST(DurationMetricE2eTest, TestWithSlicedState) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
+ *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
+
+ auto batterySaverModePredicate = CreateBatterySaverModePredicate();
+ *config.add_predicate() = batterySaverModePredicate;
+
+ auto screenState = CreateScreenState();
+ *config.add_state() = screenState;
+
+ // Create duration metric that slices by screen state.
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreen"));
+ durationMetric->set_what(batterySaverModePredicate.id());
+ durationMetric->add_slice_by_state(screenState.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ // Initialize StatsLogProcessor.
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ EXPECT_TRUE(metricsManager->isActive());
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_TRUE(metricProducer->mIsActive);
+ ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
+ ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
+
+ // Check that StateTrackers were initialized correctly.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+ /*
+ bucket #1 bucket #2
+ | 1 2 3 4 5 6 7 8 9 10 (minutes)
+ |-----------------------------|-----------------------------|--
+ ON OFF ON (BatterySaverMode)
+ | | | (ScreenIsOnEvent)
+ | | (ScreenIsOffEvent)
+ | (ScreenDozeEvent)
+ */
+ // Initialize log events.
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 10 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 50 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:00
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 80 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 120 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 250 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50
+
+ // Bucket boundary.
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 310 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:20
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 360 * NS_PER_SEC,
+ true /* include current partial bucket */, true, ADB_DUMP, FAST,
+ &buffer); // 6:10
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(3, durationMetrics.data_size());
+
+ DurationMetricData data = durationMetrics.data(0);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(1);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(370 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(2);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithConditionAndSlicedState) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
+ *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
+ *config.add_atom_matcher() = CreateBatteryStateNoneMatcher();
+ *config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
+
+ auto batterySaverModePredicate = CreateBatterySaverModePredicate();
+ *config.add_predicate() = batterySaverModePredicate;
+
+ auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate();
+ *config.add_predicate() = deviceUnpluggedPredicate;
+
+ auto screenState = CreateScreenState();
+ *config.add_state() = screenState;
+
+ // Create duration metric that has a condition and slices by screen state.
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("DurationBatterySaverModeOnBatterySliceScreen"));
+ durationMetric->set_what(batterySaverModePredicate.id());
+ durationMetric->set_condition(deviceUnpluggedPredicate.id());
+ durationMetric->add_slice_by_state(screenState.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ // Initialize StatsLogProcessor.
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ EXPECT_TRUE(metricsManager->isActive());
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_TRUE(metricProducer->mIsActive);
+ ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
+ ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
+
+ // Check that StateTrackers were initialized correctly.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+ /*
+ bucket #1 bucket #2
+ | 1 2 3 4 5 6 7 8 (minutes)
+ |---------------------------------------|------------------
+ ON OFF ON (BatterySaverMode)
+ T F T (DeviceUnpluggedPredicate)
+ | | | (ScreenIsOnEvent)
+ | | | (ScreenIsOffEvent)
+ | (ScreenDozeEvent)
+ */
+ // Initialize log events.
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 20 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:30
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 80 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:30
+ events.push_back(
+ CreateBatteryStateChangedEvent(bucketStartTimeNs + 110 * NS_PER_SEC,
+ BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 2:00
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 145 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:35
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 170 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 3:00
+ events.push_back(
+ CreateBatteryStateChangedEvent(bucketStartTimeNs + 180 * NS_PER_SEC,
+ BatteryPluggedStateEnum::BATTERY_PLUGGED_USB)); // 3:10
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 200 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 3:30
+ events.push_back(
+ CreateBatteryStateChangedEvent(bucketStartTimeNs + 230 * NS_PER_SEC,
+ BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE)); // 4:00
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 260 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 4:30
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50
+
+ // Bucket boundary.
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 320 * NS_PER_SEC)); // 5:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 380 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 6:30
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 410 * NS_PER_SEC,
+ true /* include current partial bucket */, true, ADB_DUMP, FAST,
+ &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(3, durationMetrics.data_size());
+
+ DurationMetricData data = durationMetrics.data(0);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(1);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(45 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(60 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(420 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(2);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithSlicedStateMapped) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
+ *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
+
+ auto batterySaverModePredicate = CreateBatterySaverModePredicate();
+ *config.add_predicate() = batterySaverModePredicate;
+
+ int64_t screenOnId = 4444;
+ int64_t screenOffId = 9876;
+ auto screenStateWithMap = CreateScreenStateWithOnOffMap(screenOnId, screenOffId);
+ *config.add_state() = screenStateWithMap;
+
+ // Create duration metric that slices by mapped screen state.
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("DurationBatterySaverModeSliceScreenMapped"));
+ durationMetric->set_what(batterySaverModePredicate.id());
+ durationMetric->add_slice_by_state(screenStateWithMap.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ // Initialize StatsLogProcessor.
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ EXPECT_TRUE(metricsManager->isActive());
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_TRUE(metricProducer->mIsActive);
+ ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID);
+ ASSERT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+ // Check that StateTrackers were initialized correctly.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+ /*
+ bucket #1 bucket #2
+ | 1 2 3 4 5 6 7 8 9 10 (minutes)
+ |-----------------------------|-----------------------------|--
+ ON OFF ON (BatterySaverMode)
+ ---------------------------------------------------------SCREEN_OFF events
+ | | (ScreenStateOffEvent = 1)
+ | (ScreenStateDozeEvent = 3)
+ | (ScreenStateDozeSuspendEvent = 4)
+ ---------------------------------------------------------SCREEN_ON events
+ | | | (ScreenStateOnEvent = 2)
+ | (ScreenStateVrEvent = 5)
+ | (ScreenStateOnSuspendEvent = 6)
+ */
+ // Initialize log events.
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 10 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 0:20
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 70 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 1:20
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 100 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_DOZE)); // 1:50
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 120 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 2:10
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 170 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_VR)); // 3:00
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 250 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF)); // 4:20
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50
+
+ // Bucket boundary 5:10.
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 320 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON)); // 5:30
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 390 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND)); // 6:40
+ events.push_back(CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 430 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND)); // 7:20
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 490 * NS_PER_SEC,
+ true /* include current partial bucket */, true, ADB_DUMP, FAST,
+ &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(2, durationMetrics.data_size());
+
+ DurationMetricData data = durationMetrics.data(0);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+ EXPECT_EQ(screenOnId, data.slice_by_state(0).group_id());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(130 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(110 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(1);
+ ASSERT_EQ(1, data.slice_by_state_size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+ EXPECT_EQ(screenOffId, data.slice_by_state(0).group_id());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(80 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(500 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestSlicedStatePrimaryFieldsNotSubsetDimInWhat) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto uidProcessState = CreateUidProcessState();
+ *config.add_state() = uidProcessState;
+
+ // Create duration metric that slices by uid process state.
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("DurationHoldingWakelockSliceUidProcessState"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->add_slice_by_state(uidProcessState.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ // The state has only one primary field (uid).
+ auto stateLink = durationMetric->add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+
+ // Initialize StatsLogProcessor.
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ // This config is rejected because the dimension in what fields are not a superset of the sliced
+ // state primary fields.
+ ASSERT_EQ(processor->mMetricsManagers.size(), 0);
+}
+
+TEST(DurationMetricE2eTest, TestWithSlicedStatePrimaryFieldsSubset) {
+ // Initialize config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto uidProcessState = CreateUidProcessState();
+ *config.add_state() = uidProcessState;
+
+ // Create duration metric that slices by uid process state.
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("DurationPartialWakelockPerTagUidSliceProcessState"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->add_slice_by_state(uidProcessState.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ // The metric is dimensioning by first uid of attribution node and tag.
+ *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidAndOtherDimensions(
+ util::WAKELOCK_STATE_CHANGED, {Position::FIRST}, {3 /* tag */});
+ // The state has only one primary field (uid).
+ auto stateLink = durationMetric->add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+
+ // Initialize StatsLogProcessor.
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ EXPECT_TRUE(metricsManager->isActive());
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_TRUE(metricProducer->mIsActive);
+ ASSERT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID);
+ ASSERT_EQ(metricProducer->mStateGroupMap.size(), 0);
+
+ // Check that StateTrackers were initialized correctly.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+ // Initialize log events.
+ int appUid1 = 1001;
+ int appUid2 = 1002;
+ std::vector<int> attributionUids1 = {appUid1};
+ std::vector<string> attributionTags1 = {"App1"};
+
+ std::vector<int> attributionUids2 = {appUid2};
+ std::vector<string> attributionTags2 = {"App2"};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 10 * NS_PER_SEC, appUid1,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND)); // 0:20
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 20 * NS_PER_SEC,
+ attributionUids1, attributionTags1,
+ "wakelock1")); // 0:30
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 25 * NS_PER_SEC,
+ attributionUids1, attributionTags1,
+ "wakelock2")); // 0:35
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 30 * NS_PER_SEC,
+ attributionUids2, attributionTags2,
+ "wakelock1")); // 0:40
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC,
+ attributionUids2, attributionTags2,
+ "wakelock2")); // 0:45
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 50 * NS_PER_SEC, appUid2,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:00
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 60 * NS_PER_SEC, appUid1,
+ android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND)); // 1:10
+ events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 100 * NS_PER_SEC,
+ attributionUids2, attributionTags2,
+ "wakelock1")); // 1:50
+ events.push_back(CreateUidProcessStateChangedEvent(
+ bucketStartTimeNs + 120 * NS_PER_SEC, appUid2,
+ android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE)); // 2:10
+ events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 200 * NS_PER_SEC,
+ attributionUids1, attributionTags1,
+ "wakelock2")); // 3:30
+
+ // Send log events to StatsLogProcessor.
+ for (auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ // Check dump report.
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 320 * NS_PER_SEC,
+ true /* include current partial bucket */, true, ADB_DUMP, FAST,
+ &buffer);
+ ASSERT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+ StatsLogReport::DurationMetricDataWrapper durationMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+ &durationMetrics);
+ ASSERT_EQ(9, durationMetrics.data_size());
+
+ DurationMetricData data = durationMetrics.data(0);
+ ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
+ "wakelock1");
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(1);
+ ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
+ "wakelock1");
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(240 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(2);
+ ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
+ "wakelock2");
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(3);
+ ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid1,
+ "wakelock2");
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(140 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(4);
+ ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
+ "wakelock1");
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(5);
+ ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
+ "wakelock1");
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(6);
+ ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
+ "wakelock2");
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(15 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(7);
+ ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
+ "wakelock2");
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(180 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).duration_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(1).start_bucket_elapsed_nanos());
+ EXPECT_EQ(330 * NS_PER_SEC, data.bucket_info(1).end_bucket_elapsed_nanos());
+
+ data = durationMetrics.data(8);
+ ValidateWakelockAttributionUidAndTagDimension(data.dimensions_in_what(), 10, appUid2,
+ "wakelock2");
+ ASSERT_EQ(1, data.slice_by_state_size());
+ 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(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(70 * NS_PER_SEC, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(310 * NS_PER_SEC, data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index c7ba9be..1be2612 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -12,13 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android/binder_interface_utils.h>
#include <gtest/gtest.h>
+#include <vector>
+
#include "src/StatsLogProcessor.h"
#include "src/stats_log_util.h"
#include "tests/statsd_test_util.h"
-#include <vector>
+using ::ndk::SharedRefBase;
namespace android {
namespace os {
@@ -29,12 +32,14 @@
namespace {
const int64_t metricId = 123456;
+const int32_t ATOM_TAG = util::SUBSYSTEM_SLEEP_STATE;
StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type,
bool useCondition = true) {
StatsdConfig config;
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
- auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+ config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
+ auto atomMatcher = CreateSimpleAtomMatcher("TestMatcher", ATOM_TAG);
*config.add_atom_matcher() = atomMatcher;
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
@@ -51,7 +56,7 @@
gaugeMetric->set_sampling_type(sampling_type);
gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
*gaugeMetric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
+ CreateDimensions(ATOM_TAG, {1 /* subsystem name */});
gaugeMetric->set_bucket(FIVE_MINUTES);
gaugeMetric->set_max_pull_delay_sec(INT_MAX);
config.set_hash_strings_in_metric_report(false);
@@ -59,67 +64,67 @@
return config;
}
-} // namespace
+} // namespaces
TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor =
+ CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
- int startBucketNum = processor->mMetricsManagers.begin()->second->
- mAllMetricProducers[0]->getCurrentBucketNum();
+ int startBucketNum = processor->mMetricsManagers.begin()
+ ->second->mAllMetricProducers[0]
+ ->getCurrentBucketNum();
EXPECT_GT(startBucketNum, (int64_t)0);
// When creating the config, the gauge metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+ ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
int64_t& nextPullTimeNs =
processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
- auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 55);
+ auto screenOffEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
// Pulling alarm arrives on time and reset the sequential pulling alarm.
processor->informPullAlarmFired(nextPullTimeNs + 1);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
- auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + bucketSizeNs + 10);
+ auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10,
+ android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + bucketSizeNs + 100);
+ screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100,
+ android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
processor->informPullAlarmFired(nextPullTimeNs + 1);
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs,
- nextPullTimeNs);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
processor->informPullAlarmFired(nextPullTimeNs + 1);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
- screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + 3 * bucketSizeNs + 2);
+ screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2,
+ android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
processor->informPullAlarmFired(nextPullTimeNs + 3);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 5 * bucketSizeNs + 1);
+ screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1,
+ android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
processor->informPullAlarmFired(nextPullTimeNs + 2);
@@ -136,70 +141,64 @@
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_GT((int)gaugeMetrics.data_size(), 1);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- EXPECT_EQ(6, data.bucket_info_size());
+ ASSERT_EQ(6, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).atom_size());
- EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(0).atom_size());
+ ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(1).atom_size());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1,
- data.bucket_info(1).elapsed_timestamp_nanos(0));
+ ASSERT_EQ(1, data.bucket_info(1).atom_size());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(2).atom_size());
- EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1,
- data.bucket_info(2).elapsed_timestamp_nanos(0));
+ ASSERT_EQ(1, data.bucket_info(2).atom_size());
+ ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(2).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(3).atom_size());
- EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1,
- data.bucket_info(3).elapsed_timestamp_nanos(0));
+ ASSERT_EQ(1, data.bucket_info(3).atom_size());
+ ASSERT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1, data.bucket_info(3).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(3).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(3).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(4).atom_size());
- EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1,
- data.bucket_info(4).elapsed_timestamp_nanos(0));
+ ASSERT_EQ(1, data.bucket_info(4).atom_size());
+ ASSERT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(4).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(4).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(4).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(5).atom_size());
- EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2,
- data.bucket_info(5).elapsed_timestamp_nanos(0));
+ ASSERT_EQ(1, data.bucket_info(5).atom_size());
+ ASSERT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2, data.bucket_info(5).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(5).atom(0).subsystem_sleep_state().subsystem_name().empty());
@@ -210,44 +209,45 @@
auto config = CreateStatsdConfig(GaugeMetric::CONDITION_CHANGE_TO_TRUE);
int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor =
+ CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
- int startBucketNum = processor->mMetricsManagers.begin()->second->
- mAllMetricProducers[0]->getCurrentBucketNum();
+ int startBucketNum = processor->mMetricsManagers.begin()
+ ->second->mAllMetricProducers[0]
+ ->getCurrentBucketNum();
EXPECT_GT(startBucketNum, (int64_t)0);
- auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 55);
+ auto screenOffEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
- auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + bucketSizeNs + 10);
+ auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10,
+ android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + bucketSizeNs + 100);
+ screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 100,
+ android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
- screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + 3 * bucketSizeNs + 2);
+ screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 2,
+ android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 5 * bucketSizeNs + 1);
+ screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 1,
+ android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
- screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + 5 * bucketSizeNs + 3);
+ screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 3,
+ android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 5 * bucketSizeNs + 10);
+ screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 5 * bucketSizeNs + 10,
+ android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
ConfigMetricsReportList reports;
@@ -259,45 +259,41 @@
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_GT((int)gaugeMetrics.data_size(), 1);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- EXPECT_EQ(3, data.bucket_info_size());
+ ASSERT_EQ(3, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).atom_size());
- EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(0).atom_size());
+ ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(1).atom_size());
- EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100,
- data.bucket_info(1).elapsed_timestamp_nanos(0));
+ ASSERT_EQ(1, data.bucket_info(1).atom_size());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100, data.bucket_info(1).elapsed_timestamp_nanos(0));
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(2, data.bucket_info(2).atom_size());
- EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1,
- data.bucket_info(2).elapsed_timestamp_nanos(0));
- EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10,
- data.bucket_info(2).elapsed_timestamp_nanos(1));
+ ASSERT_EQ(2, data.bucket_info(2).atom_size());
+ ASSERT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1, data.bucket_info(2).elapsed_timestamp_nanos(0));
+ EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10, data.bucket_info(2).elapsed_timestamp_nanos(1));
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
@@ -306,48 +302,48 @@
EXPECT_GT(data.bucket_info(2).atom(1).subsystem_sleep_state().time_millis(), 0);
}
-
TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor =
+ CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
- int startBucketNum = processor->mMetricsManagers.begin()->second->
- mAllMetricProducers[0]->getCurrentBucketNum();
+ int startBucketNum = processor->mMetricsManagers.begin()
+ ->second->mAllMetricProducers[0]
+ ->getCurrentBucketNum();
EXPECT_GT(startBucketNum, (int64_t)0);
// When creating the config, the gauge metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+ ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
int64_t& nextPullTimeNs =
processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
- auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 55);
+ auto screenOffEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
- auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + bucketSizeNs + 10);
+ auto screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + bucketSizeNs + 10,
+ android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
// Pulling alarm arrives one bucket size late.
processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 3 * bucketSizeNs + 11);
+ screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 3 * bucketSizeNs + 11,
+ android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
// Pulling alarm arrives more than one bucket size late.
@@ -363,30 +359,29 @@
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_GT((int)gaugeMetrics.data_size(), 1);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- EXPECT_EQ(3, data.bucket_info_size());
+ ASSERT_EQ(3, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).atom_size());
- EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(0).atom_size());
+ ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(1).atom_size());
+ ASSERT_EQ(1, data.bucket_info(1).atom_size());
EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11,
data.bucket_info(1).elapsed_timestamp_nanos(0));
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
@@ -395,10 +390,9 @@
EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(2).atom_size());
- EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12,
- data.bucket_info(2).elapsed_timestamp_nanos(0));
+ ASSERT_EQ(1, data.bucket_info(2).atom_size());
+ ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12, data.bucket_info(2).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
@@ -410,12 +404,11 @@
int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher();
*config.add_atom_matcher() = batterySaverStartMatcher;
- const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
+ const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
auto metric_activation = config.add_metric_activation();
metric_activation->set_metric_id(metricId);
metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
@@ -424,20 +417,22 @@
event_activation->set_ttl_seconds(ttlNs / 1000000000);
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor =
+ CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(), ATOM_TAG);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
- int startBucketNum = processor->mMetricsManagers.begin()->second->
- mAllMetricProducers[0]->getCurrentBucketNum();
+ int startBucketNum = processor->mMetricsManagers.begin()
+ ->second->mAllMetricProducers[0]
+ ->getCurrentBucketNum();
EXPECT_GT(startBucketNum, (int64_t)0);
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
// When creating the config, the gauge metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+ ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
int64_t& nextPullTimeNs =
@@ -446,27 +441,26 @@
// Pulling alarm arrives on time and reset the sequential pulling alarm.
// Event should not be kept.
- processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns.
+ processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
// Activate the metric. A pull occurs upon activation.
- const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
+ const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
- processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
+ processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
// This event should be kept. 2 total.
- processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns.
- EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs,
- nextPullTimeNs);
+ processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns.
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
// This event should be kept. 3 total.
- processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns.
+ processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
// Create random event to deactivate metric.
- auto deactivationEvent = CreateScreenBrightnessChangedEvent(50, activationNs + ttlNs + 1);
+ auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50);
processor->OnLogEvent(deactivationEvent.get());
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
@@ -485,50 +479,49 @@
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- EXPECT_GT((int)gaugeMetrics.data_size(), 0);
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+ ASSERT_GT((int)gaugeMetrics.data_size(), 0);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- EXPECT_EQ(3, data.bucket_info_size());
+ ASSERT_EQ(3, data.bucket_info_size());
auto bucketInfo = data.bucket_info(0);
- EXPECT_EQ(1, bucketInfo.atom_size());
- EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, bucketInfo.atom_size());
+ ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
+ ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
bucketInfo = data.bucket_info(1);
- EXPECT_EQ(1, bucketInfo.atom_size());
- EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, bucketInfo.atom_size());
+ ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
+ ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
bucketInfo = data.bucket_info(2);
- EXPECT_EQ(1, bucketInfo.atom_size());
- EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, bucketInfo.atom_size());
+ ASSERT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
+ ASSERT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)),
- bucketInfo.start_bucket_elapsed_nanos());
+ bucketInfo.start_bucket_elapsed_nanos());
EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)),
- bucketInfo.end_bucket_elapsed_nanos());
+ bucketInfo.end_bucket_elapsed_nanos());
EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
}
@@ -542,9 +535,10 @@
TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ ATOM_TAG);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
@@ -554,7 +548,7 @@
// When creating the config, the gauge metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+ ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
int64_t& nextPullTimeNs =
@@ -578,43 +572,43 @@
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
sortMetricDataByDimensionsValue(
reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- EXPECT_GT((int)gaugeMetrics.data_size(), 0);
+ ASSERT_GT((int)gaugeMetrics.data_size(), 0);
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(ATOM_TAG, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- EXPECT_EQ(3, data.bucket_info_size());
+ ASSERT_EQ(3, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).atom_size());
- EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(0).atom_size());
+ ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(1).atom_size());
- EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(1).atom_size());
+ ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1, data.bucket_info(1).elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, data.bucket_info(1).wall_clock_timestamp_nanos_size());
+ ASSERT_EQ(0, data.bucket_info(1).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(2).atom_size());
- EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(2).atom_size());
+ ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 4, data.bucket_info(2).elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, data.bucket_info(2).wall_clock_timestamp_nanos_size());
+ ASSERT_EQ(0, data.bucket_info(2).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
EXPECT_TRUE(data.bucket_info(2).atom(0).subsystem_sleep_state().subsystem_name().empty());
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index cd80310..a40a948 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -14,12 +14,13 @@
#include <gtest/gtest.h>
+#include <vector>
+
#include "src/StatsLogProcessor.h"
#include "src/stats_log_util.h"
+#include "stats_event.h"
#include "tests/statsd_test_util.h"
-#include <vector>
-
namespace android {
namespace os {
namespace statsd {
@@ -34,12 +35,12 @@
*config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
*config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
- auto atomMatcher = CreateSimpleAtomMatcher("", android::util::APP_START_OCCURRED);
+ auto atomMatcher = CreateSimpleAtomMatcher("", util::APP_START_OCCURRED);
*config.add_atom_matcher() = atomMatcher;
auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
*isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
*config.add_predicate() = isInBackgroundPredicate;
auto gaugeMetric = config.add_gauge_metric();
@@ -49,39 +50,43 @@
gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false);
gaugeMetric->set_sampling_type(sampling_type);
auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
- fieldMatcher->set_field(android::util::APP_START_OCCURRED);
+ fieldMatcher->set_field(util::APP_START_OCCURRED);
fieldMatcher->add_child()->set_field(3); // type (enum)
fieldMatcher->add_child()->set_field(4); // activity_name(str)
fieldMatcher->add_child()->set_field(7); // activity_start_msec(int64)
*gaugeMetric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::APP_START_OCCURRED, {1 /* uid field */ });
+ CreateDimensions(util::APP_START_OCCURRED, {1 /* uid field */ });
gaugeMetric->set_bucket(FIVE_MINUTES);
auto links = gaugeMetric->add_links();
links->set_condition(isInBackgroundPredicate.id());
auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(android::util::APP_START_OCCURRED);
+ dimensionWhat->set_field(util::APP_START_OCCURRED);
dimensionWhat->add_child()->set_field(1); // uid field.
auto dimensionCondition = links->mutable_fields_in_condition();
- dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
dimensionCondition->add_child()->set_field(1); // uid field.
return config;
}
std::unique_ptr<LogEvent> CreateAppStartOccurredEvent(
- const int uid, const string& pkg_name, AppStartOccurred::TransitionType type,
- const string& activity_name, const string& calling_pkg_name, const bool is_instant_app,
- int64_t activity_start_msec, uint64_t timestampNs) {
- auto logEvent = std::make_unique<LogEvent>(
- android::util::APP_START_OCCURRED, timestampNs);
- logEvent->write(uid);
- logEvent->write(pkg_name);
- logEvent->write(type);
- logEvent->write(activity_name);
- logEvent->write(calling_pkg_name);
- logEvent->write(is_instant_app);
- logEvent->write(activity_start_msec);
- logEvent->init();
+ uint64_t timestampNs, const int uid, const string& pkg_name,
+ AppStartOccurred::TransitionType type, const string& activity_name,
+ const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::APP_START_OCCURRED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_writeString(statsEvent, pkg_name.c_str());
+ AStatsEvent_writeInt32(statsEvent, type);
+ AStatsEvent_writeString(statsEvent, activity_name.c_str());
+ AStatsEvent_writeString(statsEvent, calling_pkg_name.c_str());
+ AStatsEvent_writeInt32(statsEvent, is_instant_app);
+ AStatsEvent_writeInt32(statsEvent, activity_start_msec);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
return logEvent;
}
@@ -89,58 +94,57 @@
TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent) {
for (const auto& sampling_type :
- { GaugeMetric::FIRST_N_SAMPLES, GaugeMetric:: RANDOM_ONE_SAMPLE }) {
+ {GaugeMetric::FIRST_N_SAMPLES, GaugeMetric::RANDOM_ONE_SAMPLE}) {
auto config = CreateStatsdConfigForPushedEvent(sampling_type);
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+ TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor =
+ CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
int appUid1 = 123;
int appUid2 = 456;
std::vector<std::unique_ptr<LogEvent>> events;
- events.push_back(CreateMoveToBackgroundEvent(appUid1, bucketStartTimeNs + 15));
- events.push_back(CreateMoveToForegroundEvent(
- appUid1, bucketStartTimeNs + bucketSizeNs + 250));
- events.push_back(CreateMoveToBackgroundEvent(
- appUid1, bucketStartTimeNs + bucketSizeNs + 350));
- events.push_back(CreateMoveToForegroundEvent(
- appUid1, bucketStartTimeNs + 2 * bucketSizeNs + 100));
-
+ events.push_back(CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid1));
+ events.push_back(
+ CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid1));
+ events.push_back(
+ CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid1));
+ events.push_back(
+ CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100, appUid1));
events.push_back(CreateAppStartOccurredEvent(
- appUid1, "app1", AppStartOccurred::WARM, "activity_name1", "calling_pkg_name1",
- true /*is_instant_app*/, 101 /*activity_start_msec*/, bucketStartTimeNs + 10));
+ bucketStartTimeNs + 10, appUid1, "app1", AppStartOccurred::WARM, "activity_name1",
+ "calling_pkg_name1", true /*is_instant_app*/, 101 /*activity_start_msec*/));
events.push_back(CreateAppStartOccurredEvent(
- appUid1, "app1", AppStartOccurred::HOT, "activity_name2", "calling_pkg_name2",
- true /*is_instant_app*/, 102 /*activity_start_msec*/, bucketStartTimeNs + 20));
+ bucketStartTimeNs + 20, appUid1, "app1", AppStartOccurred::HOT, "activity_name2",
+ "calling_pkg_name2", true /*is_instant_app*/, 102 /*activity_start_msec*/));
events.push_back(CreateAppStartOccurredEvent(
- appUid1, "app1", AppStartOccurred::COLD, "activity_name3", "calling_pkg_name3",
- true /*is_instant_app*/, 103 /*activity_start_msec*/, bucketStartTimeNs + 30));
+ bucketStartTimeNs + 30, appUid1, "app1", AppStartOccurred::COLD, "activity_name3",
+ "calling_pkg_name3", true /*is_instant_app*/, 103 /*activity_start_msec*/));
events.push_back(CreateAppStartOccurredEvent(
- appUid1, "app1", AppStartOccurred::WARM, "activity_name4", "calling_pkg_name4",
- true /*is_instant_app*/, 104 /*activity_start_msec*/,
- bucketStartTimeNs + bucketSizeNs + 30));
+ bucketStartTimeNs + bucketSizeNs + 30, appUid1, "app1", AppStartOccurred::WARM,
+ "activity_name4", "calling_pkg_name4", true /*is_instant_app*/,
+ 104 /*activity_start_msec*/));
events.push_back(CreateAppStartOccurredEvent(
- appUid1, "app1", AppStartOccurred::COLD, "activity_name5", "calling_pkg_name5",
- true /*is_instant_app*/, 105 /*activity_start_msec*/,
- bucketStartTimeNs + 2 * bucketSizeNs));
+ bucketStartTimeNs + 2 * bucketSizeNs, appUid1, "app1", AppStartOccurred::COLD,
+ "activity_name5", "calling_pkg_name5", true /*is_instant_app*/,
+ 105 /*activity_start_msec*/));
events.push_back(CreateAppStartOccurredEvent(
- appUid1, "app1", AppStartOccurred::HOT, "activity_name6", "calling_pkg_name6",
- false /*is_instant_app*/, 106 /*activity_start_msec*/,
- bucketStartTimeNs + 2 * bucketSizeNs + 10));
+ bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid1, "app1", AppStartOccurred::HOT,
+ "activity_name6", "calling_pkg_name6", false /*is_instant_app*/,
+ 106 /*activity_start_msec*/));
- events.push_back(CreateMoveToBackgroundEvent(
- appUid2, bucketStartTimeNs + bucketSizeNs + 10));
+ events.push_back(
+ CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 10, appUid2));
events.push_back(CreateAppStartOccurredEvent(
- appUid2, "app2", AppStartOccurred::COLD, "activity_name7", "calling_pkg_name7",
- true /*is_instant_app*/, 201 /*activity_start_msec*/,
- bucketStartTimeNs + 2 * bucketSizeNs + 10));
+ bucketStartTimeNs + 2 * bucketSizeNs + 10, appUid2, "app2", AppStartOccurred::COLD,
+ "activity_name7", "calling_pkg_name7", true /*is_instant_app*/,
+ 201 /*activity_start_msec*/));
sortLogEventsByTimestamp(&events);
@@ -149,31 +153,31 @@
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
- EXPECT_EQ(2, gaugeMetrics.data_size());
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(),
+ &gaugeMetrics);
+ ASSERT_EQ(2, gaugeMetrics.data_size());
auto data = gaugeMetrics.data(0);
- EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::APP_START_OCCURRED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(appUid1, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(3, data.bucket_info_size());
+ ASSERT_EQ(3, data.bucket_info_size());
if (sampling_type == GaugeMetric::FIRST_N_SAMPLES) {
- EXPECT_EQ(2, data.bucket_info(0).atom_size());
- EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+ ASSERT_EQ(2, data.bucket_info(0).atom_size());
+ ASSERT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
data.bucket_info(0).end_bucket_elapsed_nanos());
@@ -190,8 +194,8 @@
EXPECT_EQ(103L,
data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis());
- EXPECT_EQ(1, data.bucket_info(1).atom_size());
- EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(1).atom_size());
+ ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
@@ -203,8 +207,8 @@
EXPECT_EQ(104L,
data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
- EXPECT_EQ(2, data.bucket_info(2).atom_size());
- EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(2, data.bucket_info(2).atom_size());
+ ASSERT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
@@ -222,8 +226,8 @@
EXPECT_EQ(106L,
data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis());
} else {
- EXPECT_EQ(1, data.bucket_info(0).atom_size());
- EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(0).atom_size());
+ ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
data.bucket_info(0).end_bucket_elapsed_nanos());
@@ -234,8 +238,8 @@
EXPECT_EQ(102L,
data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
- EXPECT_EQ(1, data.bucket_info(1).atom_size());
- EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(1).atom_size());
+ ASSERT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
@@ -247,8 +251,8 @@
EXPECT_EQ(104L,
data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
- EXPECT_EQ(1, data.bucket_info(2).atom_size());
- EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info(2).atom_size());
+ ASSERT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
@@ -263,14 +267,14 @@
data = gaugeMetrics.data(1);
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().field(), util::APP_START_OCCURRED);
+ ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(appUid2, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).atom_size());
- EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+ ASSERT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info(0).atom_size());
+ ASSERT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
index f1b6029..e320419 100644
--- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp
@@ -45,7 +45,7 @@
countMetric->set_what(crashMatcher.id());
countMetric->set_bucket(FIVE_MINUTES);
countMetric->mutable_dimensions_in_what()->set_field(
- android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
auto metric_activation1 = config.add_metric_activation();
@@ -79,7 +79,7 @@
countMetric->set_what(crashMatcher.id());
countMetric->set_bucket(FIVE_MINUTES);
countMetric->mutable_dimensions_in_what()->set_field(
- android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
auto metric_activation1 = config.add_metric_activation();
@@ -117,7 +117,7 @@
countMetric->set_what(crashMatcher.id());
countMetric->set_bucket(FIVE_MINUTES);
countMetric->mutable_dimensions_in_what()->set_field(
- android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
auto metric_activation1 = config.add_metric_activation();
@@ -153,7 +153,7 @@
countMetric->set_what(crashMatcher.id());
countMetric->set_bucket(FIVE_MINUTES);
countMetric->mutable_dimensions_in_what()->set_field(
- android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
auto metric_activation1 = config.add_metric_activation();
@@ -194,7 +194,7 @@
countMetric->set_what(crashMatcher.id());
countMetric->set_bucket(FIVE_MINUTES);
countMetric->mutable_dimensions_in_what()->set_field(
- android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
int64_t metricId2 = 234567;
@@ -203,7 +203,7 @@
countMetric->set_what(foregroundMatcher.id());
countMetric->set_bucket(FIVE_MINUTES);
countMetric->mutable_dimensions_in_what()->set_field(
- android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ util::ACTIVITY_FOREGROUND_STATE_CHANGED);
countMetric->mutable_dimensions_in_what()->add_child()->set_field(1); // uid field
auto metric_activation1 = config.add_metric_activation();
@@ -236,7 +236,7 @@
TEST(MetricActivationE2eTest, TestCountMetric) {
auto config = CreateStatsdConfig();
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
@@ -252,24 +252,25 @@
long timeBase1 = 1;
int broadcastCount = 0;
- StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
- bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+ StatsLogProcessor processor(
+ m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
+ [](const ConfigKey& key) { return true; },
[&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
+ const vector<int64_t>& activeConfigs) {
broadcastCount++;
EXPECT_EQ(broadcastUid, uid);
activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
- activeConfigs.begin(), activeConfigs.end());
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
+ activeConfigs.end());
return true;
});
processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
- EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
@@ -277,7 +278,7 @@
EXPECT_FALSE(metricProducer->mIsActive);
// Two activations: one is triggered by battery saver mode (tracker index 0), the other is
// triggered by screen on event (tracker index 2).
- EXPECT_EQ(eventActivationMap.size(), 2u);
+ ASSERT_EQ(eventActivationMap.size(), 2u);
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
@@ -289,19 +290,19 @@
std::unique_ptr<LogEvent> event;
- event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 0);
// Activated by battery save mode.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
@@ -311,13 +312,12 @@
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// First processed event.
- event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Activated by screen on event.
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 20);
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -329,8 +329,8 @@
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
- event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -343,17 +343,17 @@
EXPECT_EQ(broadcastCount, 1);
// 3rd processed event.
- event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
// All activations expired.
- event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 2);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -362,13 +362,13 @@
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// Re-activate metric via screen on.
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
+ android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
@@ -378,55 +378,53 @@
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
// 4th processed event.
- event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(4, reports.reports(0).metrics(0).count_metrics().data_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- EXPECT_EQ(4, countMetrics.data_size());
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(4, countMetrics.data_size());
auto data = countMetrics.data(0);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(1);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(2);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
// Partial bucket as metric is deactivated.
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -434,23 +432,22 @@
data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(3);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
- data.bucket_info(0).end_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
}
TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) {
auto config = CreateStatsdConfigWithOneDeactivation();
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
@@ -466,24 +463,25 @@
long timeBase1 = 1;
int broadcastCount = 0;
- StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
- bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+ StatsLogProcessor processor(
+ m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
+ [](const ConfigKey& key) { return true; },
[&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
+ const vector<int64_t>& activeConfigs) {
broadcastCount++;
EXPECT_EQ(broadcastUid, uid);
activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
- activeConfigs.begin(), activeConfigs.end());
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
+ activeConfigs.end());
return true;
});
processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
- EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
@@ -492,7 +490,7 @@
EXPECT_FALSE(metricProducer->mIsActive);
// Two activations: one is triggered by battery saver mode (tracker index 0), the other is
// triggered by screen on event (tracker index 2).
- EXPECT_EQ(eventActivationMap.size(), 2u);
+ ASSERT_EQ(eventActivationMap.size(), 2u);
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
@@ -501,26 +499,26 @@
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap.size(), 1u);
+ ASSERT_EQ(eventDeactivationMap.size(), 1u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
- EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
+ ASSERT_EQ(eventDeactivationMap[3].size(), 1u);
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
std::unique_ptr<LogEvent> event;
- event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 0);
// Activated by battery save mode.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
@@ -531,13 +529,12 @@
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
// First processed event.
- event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Activated by screen on event.
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 20);
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -550,8 +547,8 @@
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
- event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -565,17 +562,17 @@
EXPECT_EQ(broadcastCount, 1);
// 3rd processed event.
- event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
// All activations expired.
- event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 2);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -585,13 +582,13 @@
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
// Re-activate metric via screen on.
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
+ android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
@@ -602,16 +599,16 @@
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
// 4th processed event.
- event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
@@ -622,16 +619,16 @@
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
// 5th processed event.
- event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
// Cancel battery saver mode activation.
- event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
- processor.OnLogEvent(event.get());
+ event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
@@ -642,13 +639,13 @@
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
// Screen-on activation expired.
- event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 4);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -657,16 +654,16 @@
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
- event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 5);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
@@ -677,12 +674,12 @@
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
// Cancel battery saver mode activation.
- event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
- processor.OnLogEvent(event.get());
+ event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 6);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -694,49 +691,47 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- EXPECT_EQ(5, countMetrics.data_size());
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(5, countMetrics.data_size());
auto data = countMetrics.data(0);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(1);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(2);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
// Partial bucket as metric is deactivated.
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -744,12 +739,12 @@
data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(3);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -757,12 +752,12 @@
data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(4);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -773,7 +768,7 @@
TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) {
auto config = CreateStatsdConfigWithTwoDeactivations();
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
@@ -789,24 +784,25 @@
long timeBase1 = 1;
int broadcastCount = 0;
- StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
- bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+ StatsLogProcessor processor(
+ m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
+ [](const ConfigKey& key) { return true; },
[&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
+ const vector<int64_t>& activeConfigs) {
broadcastCount++;
EXPECT_EQ(broadcastUid, uid);
activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
- activeConfigs.begin(), activeConfigs.end());
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
+ activeConfigs.end());
return true;
});
processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
- EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
@@ -815,7 +811,7 @@
EXPECT_FALSE(metricProducer->mIsActive);
// Two activations: one is triggered by battery saver mode (tracker index 0), the other is
// triggered by screen on event (tracker index 2).
- EXPECT_EQ(eventActivationMap.size(), 2u);
+ ASSERT_EQ(eventActivationMap.size(), 2u);
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
@@ -824,29 +820,29 @@
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap.size(), 2u);
+ ASSERT_EQ(eventDeactivationMap.size(), 2u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
- EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
- EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
+ ASSERT_EQ(eventDeactivationMap[3].size(), 1u);
+ ASSERT_EQ(eventDeactivationMap[4].size(), 1u);
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
std::unique_ptr<LogEvent> event;
- event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 0);
// Activated by battery save mode.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
@@ -858,13 +854,12 @@
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// First processed event.
- event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Activated by screen on event.
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 20);
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -878,8 +873,8 @@
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
- event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -894,17 +889,17 @@
EXPECT_EQ(broadcastCount, 1);
// 3rd processed event.
- event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
// All activations expired.
- event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 2);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -915,13 +910,13 @@
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// Re-activate metric via screen on.
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
+ android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
@@ -933,16 +928,16 @@
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// 4th processed event.
- event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
@@ -954,17 +949,17 @@
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// 5th processed event.
- event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
// Cancel battery saver mode and screen on activation.
- event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
- processor.OnLogEvent(event.get());
+ event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 4);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -975,12 +970,12 @@
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// Screen-on activation expired.
- event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 4);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -990,16 +985,16 @@
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 5);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
@@ -1011,12 +1006,12 @@
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
// Cancel battery saver mode and screen on activation.
- event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
- processor.OnLogEvent(event.get());
+ event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 6);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_EQ(eventActivationMap[0]->ttl_ns, 60 * 6 * NS_PER_SEC);
@@ -1029,49 +1024,47 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- EXPECT_EQ(5, countMetrics.data_size());
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(5, countMetrics.data_size());
auto data = countMetrics.data(0);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(1);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(2);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
// Partial bucket as metric is deactivated.
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -1079,12 +1072,12 @@
data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(3);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -1092,12 +1085,12 @@
data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(4);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -1108,7 +1101,7 @@
TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) {
auto config = CreateStatsdConfigWithSameDeactivations();
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
@@ -1124,24 +1117,25 @@
long timeBase1 = 1;
int broadcastCount = 0;
- StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
- bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+ StatsLogProcessor processor(
+ m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
+ [](const ConfigKey& key) { return true; },
[&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
+ const vector<int64_t>& activeConfigs) {
broadcastCount++;
EXPECT_EQ(broadcastUid, uid);
activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
- activeConfigs.begin(), activeConfigs.end());
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
+ activeConfigs.end());
return true;
});
processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
- EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 1);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
@@ -1150,7 +1144,7 @@
EXPECT_FALSE(metricProducer->mIsActive);
// Two activations: one is triggered by battery saver mode (tracker index 0), the other is
// triggered by screen on event (tracker index 2).
- EXPECT_EQ(eventActivationMap.size(), 2u);
+ ASSERT_EQ(eventActivationMap.size(), 2u);
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
@@ -1159,9 +1153,9 @@
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap.size(), 1u);
+ ASSERT_EQ(eventDeactivationMap.size(), 1u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
- EXPECT_EQ(eventDeactivationMap[3].size(), 2u);
+ ASSERT_EQ(eventDeactivationMap[3].size(), 2u);
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[3][1], eventActivationMap[2]);
EXPECT_EQ(broadcastCount, 0);
@@ -1169,28 +1163,28 @@
std::unique_ptr<LogEvent> event;
// Event that should be ignored.
- event = CreateAppCrashEvent(111, bucketStartTimeNs + 1);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 1, 111);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 1);
// Activate metric via screen on for 2 minutes.
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + 10, android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
// 1st processed event.
- event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Enable battery saver mode activation for 5 minutes.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 1);
@@ -1200,112 +1194,111 @@
EXPECT_EQ(eventActivationMap[2]->start_ns, bucketStartTimeNs + 10);
// 2nd processed event.
- event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 + 40);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 + 40, 333);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 + 40);
// Cancel battery saver mode and screen on activation.
int64_t firstDeactivation = bucketStartTimeNs + NS_PER_SEC * 61;
- event = CreateScreenBrightnessChangedEvent(64, firstDeactivation);
- processor.OnLogEvent(event.get());
+ event = CreateScreenBrightnessChangedEvent(firstDeactivation, 64);
+ processor.OnLogEvent(event.get(), firstDeactivation);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 2);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
// Should be ignored
- event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 61 + 80);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 61 + 80, 444);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 61 + 80);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 15);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
// 3rd processed event.
- event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80, 555);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80);
// Cancel battery saver mode activation.
int64_t secondDeactivation = bucketStartTimeNs + NS_PER_SEC * 60 * 13;
- event = CreateScreenBrightnessChangedEvent(140, secondDeactivation);
- processor.OnLogEvent(event.get());
+ event = CreateScreenBrightnessChangedEvent(secondDeactivation, 140);
+ processor.OnLogEvent(event.get(), secondDeactivation);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(broadcastCount, 4);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
// Should be ignored.
- event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80, 666);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- EXPECT_EQ(3, countMetrics.data_size());
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(3, countMetrics.data_size());
auto data = countMetrics.data(0);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(1);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(firstDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(2);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(555, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
// Partial bucket as metric is deactivated.
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+ data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(secondDeactivation, data.bucket_info(0).end_bucket_elapsed_nanos());
}
TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) {
auto config = CreateStatsdConfigWithTwoMetricsTwoDeactivations();
- int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
+ int64_t bucketStartTimeNs = NS_PER_SEC * 10; // 10 secs
int64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
@@ -1321,24 +1314,25 @@
long timeBase1 = 1;
int broadcastCount = 0;
- StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
- bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+ StatsLogProcessor processor(
+ m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, bucketStartTimeNs,
+ [](const ConfigKey& key) { return true; },
[&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
- const vector<int64_t>& activeConfigs) {
+ const vector<int64_t>& activeConfigs) {
broadcastCount++;
EXPECT_EQ(broadcastUid, uid);
activeConfigsBroadcast.clear();
- activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
- activeConfigs.begin(), activeConfigs.end());
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
+ activeConfigs.end());
return true;
});
processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config);
- EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor.mMetricsManagers.size(), 1u);
sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
EXPECT_TRUE(metricsManager->isConfigValid());
- EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 2);
+ ASSERT_EQ(metricsManager->mAllMetricProducers.size(), 2);
sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
auto& eventActivationMap = metricProducer->mEventActivationMap;
auto& eventDeactivationMap = metricProducer->mEventDeactivationMap;
@@ -1351,7 +1345,7 @@
EXPECT_FALSE(metricProducer2->mIsActive);
// Two activations: one is triggered by battery saver mode (tracker index 0), the other is
// triggered by screen on event (tracker index 2).
- EXPECT_EQ(eventActivationMap.size(), 2u);
+ ASSERT_EQ(eventActivationMap.size(), 2u);
EXPECT_TRUE(eventActivationMap.find(0) != eventActivationMap.end());
EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
@@ -1360,15 +1354,15 @@
EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap.size(), 2u);
+ ASSERT_EQ(eventDeactivationMap.size(), 2u);
EXPECT_TRUE(eventDeactivationMap.find(3) != eventDeactivationMap.end());
EXPECT_TRUE(eventDeactivationMap.find(4) != eventDeactivationMap.end());
- EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
- EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
+ ASSERT_EQ(eventDeactivationMap[3].size(), 1u);
+ ASSERT_EQ(eventDeactivationMap[4].size(), 1u);
EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]);
EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]);
- EXPECT_EQ(eventActivationMap2.size(), 2u);
+ ASSERT_EQ(eventActivationMap2.size(), 2u);
EXPECT_TRUE(eventActivationMap2.find(0) != eventActivationMap2.end());
EXPECT_TRUE(eventActivationMap2.find(2) != eventActivationMap2.end());
EXPECT_EQ(eventActivationMap2[0]->state, ActivationState::kNotActive);
@@ -1377,20 +1371,20 @@
EXPECT_EQ(eventActivationMap2[2]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap2[2]->start_ns, 0);
EXPECT_EQ(eventActivationMap2[2]->ttl_ns, 60 * 2 * NS_PER_SEC);
- EXPECT_EQ(eventDeactivationMap2.size(), 2u);
+ ASSERT_EQ(eventDeactivationMap2.size(), 2u);
EXPECT_TRUE(eventDeactivationMap2.find(3) != eventDeactivationMap2.end());
EXPECT_TRUE(eventDeactivationMap2.find(4) != eventDeactivationMap2.end());
- EXPECT_EQ(eventDeactivationMap[3].size(), 1u);
- EXPECT_EQ(eventDeactivationMap[4].size(), 1u);
+ ASSERT_EQ(eventDeactivationMap[3].size(), 1u);
+ ASSERT_EQ(eventDeactivationMap[4].size(), 1u);
EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
std::unique_ptr<LogEvent> event;
- event = CreateAppCrashEvent(111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(1111, bucketStartTimeNs + 5);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 5, 111);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + 5, 1111);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 5);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_FALSE(metricProducer2->mIsActive);
@@ -1398,10 +1392,10 @@
// Activated by battery save mode.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + 10);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 1);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -1423,15 +1417,14 @@
EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// First processed event.
- event = CreateAppCrashEvent(222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(2222, bucketStartTimeNs + 15);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + 15, 222);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + 15, 2222);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 15);
// Activated by screen on event.
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + 20);
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + 20, android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + 20);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -1454,10 +1447,10 @@
// 2nd processed event.
// The activation by screen_on event expires, but the one by battery save mode is still active.
- event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(3333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 333);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25, 3333);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -1481,20 +1474,20 @@
EXPECT_EQ(broadcastCount, 1);
// 3rd processed event.
- event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(4444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 444);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25, 4444);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25);
// All activations expired.
- event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(5555, bucketStartTimeNs + NS_PER_SEC * 60 * 8);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 555);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 8, 5555);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8);
EXPECT_FALSE(metricsManager->isActive());
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 2);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + 10);
@@ -1515,12 +1508,12 @@
EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// Re-activate metric via screen on.
- event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
- processor.OnLogEvent(event.get());
+ event = CreateScreenStateChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10,
+ android::view::DISPLAY_STATE_ON);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
@@ -1542,17 +1535,17 @@
EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// 4th processed event.
- event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(6666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 666);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1, 6666);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 3);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -1574,18 +1567,18 @@
EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// 5th processed event.
- event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
- processor.OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(7777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 777);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40, 7777);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40);
// Cancel battery saver mode and screen on activation.
- event = CreateScreenBrightnessChangedEvent(64, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
- processor.OnLogEvent(event.get());
+ event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60, 64);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60);
EXPECT_FALSE(metricsManager->isActive());
// New broadcast since the config is no longer active.
EXPECT_EQ(broadcastCount, 4);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
@@ -1606,13 +1599,13 @@
EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// Screen-on activation expired.
- event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- processor.OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(8888, bucketStartTimeNs + NS_PER_SEC * 60 * 13);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 888);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 13, 8888);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 4);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 15);
@@ -1632,17 +1625,17 @@
EXPECT_EQ(eventDeactivationMap2[3][0], eventActivationMap2[0]);
EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
- event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
- processor.OnLogEvent(event.get());
- event = CreateMoveToForegroundEvent(9999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
- processor.OnLogEvent(event.get());
+ event = CreateAppCrashEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 999);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
+ event = CreateMoveToForegroundEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1, 9999);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1);
// Re-enable battery saver mode activation.
event = CreateBatterySaverOnEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
- processor.OnLogEvent(event.get());
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
EXPECT_TRUE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 5);
- EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 1);
EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
EXPECT_TRUE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive);
@@ -1664,11 +1657,11 @@
EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]);
// Cancel battery saver mode and screen on activation.
- event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16);
- processor.OnLogEvent(event.get());
+ event = CreateScreenBrightnessChangedEvent(bucketStartTimeNs + NS_PER_SEC * 60 * 16, 140);
+ processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16);
EXPECT_FALSE(metricsManager->isActive());
EXPECT_EQ(broadcastCount, 6);
- EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ ASSERT_EQ(activeConfigsBroadcast.size(), 0);
EXPECT_FALSE(metricProducer->mIsActive);
EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kNotActive);
EXPECT_EQ(eventActivationMap[0]->start_ns, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 15);
@@ -1691,51 +1684,48 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
processor.onDumpReport(cfgKey, bucketStartTimeNs + NS_PER_SEC * 60 * 15 + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ ADB_DUMP, FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(2, reports.reports(0).metrics_size());
- EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size());
- EXPECT_EQ(5, reports.reports(0).metrics(1).count_metrics().data_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(2, reports.reports(0).metrics_size());
StatsLogReport::CountMetricDataWrapper countMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).count_metrics(), &countMetrics);
- EXPECT_EQ(5, countMetrics.data_size());
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+ ASSERT_EQ(5, countMetrics.data_size());
auto data = countMetrics.data(0);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(1);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(2);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
// Partial bucket as metric is deactivated.
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -1743,12 +1733,12 @@
data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(3);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -1756,53 +1746,51 @@
data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(4);
- EXPECT_EQ(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + NS_PER_SEC * 60 * 11,
data.bucket_info(0).end_bucket_elapsed_nanos());
-
- countMetrics.clear_data();
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(1).count_metrics(), &countMetrics);
- EXPECT_EQ(5, countMetrics.data_size());
+ countMetrics.clear_data();
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(1).count_metrics(), &countMetrics);
+ ASSERT_EQ(5, countMetrics.data_size());
data = countMetrics.data(0);
- EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(2222, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(1);
- EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(3333, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(2);
- EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(4444, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
// Partial bucket as metric is deactivated.
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -1810,12 +1798,12 @@
data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(3);
- EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(6666, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
@@ -1823,12 +1811,12 @@
data.bucket_info(0).end_bucket_elapsed_nanos());
data = countMetrics.data(4);
- EXPECT_EQ(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::ACTIVITY_FOREGROUND_STATE_CHANGED, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* uid field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_EQ(7777, data.dimensions_in_what().value_tuple().dimensions_value(0).value_int());
- EXPECT_EQ(1, data.bucket_info_size());
+ ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(1, data.bucket_info(0).count());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
data.bucket_info(0).start_bucket_elapsed_nanos());
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 78fb391..5e77ee0 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -48,12 +48,12 @@
auto isSyncingPredicate = CreateIsSyncingPredicate();
auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
*syncDimension = CreateAttributionUidDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ util::SYNC_STATE_CHANGED, {Position::FIRST});
syncDimension->add_child()->set_field(2 /* name field*/);
auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
*isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+ CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
*config.add_predicate() = screenIsOffPredicate;
*config.add_predicate() = isSyncingPredicate;
@@ -72,26 +72,26 @@
countMetric->set_condition(combinationPredicate->id());
// The metric is dimensioning by uid only.
*countMetric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1});
+ CreateDimensions(util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1});
countMetric->set_bucket(FIVE_MINUTES);
// Links between crash atom and condition of app is in syncing.
auto links = countMetric->add_links();
links->set_condition(isSyncingPredicate.id());
auto dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ dimensionWhat->set_field(util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
*links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
- android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ util::SYNC_STATE_CHANGED, {Position::FIRST});
// Links between crash atom and condition of app is in background.
links = countMetric->add_links();
links->set_condition(isInBackgroundPredicate.id());
dimensionWhat = links->mutable_fields_in_what();
- dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ dimensionWhat->set_field(util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
auto dimensionCondition = links->mutable_fields_in_condition();
- dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
dimensionCondition->add_child()->set_field(1); // uid field.
return config;
}
@@ -103,56 +103,54 @@
auto config = CreateStatsdConfig();
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
ConfigKey cfgKey;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
int appUid = 123;
- auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1);
- auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201);
- auto crashEvent3= CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101);
+ auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid);
+ auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid);
+ auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid);
- auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51);
- auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299);
- auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001);
+ auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid);
+ auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid);
+ auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid);
- auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16);
- auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249);
+ auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid);
+ auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid);
- auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351);
- auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2);
+ auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid);
+ auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid);
- auto screenTurnedOnEvent =
- CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- bucketStartTimeNs + 2);
- auto screenTurnedOffEvent =
- CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 200);
+ auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
+ bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
auto screenTurnedOnEvent2 =
- CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- bucketStartTimeNs + 2 * bucketSizeNs - 100);
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- std::vector<AttributionNodeInternal> attributions = {
- CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
- auto syncOnEvent1 =
- CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
- auto syncOffEvent1 =
- CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
- auto syncOnEvent2 =
- CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+ std::vector<int> attributionUids = {appUid, appUid + 1};
+ std::vector<string> attributionTags = {"App1", "GMSCoreModule1"};
- auto moveToBackgroundEvent1 =
- CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
+ auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids,
+ attributionTags, "ReadEmail");
+ auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids,
+ attributionTags, "ReadEmail");
+ auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000,
+ attributionUids, attributionTags, "ReadDoc");
+
+ auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid);
auto moveToForegroundEvent1 =
- CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250);
+ CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid);
auto moveToBackgroundEvent2 =
- CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350);
+ CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid);
auto moveToForegroundEvent2 =
- CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1);
+ CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid);
/*
bucket #1 bucket #2
@@ -199,22 +197,22 @@
}
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
auto data = reports.reports(0).metrics(0).count_metrics().data(0);
// Validate dimension value.
- EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().field(), util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
// Uid field.
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
@@ -227,50 +225,51 @@
TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
int appUid = 123;
- auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1);
- auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201);
- auto crashEvent3 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101);
+ auto crashEvent1 = CreateAppCrashEvent(bucketStartTimeNs + 1, appUid);
+ auto crashEvent2 = CreateAppCrashEvent(bucketStartTimeNs + 201, appUid);
+ auto crashEvent3 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 101, appUid);
- auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51);
- auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299);
- auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001);
+ auto crashEvent4 = CreateAppCrashEvent(bucketStartTimeNs + 51, appUid);
+ auto crashEvent5 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 299, appUid);
+ auto crashEvent6 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 2001, appUid);
- auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16);
- auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249);
+ auto crashEvent7 = CreateAppCrashEvent(bucketStartTimeNs + 16, appUid);
+ auto crashEvent8 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 249, appUid);
- auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351);
- auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2);
+ auto crashEvent9 = CreateAppCrashEvent(bucketStartTimeNs + bucketSizeNs + 351, appUid);
+ auto crashEvent10 = CreateAppCrashEvent(bucketStartTimeNs + 2 * bucketSizeNs - 2, appUid);
auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 2);
+ bucketStartTimeNs + 2, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200);
+ bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
auto screenTurnedOnEvent2 =
- CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- bucketStartTimeNs + 2 * bucketSizeNs - 100);
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- std::vector<AttributionNodeInternal> attributions = {
- CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
- auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
- auto syncOffEvent1 =
- CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
- auto syncOnEvent2 =
- CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+ std::vector<int> attributionUids = {appUid, appUid + 1};
+ std::vector<string> attributionTags = {"App1", "GMSCoreModule1"};
- auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
+ auto syncOnEvent1 = CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids,
+ attributionTags, "ReadEmail");
+ auto syncOffEvent1 = CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids,
+ attributionTags, "ReadEmail");
+ auto syncOnEvent2 = CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs + 2000,
+ attributionUids, attributionTags, "ReadDoc");
+
+ auto moveToBackgroundEvent1 = CreateMoveToBackgroundEvent(bucketStartTimeNs + 15, appUid);
auto moveToForegroundEvent1 =
- CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250);
+ CreateMoveToForegroundEvent(bucketStartTimeNs + bucketSizeNs + 250, appUid);
auto moveToBackgroundEvent2 =
- CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350);
+ CreateMoveToBackgroundEvent(bucketStartTimeNs + bucketSizeNs + 350, appUid);
auto moveToForegroundEvent2 =
- CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1);
+ CreateMoveToForegroundEvent(bucketStartTimeNs + 2 * bucketSizeNs - 1, appUid);
/*
bucket #1 bucket #2
@@ -318,24 +317,23 @@
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2);
EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3);
auto data = reports.reports(0).metrics(0).count_metrics().data(0);
// Validate dimension value.
- EXPECT_EQ(data.dimensions_in_what().field(),
- android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
- EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().field(), util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
// Uid field.
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), appUid);
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 0bc3ebb..c03b925 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -12,45 +12,47 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android/binder_ibinder.h>
+#include <android/binder_interface_utils.h>
#include <gtest/gtest.h>
-#include <binder/IPCThreadState.h>
+#include <vector>
+
#include "src/StatsLogProcessor.h"
#include "src/StatsService.h"
#include "src/stats_log_util.h"
#include "tests/statsd_test_util.h"
-#include <vector>
+using::ndk::SharedRefBase;
+using std::shared_ptr;
namespace android {
namespace os {
namespace statsd {
#ifdef __ANDROID__
-
-const string kAndroid = "android";
+namespace {
const string kApp1 = "app1.sharing.1";
const int kConfigKey = 789130123; // Randomly chosen to avoid collisions with existing configs.
+const int kCallingUid = 0; // Randomly chosen
-void SendConfig(StatsService& service, const StatsdConfig& config) {
+void SendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) {
string str;
config.SerializeToString(&str);
std::vector<uint8_t> configAsVec(str.begin(), str.end());
- bool success;
- service.addConfiguration(kConfigKey, configAsVec, String16(kAndroid.c_str()));
+ service->addConfiguration(kConfigKey, configAsVec, kCallingUid);
}
ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp,
bool include_current = false) {
vector<uint8_t> output;
- IPCThreadState* ipc = IPCThreadState::self();
- ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
+ ConfigKey configKey(AIBinder_getCallingUid(), kConfigKey);
processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output);
ConfigMetricsReportList reports;
reports.ParseFromArray(output.data(), output.size());
EXPECT_EQ(1, reports.reports_size());
- return reports.reports(0);
+ return reports.reports(kCallingUid);
}
StatsdConfig MakeConfig() {
@@ -69,9 +71,10 @@
StatsdConfig MakeValueMetricConfig(int64_t minTime) {
StatsdConfig config;
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto pulledAtomMatcher =
- CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+ CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
*config.add_atom_matcher() = pulledAtomMatcher;
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
@@ -80,21 +83,23 @@
valueMetric->set_id(123456);
valueMetric->set_what(pulledAtomMatcher.id());
*valueMetric->mutable_value_field() =
- CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
*valueMetric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
valueMetric->set_bucket(FIVE_MINUTES);
valueMetric->set_min_bucket_size_nanos(minTime);
valueMetric->set_use_absolute_value_on_reset(true);
+ valueMetric->set_skip_zero_diff_output(false);
return config;
}
StatsdConfig MakeGaugeMetricConfig(int64_t minTime) {
StatsdConfig config;
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto pulledAtomMatcher =
- CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+ CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
*config.add_atom_matcher() = pulledAtomMatcher;
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
@@ -104,135 +109,192 @@
gaugeMetric->set_what(pulledAtomMatcher.id());
gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
*gaugeMetric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
gaugeMetric->set_bucket(FIVE_MINUTES);
gaugeMetric->set_min_bucket_size_nanos(minTime);
return config;
}
+} // anonymous namespace
TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 2, 100).get());
- ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
+ ConfigMetricsReport report = GetReports(service->mProcessor, start + 3);
// Expect no metrics since the bucket has not finished yet.
- EXPECT_EQ(1, report.metrics_size());
- EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(0, report.metrics(0).count_metrics().data_size());
}
TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
// Force the uidmap to update at timestamp 2.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
// This is a new installation, so there shouldn't be a split (should be same as the without
// split case).
- service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
- String16(""));
+ service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ String16(""));
// Goes into the second bucket.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get());
- ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
- EXPECT_EQ(1, report.metrics_size());
- EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
+ ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(0, report.metrics(0).count_metrics().data_size());
}
TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
- {String16("")});
+ service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+ {String16("")});
// Force the uidmap to update at timestamp 2.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
- service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
- String16(""));
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
+ service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ String16(""));
// Goes into the second bucket.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get());
- ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+ ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
- EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
- has_start_bucket_elapsed_nanos());
- EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
- has_end_bucket_elapsed_nanos());
+ EXPECT_TRUE(report.metrics(0)
+ .count_metrics()
+ .data(0)
+ .bucket_info(0)
+ .has_start_bucket_elapsed_nanos());
+ EXPECT_TRUE(report.metrics(0)
+ .count_metrics()
+ .data(0)
+ .bucket_info(0)
+ .has_end_bucket_elapsed_nanos());
EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
}
TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
SendConfig(service, MakeConfig());
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
- {String16("")});
+ service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+ {String16("")});
// Force the uidmap to update at timestamp 2.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
- service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 1, 100).get());
+ service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
// Goes into the second bucket.
- service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3, 100).get());
- ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+ ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
- EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
- has_start_bucket_elapsed_nanos());
- EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
- has_end_bucket_elapsed_nanos());
+ EXPECT_TRUE(report.metrics(0)
+ .count_metrics()
+ .data(0)
+ .bucket_info(0)
+ .has_start_bucket_elapsed_nanos());
+ EXPECT_TRUE(report.metrics(0)
+ .count_metrics()
+ .data(0)
+ .bucket_info(0)
+ .has_end_bucket_elapsed_nanos());
+ EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
+}
+
+TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot) {
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ SendConfig(service, MakeConfig());
+ int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
+ // initialized with.
+
+ // Goes into the first bucket
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + NS_PER_SEC, 100).get());
+ int64_t bootCompleteTimeNs = start + 2 * NS_PER_SEC;
+ service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs);
+ // Goes into the second bucket.
+ service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3 * NS_PER_SEC, 100).get());
+
+ ConfigMetricsReport report = GetReports(service->mProcessor, start + 4 * NS_PER_SEC);
+ backfillStartEndTimestamp(&report);
+
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
+ ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
+ EXPECT_TRUE(report.metrics(0)
+ .count_metrics()
+ .data(0)
+ .bucket_info(0)
+ .has_start_bucket_elapsed_nanos());
+ EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)),
+ report.metrics(0).count_metrics().data(0).bucket_info(0).end_bucket_elapsed_nanos());
EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
}
TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ service->mPullerManager->RegisterPullAtomCallback(
+ /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
+ SharedRefBase::make<FakeSubsystemSleepCallback>());
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+ service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeValueMetricConfig(0));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
- String16("v2"), String16(""));
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+ int64_t appUpgradeTimeNs = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC;
+ service->mUidMap->updateApp(appUpgradeTimeNs, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ String16(""));
ConfigMetricsReport report =
- GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
- EXPECT_EQ(1, report.metrics_size());
- EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size());
+ GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC);
+ backfillStartEndTimestamp(&report);
+
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(0, report.metrics(0).value_metrics().skipped_size());
+
+ // The fake subsystem state sleep puller returns two atoms.
+ ASSERT_EQ(2, report.metrics(0).value_metrics().data_size());
+ ASSERT_EQ(2, report.metrics(0).value_metrics().data(0).bucket_info_size());
+ EXPECT_EQ(MillisToNano(NanoToMillis(appUpgradeTimeNs)),
+ report.metrics(0).value_metrics().data(0).bucket_info(1).end_bucket_elapsed_nanos());
}
TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ service->mPullerManager->RegisterPullAtomCallback(
+ /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
+ SharedRefBase::make<FakeSubsystemSleepCallback>());
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+ service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
- service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC;
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+ service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
String16(""));
ConfigMetricsReport report =
- GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
+ GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
@@ -241,41 +303,86 @@
// Can't test the start time since it will be based on the actual time when the pulling occurs.
EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
+
+ ASSERT_EQ(2, report.metrics(0).value_metrics().data_size());
+ ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size());
+}
+
+TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) {
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ // Initial pull will fail since puller is not registered.
+ SendConfig(service, MakeValueMetricConfig(0));
+ int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
+ // initialized with.
+
+ service->mPullerManager->RegisterPullAtomCallback(
+ /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
+ SharedRefBase::make<FakeSubsystemSleepCallback>());
+
+ int64_t bootCompleteTimeNs = start + NS_PER_SEC;
+ service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs);
+
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+
+ ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100);
+ backfillStartEndTimestamp(&report);
+
+ // First bucket is dropped due to the initial pull failing
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size());
+ EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)),
+ report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos());
+
+ // The fake subsystem state sleep puller returns two atoms.
+ ASSERT_EQ(2, report.metrics(0).value_metrics().data_size());
+ ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size());
+ EXPECT_EQ(
+ MillisToNano(NanoToMillis(bootCompleteTimeNs)),
+ report.metrics(0).value_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos());
}
TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ service->mPullerManager->RegisterPullAtomCallback(
+ /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
+ SharedRefBase::make<FakeSubsystemSleepCallback>());
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+ service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
SendConfig(service, MakeGaugeMetricConfig(0));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
- service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+ service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
String16("v2"), String16(""));
- ConfigMetricsReport report =
- GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
- EXPECT_EQ(1, report.metrics_size());
- EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
+ ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100);
+ backfillStartEndTimestamp(&report);
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
+ // The fake subsystem state sleep puller returns two atoms.
+ ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size());
+ ASSERT_EQ(2, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
}
TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
- StatsService service(nullptr, nullptr);
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
// Partial buckets don't occur when app is first installed.
- service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+ service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+ service->mPullerManager->RegisterPullAtomCallback(
+ /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
+ SharedRefBase::make<FakeSubsystemSleepCallback>());
SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
// initialized with.
const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
- service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
- service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
- String16(""));
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+ service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+ String16(""));
ConfigMetricsReport report =
- GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
+ GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC);
backfillStartEndTimestamp(&report);
ASSERT_EQ(1, report.metrics_size());
ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
@@ -283,6 +390,38 @@
EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos());
+ ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size());
+ ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
+}
+
+TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) {
+ shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+ // Initial pull will fail since puller hasn't been registered.
+ SendConfig(service, MakeGaugeMetricConfig(0));
+ int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are
+ // initialized with.
+
+ service->mPullerManager->RegisterPullAtomCallback(
+ /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
+ SharedRefBase::make<FakeSubsystemSleepCallback>());
+
+ int64_t bootCompleteTimeNs = start + NS_PER_SEC;
+ service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs);
+
+ service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+
+ ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100);
+ backfillStartEndTimestamp(&report);
+
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
+ // The fake subsystem state sleep puller returns two atoms.
+ ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size());
+ // No data in the first bucket, so nothing is reported
+ ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size());
+ EXPECT_EQ(
+ MillisToNano(NanoToMillis(bootCompleteTimeNs)),
+ report.metrics(0).gauge_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos());
}
#else
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index fb878dc7..4d39282 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android/binder_interface_utils.h>
#include <gtest/gtest.h>
#include "src/StatsLogProcessor.h"
@@ -20,6 +21,8 @@
#include <vector>
+using ::ndk::SharedRefBase;
+
namespace android {
namespace os {
namespace statsd {
@@ -33,8 +36,9 @@
StatsdConfig CreateStatsdConfig(bool useCondition = true) {
StatsdConfig config;
config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
auto pulledAtomMatcher =
- CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+ CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
*config.add_atom_matcher() = pulledAtomMatcher;
*config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
*config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
@@ -49,9 +53,9 @@
valueMetric->set_condition(screenIsOffPredicate.id());
}
*valueMetric->mutable_value_field() =
- CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
*valueMetric->mutable_dimensions_in_what() =
- CreateDimensions(android::util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
valueMetric->set_bucket(FIVE_MINUTES);
valueMetric->set_use_absolute_value_on_reset(true);
valueMetric->set_skip_zero_diff_output(false);
@@ -59,45 +63,181 @@
return config;
}
+StatsdConfig CreateStatsdConfigWithStates() {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ config.add_default_pull_packages("AID_ROOT"); // Fake puller is registered with root.
+
+ auto pulledAtomMatcher = CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
+ *config.add_atom_matcher() = pulledAtomMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateBatteryStateNoneMatcher();
+ *config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
+
+ auto screenOnPredicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = screenOnPredicate;
+
+ auto screenOffPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = screenOffPredicate;
+
+ auto deviceUnpluggedPredicate = CreateDeviceUnpluggedPredicate();
+ *config.add_predicate() = deviceUnpluggedPredicate;
+
+ auto screenOnOnBatteryPredicate = config.add_predicate();
+ screenOnOnBatteryPredicate->set_id(StringToId("screenOnOnBatteryPredicate"));
+ screenOnOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
+ addPredicateToPredicateCombination(screenOnPredicate, screenOnOnBatteryPredicate);
+ addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOnOnBatteryPredicate);
+
+ auto screenOffOnBatteryPredicate = config.add_predicate();
+ screenOffOnBatteryPredicate->set_id(StringToId("ScreenOffOnBattery"));
+ screenOffOnBatteryPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
+ addPredicateToPredicateCombination(screenOffPredicate, screenOffOnBatteryPredicate);
+ addPredicateToPredicateCombination(deviceUnpluggedPredicate, screenOffOnBatteryPredicate);
+
+ const State screenState =
+ CreateScreenStateWithSimpleOnOffMap(/*screen on id=*/321, /*screen off id=*/123);
+ *config.add_state() = screenState;
+
+ // ValueMetricSubsystemSleepWhileScreenOnOnBattery
+ auto valueMetric1 = config.add_value_metric();
+ valueMetric1->set_id(metricId);
+ valueMetric1->set_what(pulledAtomMatcher.id());
+ valueMetric1->set_condition(screenOnOnBatteryPredicate->id());
+ *valueMetric1->mutable_value_field() =
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+ valueMetric1->set_bucket(FIVE_MINUTES);
+ valueMetric1->set_use_absolute_value_on_reset(true);
+ valueMetric1->set_skip_zero_diff_output(false);
+ valueMetric1->set_max_pull_delay_sec(INT_MAX);
+
+ // ValueMetricSubsystemSleepWhileScreenOffOnBattery
+ ValueMetric* valueMetric2 = config.add_value_metric();
+ valueMetric2->set_id(StringToId("ValueMetricSubsystemSleepWhileScreenOffOnBattery"));
+ valueMetric2->set_what(pulledAtomMatcher.id());
+ valueMetric2->set_condition(screenOffOnBatteryPredicate->id());
+ *valueMetric2->mutable_value_field() =
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+ valueMetric2->set_bucket(FIVE_MINUTES);
+ valueMetric2->set_use_absolute_value_on_reset(true);
+ valueMetric2->set_skip_zero_diff_output(false);
+ valueMetric2->set_max_pull_delay_sec(INT_MAX);
+
+ // ValueMetricSubsystemSleepWhileOnBatterySliceScreen
+ ValueMetric* valueMetric3 = config.add_value_metric();
+ valueMetric3->set_id(StringToId("ValueMetricSubsystemSleepWhileOnBatterySliceScreen"));
+ valueMetric3->set_what(pulledAtomMatcher.id());
+ valueMetric3->set_condition(deviceUnpluggedPredicate.id());
+ *valueMetric3->mutable_value_field() =
+ CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+ valueMetric3->add_slice_by_state(screenState.id());
+ valueMetric3->set_bucket(FIVE_MINUTES);
+ valueMetric3->set_use_absolute_value_on_reset(true);
+ valueMetric3->set_skip_zero_diff_output(false);
+ valueMetric3->set_max_pull_delay_sec(INT_MAX);
+ return config;
+}
+
} // namespace
+/**
+ * Tests the initial condition and condition after the first log events for
+ * value metrics with either a combination condition or simple condition.
+ *
+ * Metrics should be initialized with condition kUnknown (given that the
+ * predicate is using the default InitialValue of UNKNOWN). The condition should
+ * be updated to either kFalse or kTrue if a condition event is logged for all
+ * children conditions.
+ */
+TEST(ValueMetricE2eTest, TestInitialConditionChanges) {
+ StatsdConfig config = CreateStatsdConfigWithStates();
+ int64_t baseTimeNs = getElapsedRealtimeNs();
+ int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+ ConfigKey cfgKey;
+ int32_t tagId = util::SUBSYSTEM_SLEEP_STATE;
+ auto processor =
+ CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(), tagId);
+
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(3, metricsManager->mAllMetricProducers.size());
+
+ // Combination condition metric - screen on and device unplugged
+ sp<MetricProducer> metricProducer1 = metricsManager->mAllMetricProducers[0];
+ // Simple condition metric - device unplugged
+ sp<MetricProducer> metricProducer2 = metricsManager->mAllMetricProducers[2];
+
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
+
+ auto screenOnEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 30, android::view::DISPLAY_STATE_ON);
+ processor->OnLogEvent(screenOnEvent.get());
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
+
+ auto screenOffEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 40, android::view::DISPLAY_STATE_OFF);
+ processor->OnLogEvent(screenOffEvent.get());
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer1->mCondition);
+ EXPECT_EQ(ConditionState::kUnknown, metricProducer2->mCondition);
+
+ auto pluggedUsbEvent = CreateBatteryStateChangedEvent(
+ configAddedTimeNs + 50, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
+ processor->OnLogEvent(pluggedUsbEvent.get());
+ EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition);
+ EXPECT_EQ(ConditionState::kFalse, metricProducer2->mCondition);
+
+ auto pluggedNoneEvent = CreateBatteryStateChangedEvent(
+ configAddedTimeNs + 70, BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE);
+ processor->OnLogEvent(pluggedNoneEvent.get());
+ EXPECT_EQ(ConditionState::kFalse, metricProducer1->mCondition);
+ EXPECT_EQ(ConditionState::kTrue, metricProducer2->mCondition);
+}
+
TEST(ValueMetricE2eTest, TestPulledEvents) {
auto config = CreateStatsdConfig();
int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ util::SUBSYSTEM_SLEEP_STATE);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
- int startBucketNum = processor->mMetricsManagers.begin()->second->
- mAllMetricProducers[0]->getCurrentBucketNum();
+ int startBucketNum = processor->mMetricsManagers.begin()
+ ->second->mAllMetricProducers[0]
+ ->getCurrentBucketNum();
EXPECT_GT(startBucketNum, (int64_t)0);
// When creating the config, the value metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+ ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
int64_t& expectedPullTimeNs =
processor->mPullerManager->mReceivers.begin()->second.front().nextPullTimeNs;
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
- auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 55);
+ auto screenOffEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
- auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + 65);
+ auto screenOnEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 75);
+ screenOffEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
// Pulling alarm arrives on time and reset the sequential pulling alarm.
@@ -106,16 +246,16 @@
processor->informPullAlarmFired(expectedPullTimeNs + 1);
- screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + 2 * bucketSizeNs + 15);
+ screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 2 * bucketSizeNs + 15,
+ android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
processor->informPullAlarmFired(expectedPullTimeNs + 1);
processor->informPullAlarmFired(expectedPullTimeNs + 1);
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 4 * bucketSizeNs + 11);
+ screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 11,
+ android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
processor->informPullAlarmFired(expectedPullTimeNs + 1);
@@ -131,37 +271,36 @@
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::ValueMetricDataWrapper valueMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
- EXPECT_GT((int)valueMetrics.data_size(), 1);
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+ ASSERT_GT((int)valueMetrics.data_size(), 1);
auto data = valueMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
// We have 4 buckets, the first one was incomplete since the condition was unknown.
- EXPECT_EQ(4, data.bucket_info_size());
+ ASSERT_EQ(4, data.bucket_info_size());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, data.bucket_info(0).values_size());
+ ASSERT_EQ(1, data.bucket_info(0).values_size());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, data.bucket_info(1).values_size());
+ ASSERT_EQ(1, data.bucket_info(1).values_size());
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, data.bucket_info(2).values_size());
+ ASSERT_EQ(1, data.bucket_info(2).values_size());
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, data.bucket_info(3).values_size());
+ ASSERT_EQ(1, data.bucket_info(3).values_size());
}
TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
@@ -169,23 +308,24 @@
int64_t baseTimeNs = getElapsedRealtimeNs();
// 10 mins == 2 bucket durations.
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ util::SUBSYSTEM_SLEEP_STATE);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
- int startBucketNum = processor->mMetricsManagers.begin()->second->
- mAllMetricProducers[0]->getCurrentBucketNum();
+ int startBucketNum = processor->mMetricsManagers.begin()
+ ->second->mAllMetricProducers[0]
+ ->getCurrentBucketNum();
EXPECT_GT(startBucketNum, (int64_t)0);
// When creating the config, the value metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+ ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
int64_t& expectedPullTimeNs =
@@ -193,16 +333,16 @@
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
// Screen off/on/off events.
- auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 55);
+ auto screenOffEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
- auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + 65);
+ auto screenOnEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 65, android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 75);
+ screenOffEvent =
+ CreateScreenStateChangedEvent(configAddedTimeNs + 75, android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
// Pulling alarm arrives late by 2 buckets and 1 ns. 2 buckets late is too far away in the
@@ -211,8 +351,8 @@
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
// This screen state change will start a new bucket.
- screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
- configAddedTimeNs + 4 * bucketSizeNs + 65);
+ screenOnEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 4 * bucketSizeNs + 65,
+ android::view::DISPLAY_STATE_ON);
processor->OnLogEvent(screenOnEvent.get());
// The alarm is delayed but we already created a bucket thanks to the screen state condition.
@@ -220,8 +360,8 @@
processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
- screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
- configAddedTimeNs + 6 * bucketSizeNs + 31);
+ screenOffEvent = CreateScreenStateChangedEvent(configAddedTimeNs + 6 * bucketSizeNs + 31,
+ android::view::DISPLAY_STATE_OFF);
processor->OnLogEvent(screenOffEvent.get());
processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
@@ -239,44 +379,42 @@
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::ValueMetricDataWrapper valueMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
- EXPECT_GT((int)valueMetrics.data_size(), 1);
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+ ASSERT_GT((int)valueMetrics.data_size(), 1);
auto data = valueMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- EXPECT_EQ(3, data.bucket_info_size());
+ ASSERT_EQ(3, data.bucket_info_size());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, data.bucket_info(0).values_size());
+ ASSERT_EQ(1, data.bucket_info(0).values_size());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, data.bucket_info(1).values_size());
+ ASSERT_EQ(1, data.bucket_info(1).values_size());
EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, data.bucket_info(2).values_size());
+ ASSERT_EQ(1, data.bucket_info(2).values_size());
}
TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) {
auto config = CreateStatsdConfig(false);
int64_t baseTimeNs = getElapsedRealtimeNs();
int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
- int64_t bucketSizeNs =
- TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+ int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
auto batterySaverStartMatcher = CreateBatterySaverModeStartAtomMatcher();
*config.add_atom_matcher() = batterySaverStartMatcher;
- const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
+ const int64_t ttlNs = 2 * bucketSizeNs; // Two buckets.
auto metric_activation = config.add_metric_activation();
metric_activation->set_metric_id(metricId);
metric_activation->set_activation_type(ACTIVATE_IMMEDIATELY);
@@ -285,20 +423,22 @@
event_activation->set_ttl_seconds(ttlNs / 1000000000);
ConfigKey cfgKey;
- auto processor = CreateStatsLogProcessor(
- baseTimeNs, configAddedTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+ SharedRefBase::make<FakeSubsystemSleepCallback>(),
+ util::SUBSYSTEM_SLEEP_STATE);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
processor->mPullerManager->ForceClearPullerCache();
- int startBucketNum = processor->mMetricsManagers.begin()->second->
- mAllMetricProducers[0]->getCurrentBucketNum();
+ int startBucketNum = processor->mMetricsManagers.begin()
+ ->second->mAllMetricProducers[0]
+ ->getCurrentBucketNum();
EXPECT_GT(startBucketNum, (int64_t)0);
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
// When creating the config, the value metric producer should register the alarm at the
// end of the current bucket.
- EXPECT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
+ ASSERT_EQ((size_t)1, processor->mPullerManager->mReceivers.size());
EXPECT_EQ(bucketSizeNs,
processor->mPullerManager->mReceivers.begin()->second.front().intervalNs);
int64_t& expectedPullTimeNs =
@@ -306,24 +446,24 @@
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
// Pulling alarm arrives on time and reset the sequential pulling alarm.
- processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns.
+ processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
// Activate the metric. A pull occurs here
- const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
+ const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
- processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
+ processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
- processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns.
+ processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs);
- processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns.
+ processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
// Create random event to deactivate metric.
- auto deactivationEvent = CreateScreenBrightnessChangedEvent(50, activationNs + ttlNs + 1);
+ auto deactivationEvent = CreateScreenBrightnessChangedEvent(activationNs + ttlNs + 1, 50);
processor->OnLogEvent(deactivationEvent.get());
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
@@ -342,31 +482,192 @@
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(1, reports.reports_size());
- EXPECT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(1, reports.reports_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
StatsLogReport::ValueMetricDataWrapper valueMetrics;
- sortMetricDataByDimensionsValue(
- reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
- EXPECT_GT((int)valueMetrics.data_size(), 0);
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+ ASSERT_GT((int)valueMetrics.data_size(), 0);
auto data = valueMetrics.data(0);
- EXPECT_EQ(android::util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
- EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+ EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+ ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
// We have 2 full buckets, the two surrounding the activation are dropped.
- EXPECT_EQ(2, data.bucket_info_size());
+ ASSERT_EQ(2, data.bucket_info_size());
auto bucketInfo = data.bucket_info(0);
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_EQ(1, bucketInfo.values_size());
+ ASSERT_EQ(1, bucketInfo.values_size());
bucketInfo = data.bucket_info(1);
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
- EXPECT_EQ(1, bucketInfo.values_size());
+ ASSERT_EQ(1, bucketInfo.values_size());
+}
+
+/**
+ * Test initialization of a simple value metric that is sliced by a state.
+ *
+ * ValueCpuUserTimePerScreenState
+ */
+TEST(ValueMetricE2eTest, TestInitWithSlicedState) {
+ // Create config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto pulledAtomMatcher =
+ CreateSimpleAtomMatcher("TestMatcher", util::SUBSYSTEM_SLEEP_STATE);
+ *config.add_atom_matcher() = pulledAtomMatcher;
+
+ auto screenState = CreateScreenState();
+ *config.add_state() = screenState;
+
+ // Create value metric that slices by screen state without a map.
+ int64_t metricId = 123456;
+ auto valueMetric = config.add_value_metric();
+ valueMetric->set_id(metricId);
+ valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+ valueMetric->set_what(pulledAtomMatcher.id());
+ *valueMetric->mutable_value_field() =
+ CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
+ valueMetric->add_slice_by_state(screenState.id());
+ valueMetric->set_max_pull_delay_sec(INT_MAX);
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ const uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000LL;
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were initialized correctly.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+ // Check that ValueMetricProducer was initialized correctly.
+ ASSERT_EQ(1U, processor->mMetricsManagers.size());
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(1, metricsManager->mAllMetricProducers.size());
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ ASSERT_EQ(1, metricProducer->mSlicedStateAtoms.size());
+ EXPECT_EQ(SCREEN_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0));
+ ASSERT_EQ(0, metricProducer->mStateGroupMap.size());
+}
+
+/**
+ * Test initialization of a value metric that is sliced by state and has
+ * dimensions_in_what.
+ *
+ * ValueCpuUserTimePerUidPerUidProcessState
+ */
+TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions) {
+ // Create config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto cpuTimePerUidMatcher =
+ CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID);
+ *config.add_atom_matcher() = cpuTimePerUidMatcher;
+
+ auto uidProcessState = CreateUidProcessState();
+ *config.add_state() = uidProcessState;
+
+ // Create value metric that slices by screen state with a complete map.
+ int64_t metricId = 123456;
+ auto valueMetric = config.add_value_metric();
+ valueMetric->set_id(metricId);
+ valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+ valueMetric->set_what(cpuTimePerUidMatcher.id());
+ *valueMetric->mutable_value_field() =
+ CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
+ *valueMetric->mutable_dimensions_in_what() =
+ CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */});
+ valueMetric->add_slice_by_state(uidProcessState.id());
+ MetricStateLink* stateLink = valueMetric->add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+ valueMetric->set_max_pull_delay_sec(INT_MAX);
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were initialized correctly.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+ // Check that ValueMetricProducer was initialized correctly.
+ ASSERT_EQ(1U, processor->mMetricsManagers.size());
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ ASSERT_EQ(1, metricsManager->mAllMetricProducers.size());
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ ASSERT_EQ(1, metricProducer->mSlicedStateAtoms.size());
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0));
+ ASSERT_EQ(0, metricProducer->mStateGroupMap.size());
+}
+
+/**
+ * Test initialization of a value metric that is sliced by state and has
+ * dimensions_in_what.
+ *
+ * ValueCpuUserTimePerUidPerUidProcessState
+ */
+TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions) {
+ // Create config.
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto cpuTimePerUidMatcher =
+ CreateSimpleAtomMatcher("CpuTimePerUidMatcher", util::CPU_TIME_PER_UID);
+ *config.add_atom_matcher() = cpuTimePerUidMatcher;
+
+ auto uidProcessState = CreateUidProcessState();
+ *config.add_state() = uidProcessState;
+
+ // Create value metric that slices by screen state with a complete map.
+ int64_t metricId = 123456;
+ auto valueMetric = config.add_value_metric();
+ valueMetric->set_id(metricId);
+ valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+ valueMetric->set_what(cpuTimePerUidMatcher.id());
+ *valueMetric->mutable_value_field() =
+ CreateDimensions(util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
+ valueMetric->add_slice_by_state(uidProcessState.id());
+ MetricStateLink* stateLink = valueMetric->add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(util::CPU_TIME_PER_UID, {1 /* uid */});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+ valueMetric->set_max_pull_delay_sec(INT_MAX);
+
+ // Initialize StatsLogProcessor.
+ const uint64_t bucketStartTimeNs = 10000000000; // 0:10
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+ // No StateTrackers are initialized.
+ EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount());
+
+ // Config initialization fails.
+ ASSERT_EQ(0, processor->mMetricsManagers.size());
}
#else
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index e13bf14..52bc222 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -42,7 +42,7 @@
auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
// The predicate is dimensioning by any attribution node and both by uid and tag.
FieldMatcher dimensions = CreateAttributionUidAndTagDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST});
+ util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST});
// Also slice by the wakelock tag
dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock.
*holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
@@ -56,18 +56,16 @@
// The metric is dimensioning by first attribution node and only by uid.
*durationMetric->mutable_dimensions_in_what() =
CreateAttributionUidDimensions(
- android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
durationMetric->set_bucket(FIVE_MINUTES);
return config;
}
-std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
- CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
+std::vector<int> attributionUids1 = {111, 222, 222};
+std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
-std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"),
- CreateAttribution(222, "GMSCoreModule1"),
- CreateAttribution(222, "GMSCoreModule2")};
+std::vector<int> attributionUids2 = {111, 222, 222};
+std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
/*
Events:
@@ -81,20 +79,21 @@
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto screenTurnedOnEvent = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 1);
+ bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
auto screenTurnedOffEvent = CreateScreenStateChangedEvent(
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF, bucketStartTimeNs + 200);
+ bucketStartTimeNs + 200, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
auto screenTurnedOnEvent2 =
- CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
- bucketStartTimeNs + bucketSizeNs + 500);
+ CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 500,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- auto acquireEvent1 = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
- auto releaseEvent1 =
- CreateReleaseWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
- auto acquireEvent2 =
- CreateAcquireWakelockEvent(attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10);
- auto releaseEvent2 = CreateReleaseWakelockEvent(attributions2, "wl2",
- bucketStartTimeNs + 2 * bucketSizeNs - 15);
+ auto acquireEvent1 = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
+ attributionTags1, "wl1");
+ auto releaseEvent1 = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2,
+ attributionUids1, attributionTags1, "wl1");
+ auto acquireEvent2 = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 10,
+ attributionUids2, attributionTags2, "wl2");
+ auto releaseEvent2 = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 15,
+ attributionUids2, attributionTags2, "wl2");
std::vector<std::unique_ptr<LogEvent>> events;
@@ -122,30 +121,30 @@
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
vector<uint8_t> buffer;
ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
// Only 1 dimension output. The tag dimension in the predicate has been aggregated.
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
// Validate dimension value.
ValidateAttributionUidDimension(data.dimensions_in_what(),
- android::util::WAKELOCK_STATE_CHANGED, 111);
+ util::WAKELOCK_STATE_CHANGED, 111);
// Validate bucket info.
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
data = reports.reports(0).metrics(0).duration_metrics().data(0);
// The wakelock holding interval starts from the screen off event and to the end of the 1st
// bucket.
@@ -159,27 +158,27 @@
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
vector<uint8_t> buffer;
ConfigMetricsReportList reports;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
// Dump the report after the end of 2nd bucket.
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
// Validate dimension value.
ValidateAttributionUidDimension(data.dimensions_in_what(),
- android::util::WAKELOCK_STATE_CHANGED, 111);
+ util::WAKELOCK_STATE_CHANGED, 111);
// Two output buckets.
// The wakelock holding interval in the 1st bucket starts from the screen off event and to
// the end of the 1st bucket.
@@ -189,6 +188,7 @@
// ends at the second screen on event.
EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
}
+
TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3) {
ConfigKey cfgKey;
auto config = CreateStatsdConfig(DurationMetric::SUM);
@@ -196,7 +196,7 @@
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
vector<uint8_t> buffer;
@@ -204,31 +204,31 @@
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(
- CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 2 * bucketSizeNs + 90));
- events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3",
- bucketStartTimeNs + 2 * bucketSizeNs + 100));
- events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3",
- bucketStartTimeNs + 5 * bucketSizeNs + 100));
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
+ attributionUids1, attributionTags1, "wl3"));
+ events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100,
+ attributionUids1, attributionTags1, "wl3"));
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 6);
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
ValidateAttributionUidDimension(data.dimensions_in_what(),
- android::util::WAKELOCK_STATE_CHANGED, 111);
+ util::WAKELOCK_STATE_CHANGED, 111);
// The last wakelock holding spans 4 buckets.
EXPECT_EQ((unsigned long long)data.bucket_info(2).duration_nanos(), bucketSizeNs - 100);
EXPECT_EQ((unsigned long long)data.bucket_info(3).duration_nanos(), bucketSizeNs);
@@ -243,13 +243,13 @@
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -257,13 +257,13 @@
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports_size(), 1);
// When using ProtoOutputStream, if nothing written to a sub msg, it won't be treated as
// one. It was previsouly 1 because we had a fake onDumpReport which calls add_metric() by
// itself.
- EXPECT_EQ(1, reports.reports(0).metrics_size());
- EXPECT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size());
+ ASSERT_EQ(1, reports.reports(0).metrics_size());
+ ASSERT_EQ(0, reports.reports(0).metrics(0).duration_metrics().data_size());
}
TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForMaxDuration2) {
@@ -273,27 +273,27 @@
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
// Dump the report after the end of 2nd bucket. One dimension with one bucket.
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
// Validate dimension value.
ValidateAttributionUidDimension(data.dimensions_in_what(),
- android::util::WAKELOCK_STATE_CHANGED, 111);
+ util::WAKELOCK_STATE_CHANGED, 111);
// The max is acquire event for wl1 to screen off start.
EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs + 2 - 200);
}
@@ -305,7 +305,7 @@
uint64_t bucketSizeNs =
TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
- EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
FeedEvents(config, processor);
ConfigMetricsReportList reports;
@@ -313,31 +313,31 @@
std::vector<std::unique_ptr<LogEvent>> events;
events.push_back(
- CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- bucketStartTimeNs + 2 * bucketSizeNs + 90));
- events.push_back(CreateAcquireWakelockEvent(attributions1, "wl3",
- bucketStartTimeNs + 2 * bucketSizeNs + 100));
- events.push_back(CreateReleaseWakelockEvent(attributions1, "wl3",
- bucketStartTimeNs + 5 * bucketSizeNs + 100));
+ CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 90,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF));
+ events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 100,
+ attributionUids1, attributionTags1, "wl3"));
+ events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs + 100,
+ attributionUids1, attributionTags1, "wl3"));
sortLogEventsByTimestamp(&events);
for (const auto& event : events) {
processor->OnLogEvent(event.get());
}
- processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true,
- ADB_DUMP, FAST, &buffer);
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, true, ADB_DUMP,
+ FAST, &buffer);
EXPECT_TRUE(buffer.size() > 0);
EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
backfillDimensionPath(&reports);
backfillStringInReport(&reports);
backfillStartEndTimestamp(&reports);
- EXPECT_EQ(reports.reports_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
- EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
+ ASSERT_EQ(reports.reports_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+ ASSERT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
ValidateAttributionUidDimension(data.dimensions_in_what(),
- android::util::WAKELOCK_STATE_CHANGED, 111);
+ util::WAKELOCK_STATE_CHANGED, 111);
// The last wakelock holding spans 4 buckets.
EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 3 * bucketSizeNs);
EXPECT_EQ((unsigned long long)data.bucket_info(1).start_bucket_elapsed_nanos(),
diff --git a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp
deleted file mode 100644
index 3240918..0000000
--- a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "GpuStatsPuller_test"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include <graphicsenv/GpuStatsInfo.h>
-#include <log/log.h>
-
-#include "src/external/GpuStatsPuller.h"
-#include "statslog.h"
-
-#ifdef __ANDROID__
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// clang-format off
-static const std::string DRIVER_PACKAGE_NAME = "TEST_DRIVER";
-static const std::string DRIVER_VERSION_NAME = "TEST_DRIVER_VERSION";
-static const std::string APP_PACKAGE_NAME = "TEST_APP";
-static const int64_t TIMESTAMP_WALLCLOCK = 111;
-static const int64_t TIMESTAMP_ELAPSED = 222;
-static const int64_t DRIVER_VERSION_CODE = 333;
-static const int64_t DRIVER_BUILD_TIME = 444;
-static const int64_t GL_LOADING_COUNT = 3;
-static const int64_t GL_LOADING_FAILURE_COUNT = 1;
-static const int64_t VK_LOADING_COUNT = 4;
-static const int64_t VK_LOADING_FAILURE_COUNT = 0;
-static const int64_t ANGLE_LOADING_COUNT = 2;
-static const int64_t ANGLE_LOADING_FAILURE_COUNT = 1;
-static const int64_t GL_DRIVER_LOADING_TIME_0 = 555;
-static const int64_t GL_DRIVER_LOADING_TIME_1 = 666;
-static const int64_t VK_DRIVER_LOADING_TIME_0 = 777;
-static const int64_t VK_DRIVER_LOADING_TIME_1 = 888;
-static const int64_t VK_DRIVER_LOADING_TIME_2 = 999;
-static const int64_t ANGLE_DRIVER_LOADING_TIME_0 = 1010;
-static const int64_t ANGLE_DRIVER_LOADING_TIME_1 = 1111;
-static const int32_t VULKAN_VERSION = 1;
-static const int32_t CPU_VULKAN_VERSION = 2;
-static const int32_t GLES_VERSION = 3;
-static const bool CPU_VULKAN_IN_USE = true;
-static const size_t NUMBER_OF_VALUES_GLOBAL = 13;
-static const size_t NUMBER_OF_VALUES_APP = 6;
-// clang-format on
-
-class MockGpuStatsPuller : public GpuStatsPuller {
-public:
- MockGpuStatsPuller(const int tagId, vector<std::shared_ptr<LogEvent>>* data)
- : GpuStatsPuller(tagId), mData(data){};
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override {
- *data = *mData;
- return true;
- }
-
- vector<std::shared_ptr<LogEvent>>* mData;
-};
-
-class GpuStatsPuller_test : public ::testing::Test {
-public:
- GpuStatsPuller_test() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- }
-
- ~GpuStatsPuller_test() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
- }
-};
-
-TEST_F(GpuStatsPuller_test, PullGpuStatsGlobalInfo) {
- vector<std::shared_ptr<LogEvent>> inData, outData;
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(android::util::GPU_STATS_GLOBAL_INFO,
- TIMESTAMP_WALLCLOCK, TIMESTAMP_ELAPSED);
- EXPECT_TRUE(event->write(DRIVER_PACKAGE_NAME));
- EXPECT_TRUE(event->write(DRIVER_VERSION_NAME));
- EXPECT_TRUE(event->write(DRIVER_VERSION_CODE));
- EXPECT_TRUE(event->write(DRIVER_BUILD_TIME));
- EXPECT_TRUE(event->write(GL_LOADING_COUNT));
- EXPECT_TRUE(event->write(GL_LOADING_FAILURE_COUNT));
- EXPECT_TRUE(event->write(VK_LOADING_COUNT));
- EXPECT_TRUE(event->write(VK_LOADING_FAILURE_COUNT));
- EXPECT_TRUE(event->write(VULKAN_VERSION));
- EXPECT_TRUE(event->write(CPU_VULKAN_VERSION));
- EXPECT_TRUE(event->write(GLES_VERSION));
- EXPECT_TRUE(event->write(ANGLE_LOADING_COUNT));
- EXPECT_TRUE(event->write(ANGLE_LOADING_FAILURE_COUNT));
- event->init();
- inData.emplace_back(event);
- MockGpuStatsPuller mockPuller(android::util::GPU_STATS_GLOBAL_INFO, &inData);
- mockPuller.ForceClearCache();
- mockPuller.Pull(&outData);
-
- ASSERT_EQ(1, outData.size());
- EXPECT_EQ(android::util::GPU_STATS_GLOBAL_INFO, outData[0]->GetTagId());
- ASSERT_EQ(NUMBER_OF_VALUES_GLOBAL, outData[0]->size());
- EXPECT_EQ(DRIVER_PACKAGE_NAME, outData[0]->getValues()[0].mValue.str_value);
- EXPECT_EQ(DRIVER_VERSION_NAME, outData[0]->getValues()[1].mValue.str_value);
- EXPECT_EQ(DRIVER_VERSION_CODE, outData[0]->getValues()[2].mValue.long_value);
- EXPECT_EQ(DRIVER_BUILD_TIME, outData[0]->getValues()[3].mValue.long_value);
- EXPECT_EQ(GL_LOADING_COUNT, outData[0]->getValues()[4].mValue.long_value);
- EXPECT_EQ(GL_LOADING_FAILURE_COUNT, outData[0]->getValues()[5].mValue.long_value);
- EXPECT_EQ(VK_LOADING_COUNT, outData[0]->getValues()[6].mValue.long_value);
- EXPECT_EQ(VK_LOADING_FAILURE_COUNT, outData[0]->getValues()[7].mValue.long_value);
- EXPECT_EQ(VULKAN_VERSION, outData[0]->getValues()[8].mValue.int_value);
- EXPECT_EQ(CPU_VULKAN_VERSION, outData[0]->getValues()[9].mValue.int_value);
- EXPECT_EQ(GLES_VERSION, outData[0]->getValues()[10].mValue.int_value);
- EXPECT_EQ(ANGLE_LOADING_COUNT, outData[0]->getValues()[11].mValue.long_value);
- EXPECT_EQ(ANGLE_LOADING_FAILURE_COUNT, outData[0]->getValues()[12].mValue.long_value);
-}
-
-TEST_F(GpuStatsPuller_test, PullGpuStatsAppInfo) {
- vector<std::shared_ptr<LogEvent>> inData, outData;
- std::shared_ptr<LogEvent> event = make_shared<LogEvent>(android::util::GPU_STATS_APP_INFO,
- TIMESTAMP_WALLCLOCK, TIMESTAMP_ELAPSED);
- EXPECT_TRUE(event->write(APP_PACKAGE_NAME));
- EXPECT_TRUE(event->write(DRIVER_VERSION_CODE));
- std::vector<int64_t> glDriverLoadingTime;
- glDriverLoadingTime.emplace_back(GL_DRIVER_LOADING_TIME_0);
- glDriverLoadingTime.emplace_back(GL_DRIVER_LOADING_TIME_1);
- std::vector<int64_t> vkDriverLoadingTime;
- vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_0);
- vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_1);
- vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_2);
- std::vector<int64_t> angleDriverLoadingTime;
- angleDriverLoadingTime.emplace_back(ANGLE_DRIVER_LOADING_TIME_0);
- angleDriverLoadingTime.emplace_back(ANGLE_DRIVER_LOADING_TIME_1);
- EXPECT_TRUE(event->write(int64VectorToProtoByteString(glDriverLoadingTime)));
- EXPECT_TRUE(event->write(int64VectorToProtoByteString(vkDriverLoadingTime)));
- EXPECT_TRUE(event->write(int64VectorToProtoByteString(angleDriverLoadingTime)));
- EXPECT_TRUE(event->write(CPU_VULKAN_IN_USE));
- event->init();
- inData.emplace_back(event);
- MockGpuStatsPuller mockPuller(android::util::GPU_STATS_APP_INFO, &inData);
- mockPuller.ForceClearCache();
- mockPuller.Pull(&outData);
-
- ASSERT_EQ(1, outData.size());
- EXPECT_EQ(android::util::GPU_STATS_APP_INFO, outData[0]->GetTagId());
- ASSERT_EQ(NUMBER_OF_VALUES_APP, outData[0]->size());
- EXPECT_EQ(APP_PACKAGE_NAME, outData[0]->getValues()[0].mValue.str_value);
- EXPECT_EQ(DRIVER_VERSION_CODE, outData[0]->getValues()[1].mValue.long_value);
- EXPECT_EQ(int64VectorToProtoByteString(glDriverLoadingTime),
- outData[0]->getValues()[2].mValue.str_value);
- EXPECT_EQ(int64VectorToProtoByteString(vkDriverLoadingTime),
- outData[0]->getValues()[3].mValue.str_value);
- EXPECT_EQ(int64VectorToProtoByteString(angleDriverLoadingTime),
- outData[0]->getValues()[4].mValue.str_value);
- EXPECT_EQ(CPU_VULKAN_IN_USE, outData[0]->getValues()[5].mValue.int_value);
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#else
-GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
diff --git a/cmds/statsd/tests/external/IncidentReportArgs_test.cpp b/cmds/statsd/tests/external/IncidentReportArgs_test.cpp
deleted file mode 100644
index 38bc194..0000000
--- a/cmds/statsd/tests/external/IncidentReportArgs_test.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <android/os/IncidentReportArgs.h>
-
-#include <gtest/gtest.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-TEST(IncidentReportArgsTest, testSerialization) {
- IncidentReportArgs args;
- args.setAll(0);
- args.addSection(1000);
- args.addSection(1001);
-
- vector<uint8_t> header1;
- header1.push_back(0x1);
- header1.push_back(0x2);
- vector<uint8_t> header2;
- header1.push_back(0x22);
- header1.push_back(0x33);
-
- args.addHeader(header1);
- args.addHeader(header2);
-
- args.setPrivacyPolicy(1);
-
- args.setReceiverPkg("com.android.os");
- args.setReceiverCls("com.android.os.Receiver");
-
- Parcel out;
- status_t err = args.writeToParcel(&out);
- EXPECT_EQ(NO_ERROR, err);
-
- out.setDataPosition(0);
-
- IncidentReportArgs args2;
- err = args2.readFromParcel(&out);
- EXPECT_EQ(NO_ERROR, err);
-
- EXPECT_EQ(0, args2.all());
- set<int> sections;
- sections.insert(1000);
- sections.insert(1001);
- EXPECT_EQ(sections, args2.sections());
- EXPECT_EQ(1, args2.getPrivacyPolicy());
-
- EXPECT_EQ(string("com.android.os"), args2.receiverPkg());
- EXPECT_EQ(string("com.android.os.Receiver"), args2.receiverCls());
-
- vector<vector<uint8_t>> headers;
- headers.push_back(header1);
- headers.push_back(header2);
- EXPECT_EQ(headers, args2.headers());
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
new file mode 100644
index 0000000..85a6088
--- /dev/null
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -0,0 +1,219 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/external/StatsCallbackPuller.h"
+
+#include <aidl/android/os/BnPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+
+#include <chrono>
+#include <thread>
+#include <vector>
+
+#include "../metrics/metrics_test_helper.h"
+#include "src/stats_log_util.h"
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnPullAtomCallback;
+using aidl::android::os::IPullAtomResultReceiver;
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using std::this_thread::sleep_for;
+using testing::Contains;
+
+namespace {
+int pullTagId = -12;
+bool pullSuccess;
+vector<int64_t> values;
+int64_t pullDelayNs;
+int64_t pullTimeoutNs;
+int64_t pullCoolDownNs;
+std::thread pullThread;
+
+AStatsEvent* createSimpleEvent(int64_t value) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, pullTagId);
+ AStatsEvent_writeInt64(event, value);
+ AStatsEvent_build(event);
+ return event;
+}
+
+void executePull(const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
+ // Convert stats_events into StatsEventParcels.
+ vector<StatsEventParcel> parcels;
+ for (int i = 0; i < values.size(); i++) {
+ AStatsEvent* event = createSimpleEvent(values[i]);
+ size_t size;
+ uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
+
+ StatsEventParcel p;
+ // vector.assign() creates a copy, but this is inevitable unless
+ // stats_event.h/c uses a vector as opposed to a buffer.
+ p.buffer.assign(buffer, buffer + size);
+ parcels.push_back(std::move(p));
+ AStatsEvent_release(event);
+ }
+
+ sleep_for(std::chrono::nanoseconds(pullDelayNs));
+ resultReceiver->pullFinished(pullTagId, pullSuccess, parcels);
+}
+
+class FakePullAtomCallback : public BnPullAtomCallback {
+public:
+ Status onPullAtom(int atomTag,
+ const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
+ // Force pull to happen in separate thread to simulate binder.
+ pullThread = std::thread(executePull, resultReceiver);
+ return Status::ok();
+ }
+};
+
+class StatsCallbackPullerTest : public ::testing::Test {
+public:
+ StatsCallbackPullerTest() {
+ }
+
+ void SetUp() override {
+ pullSuccess = false;
+ pullDelayNs = 0;
+ values.clear();
+ pullTimeoutNs = 10000000000LL; // 10 seconds.
+ pullCoolDownNs = 1000000000; // 1 second.
+ }
+
+ void TearDown() override {
+ if (pullThread.joinable()) {
+ pullThread.join();
+ }
+ values.clear();
+ }
+};
+} // Anonymous namespace.
+
+TEST_F(StatsCallbackPullerTest, PullSuccess) {
+ shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
+ int64_t value = 43;
+ pullSuccess = true;
+ values.push_back(value);
+
+ StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ int64_t startTimeNs = getElapsedRealtimeNs();
+ EXPECT_TRUE(puller.PullInternal(&dataHolder));
+ int64_t endTimeNs = getElapsedRealtimeNs();
+
+ ASSERT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_LT(startTimeNs, dataHolder[0]->GetElapsedTimestampNs());
+ EXPECT_GT(endTimeNs, dataHolder[0]->GetElapsedTimestampNs());
+ ASSERT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(value, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsCallbackPullerTest, PullFail) {
+ shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
+ pullSuccess = false;
+ int64_t value = 1234;
+ values.push_back(value);
+
+ StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
+
+ vector<shared_ptr<LogEvent>> dataHolder;
+ EXPECT_FALSE(puller.PullInternal(&dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsCallbackPullerTest, PullTimeout) {
+ shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
+ pullSuccess = true;
+ pullDelayNs = MillisToNano(5); // 5ms.
+ pullTimeoutNs = 10000; // 10 microseconds.
+ int64_t value = 4321;
+ values.push_back(value);
+
+ StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
+
+ vector<shared_ptr<LogEvent>> dataHolder;
+ int64_t startTimeNs = getElapsedRealtimeNs();
+ // Returns true to let StatsPuller code evaluate the timeout.
+ EXPECT_TRUE(puller.PullInternal(&dataHolder));
+ int64_t endTimeNs = getElapsedRealtimeNs();
+ int64_t actualPullDurationNs = endTimeNs - startTimeNs;
+
+ // Pull should take at least the timeout amount of time, but should stop early because the delay
+ // is bigger.
+ EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
+ EXPECT_GT(pullDelayNs, actualPullDurationNs);
+ ASSERT_EQ(0, dataHolder.size());
+
+ // Let the pull return and make sure that the dataHolder is not modified.
+ pullThread.join();
+ ASSERT_EQ(0, dataHolder.size());
+}
+
+// Register a puller and ensure that the timeout logic works.
+TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
+ shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
+ pullSuccess = true;
+ pullDelayNs = MillisToNano(5); // 5 ms.
+ pullTimeoutNs = 10000; // 10 microsseconds.
+ int64_t value = 4321;
+ int32_t uid = 123;
+ values.push_back(value);
+
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ pullerManager->RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs,
+ vector<int32_t>(), cb);
+ vector<shared_ptr<LogEvent>> dataHolder;
+ int64_t startTimeNs = getElapsedRealtimeNs();
+ // Returns false, since StatsPuller code will evaluate the timeout.
+ EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, startTimeNs, &dataHolder));
+ int64_t endTimeNs = getElapsedRealtimeNs();
+ int64_t actualPullDurationNs = endTimeNs - startTimeNs;
+
+ // Pull should take at least the timeout amount of time, but should stop early because the delay
+ // is bigger. Make sure that the time is closer to the timeout, than to the intended delay.
+ EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
+ EXPECT_GT(pullDelayNs / 5, actualPullDurationNs);
+ ASSERT_EQ(0, dataHolder.size());
+
+ // Let the pull return and make sure that the dataHolder is not modified.
+ pullThread.join();
+ ASSERT_EQ(0, dataHolder.size());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/external/StatsPullerManager_test.cpp b/cmds/statsd/tests/external/StatsPullerManager_test.cpp
new file mode 100644
index 0000000..c76e85e
--- /dev/null
+++ b/cmds/statsd/tests/external/StatsPullerManager_test.cpp
@@ -0,0 +1,150 @@
+// 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.
+
+#include "src/external/StatsPullerManager.h"
+
+#include <aidl/android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
+
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+
+int pullTagId1 = 10101;
+int pullTagId2 = 10102;
+int uid1 = 9999;
+int uid2 = 8888;
+ConfigKey configKey(50, 12345);
+ConfigKey badConfigKey(60, 54321);
+int unregisteredUid = 98765;
+int64_t coolDownNs = NS_PER_SEC;
+int64_t timeoutNs = NS_PER_SEC / 2;
+
+AStatsEvent* createSimpleEvent(int32_t atomId, int32_t value) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, atomId);
+ AStatsEvent_writeInt32(event, value);
+ AStatsEvent_build(event);
+ return event;
+}
+
+class FakePullAtomCallback : public BnPullAtomCallback {
+public:
+ FakePullAtomCallback(int32_t uid) : mUid(uid){};
+ Status onPullAtom(int atomTag,
+ const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
+ vector<StatsEventParcel> parcels;
+ AStatsEvent* event = createSimpleEvent(atomTag, mUid);
+ size_t size;
+ uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
+
+ StatsEventParcel p;
+ // vector.assign() creates a copy, but this is inevitable unless
+ // stats_event.h/c uses a vector as opposed to a buffer.
+ p.buffer.assign(buffer, buffer + size);
+ parcels.push_back(std::move(p));
+ AStatsEvent_release(event);
+ resultReceiver->pullFinished(atomTag, /*success*/ true, parcels);
+ return Status::ok();
+ }
+ int32_t mUid;
+};
+
+class FakePullUidProvider : public PullUidProvider {
+public:
+ vector<int32_t> getPullAtomUids(int atomId) override {
+ if (atomId == pullTagId1) {
+ return {uid2, uid1};
+ } else if (atomId == pullTagId2) {
+ return {uid2};
+ }
+ return {};
+ }
+};
+
+sp<StatsPullerManager> createPullerManagerAndRegister() {
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ shared_ptr<FakePullAtomCallback> cb1 = SharedRefBase::make<FakePullAtomCallback>(uid1);
+ pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1, true);
+ shared_ptr<FakePullAtomCallback> cb2 = SharedRefBase::make<FakePullAtomCallback>(uid2);
+ pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2, true);
+ pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1, true);
+ return pullerManager;
+}
+} // anonymous namespace
+
+TEST(StatsPullerManagerTest, TestPullInvalidUid) {
+ sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
+
+ vector<shared_ptr<LogEvent>> data;
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data, true));
+}
+
+TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) {
+ sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
+
+ vector<shared_ptr<LogEvent>> data;
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data, true));
+ ASSERT_EQ(data.size(), 1);
+ EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
+ ASSERT_EQ(data[0]->getValues().size(), 1);
+ EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid1);
+}
+
+TEST(StatsPullerManagerTest, TestPullInvalidConfigKey) {
+ sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
+ sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
+ pullerManager->RegisterPullUidProvider(configKey, uidProvider);
+
+ vector<shared_ptr<LogEvent>> data;
+ EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data, true));
+}
+
+TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
+ sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
+ sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
+ pullerManager->RegisterPullUidProvider(configKey, uidProvider);
+
+ vector<shared_ptr<LogEvent>> data;
+ EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data, true));
+ EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
+ ASSERT_EQ(data[0]->getValues().size(), 1);
+ EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2);
+}
+
+TEST(StatsPullerManagerTest, TestPullConfigKeyNoPullerWithUid) {
+ sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
+ sp<FakePullUidProvider> uidProvider = new FakePullUidProvider();
+ pullerManager->RegisterPullUidProvider(configKey, uidProvider);
+
+ vector<shared_ptr<LogEvent>> data;
+ EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data, true));
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
index 76e2097..55a9036 100644
--- a/cmds/statsd/tests/external/StatsPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -15,11 +15,14 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
+
#include <chrono>
#include <thread>
#include <vector>
+
#include "../metrics/metrics_test_helper.h"
#include "src/stats_log_util.h"
+#include "stats_event.h"
#include "tests/statsd_test_util.h"
#ifdef __ANDROID__
@@ -35,7 +38,7 @@
using std::this_thread::sleep_for;
using testing::Contains;
-// cooldown time 1sec.
+namespace {
int pullTagId = 10014;
bool pullSuccess;
@@ -44,7 +47,8 @@
class FakePuller : public StatsPuller {
public:
- FakePuller() : StatsPuller(pullTagId){};
+ FakePuller()
+ : StatsPuller(pullTagId, /*coolDownNs=*/MillisToNano(10), /*timeoutNs=*/MillisToNano(5)){};
private:
bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override {
@@ -56,11 +60,15 @@
FakePuller puller;
-shared_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) {
- shared_ptr<LogEvent> event = make_shared<LogEvent>(pullTagId, eventTimeNs);
- event->write(value);
- event->init();
- return event;
+std::unique_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, pullTagId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+ AStatsEvent_writeInt64(statsEvent, value);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
}
class StatsPullerTest : public ::testing::Test {
@@ -76,31 +84,33 @@
}
};
-TEST_F(StatsPullerTest, PullSucces) {
+} // Anonymous namespace.
+
+TEST_F(StatsPullerTest, PullSuccess) {
pullData.push_back(createSimpleEvent(1111L, 33));
pullSuccess = true;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(&dataHolder));
- EXPECT_EQ(1, dataHolder.size());
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- EXPECT_EQ(1, dataHolder[0]->size());
+ ASSERT_EQ(1, dataHolder[0]->size());
EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
- sleep_for(std::chrono::seconds(1));
+ sleep_for(std::chrono::milliseconds(11));
pullData.clear();
pullData.push_back(createSimpleEvent(2222L, 44));
pullSuccess = true;
- EXPECT_TRUE(puller.Pull(&dataHolder));
- EXPECT_EQ(1, dataHolder.size());
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(2222L, dataHolder[0]->GetElapsedTimestampNs());
- EXPECT_EQ(1, dataHolder[0]->size());
+ ASSERT_EQ(1, dataHolder[0]->size());
EXPECT_EQ(44, dataHolder[0]->getValues()[0].mValue.int_value);
}
@@ -110,47 +120,49 @@
pullSuccess = true;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(&dataHolder));
- EXPECT_EQ(1, dataHolder.size());
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- EXPECT_EQ(1, dataHolder[0]->size());
+ ASSERT_EQ(1, dataHolder[0]->size());
EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
- sleep_for(std::chrono::seconds(1));
+ sleep_for(std::chrono::milliseconds(11));
pullData.clear();
pullData.push_back(createSimpleEvent(2222L, 44));
pullSuccess = false;
dataHolder.clear();
- EXPECT_FALSE(puller.Pull(&dataHolder));
- EXPECT_EQ(0, dataHolder.size());
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+ // Fails due to hitting the cool down.
pullSuccess = true;
dataHolder.clear();
- EXPECT_FALSE(puller.Pull(&dataHolder));
- EXPECT_EQ(0, dataHolder.size());
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
}
// Test pull takes longer than timeout, 2nd pull happens shorter than cooldown
TEST_F(StatsPullerTest, PullTakeTooLongAndPullFast) {
pullData.push_back(createSimpleEvent(1111L, 33));
pullSuccess = true;
- // timeout is 0.5
- pullDelayNs = (long)(0.8 * NS_PER_SEC);
+ // timeout is 5ms
+ pullDelayNs = MillisToNano(6);
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(&dataHolder));
- EXPECT_EQ(0, dataHolder.size());
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
pullData.clear();
pullData.push_back(createSimpleEvent(2222L, 44));
+ pullDelayNs = 0;
pullSuccess = true;
dataHolder.clear();
- EXPECT_FALSE(puller.Pull(&dataHolder));
- EXPECT_EQ(0, dataHolder.size());
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
}
TEST_F(StatsPullerTest, PullFail) {
@@ -159,19 +171,19 @@
pullSuccess = false;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(&dataHolder));
- EXPECT_EQ(0, dataHolder.size());
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
}
TEST_F(StatsPullerTest, PullTakeTooLong) {
pullData.push_back(createSimpleEvent(1111L, 33));
pullSuccess = true;
- pullDelayNs = NS_PER_SEC;
+ pullDelayNs = MillisToNano(6);
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(&dataHolder));
- EXPECT_EQ(0, dataHolder.size());
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
}
TEST_F(StatsPullerTest, PullTooFast) {
@@ -180,11 +192,11 @@
pullSuccess = true;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_TRUE(puller.Pull(&dataHolder));
- EXPECT_EQ(1, dataHolder.size());
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- EXPECT_EQ(1, dataHolder[0]->size());
+ ASSERT_EQ(1, dataHolder[0]->size());
EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
pullData.clear();
@@ -193,11 +205,11 @@
pullSuccess = true;
dataHolder.clear();
- EXPECT_TRUE(puller.Pull(&dataHolder));
- EXPECT_EQ(1, dataHolder.size());
+ EXPECT_TRUE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(1, dataHolder.size());
EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
- EXPECT_EQ(1, dataHolder[0]->size());
+ ASSERT_EQ(1, dataHolder[0]->size());
EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
}
@@ -207,16 +219,93 @@
pullSuccess = false;
vector<std::shared_ptr<LogEvent>> dataHolder;
- EXPECT_FALSE(puller.Pull(&dataHolder));
- EXPECT_EQ(0, dataHolder.size());
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
pullData.clear();
pullData.push_back(createSimpleEvent(2222L, 44));
pullSuccess = true;
- EXPECT_FALSE(puller.Pull(&dataHolder));
- EXPECT_EQ(0, dataHolder.size());
+ EXPECT_FALSE(puller.Pull(getElapsedRealtimeNs(), &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullSameEventTime) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = true;
+ int64_t eventTimeNs = getElapsedRealtimeNs();
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+ ASSERT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+
+ // Sleep to ensure the cool down expires.
+ sleep_for(std::chrono::milliseconds(11));
+ pullSuccess = true;
+
+ dataHolder.clear();
+ EXPECT_TRUE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(1, dataHolder.size());
+ EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+ EXPECT_EQ(1111L, dataHolder[0]->GetElapsedTimestampNs());
+ ASSERT_EQ(1, dataHolder[0]->size());
+ EXPECT_EQ(33, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+// Test pull takes longer than timeout, 2nd pull happens at same event time
+TEST_F(StatsPullerTest, PullTakeTooLongAndPullSameEventTime) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+ pullSuccess = true;
+ int64_t eventTimeNs = getElapsedRealtimeNs();
+ // timeout is 5ms
+ pullDelayNs = MillisToNano(6);
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+
+ // Sleep to ensure the cool down expires. 6ms is taken by the delay, so only 5 is needed here.
+ sleep_for(std::chrono::milliseconds(5));
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+ pullDelayNs = 0;
+
+ pullSuccess = true;
+ dataHolder.clear();
+ EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsPullerTest, PullFailsAndPullSameEventTime) {
+ pullData.push_back(createSimpleEvent(1111L, 33));
+
+ pullSuccess = false;
+ int64_t eventTimeNs = getElapsedRealtimeNs();
+
+ vector<std::shared_ptr<LogEvent>> dataHolder;
+ EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
+
+ // Sleep to ensure the cool down expires.
+ sleep_for(std::chrono::milliseconds(11));
+
+ pullData.clear();
+ pullData.push_back(createSimpleEvent(2222L, 44));
+
+ pullSuccess = true;
+
+ EXPECT_FALSE(puller.Pull(eventTimeNs, &dataHolder));
+ ASSERT_EQ(0, dataHolder.size());
}
} // namespace statsd
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
index 6730828..a21dc87 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -13,12 +13,18 @@
// limitations under the License.
#include "external/puller_util.h"
+
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
+
#include <vector>
-#include "statslog.h"
+
#include "../metrics/metrics_test_helper.h"
+#include "FieldValue.h"
+#include "annotations.h"
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
#ifdef __ANDROID__
@@ -27,239 +33,371 @@
namespace statsd {
using namespace testing;
-using std::make_shared;
using std::shared_ptr;
using std::vector;
-using testing::Contains;
/*
* Test merge isolated and host uid
*/
+namespace {
+const int uidAtomTagId = 100;
+const vector<int> additiveFields = {3};
+const int nonUidAtomTagId = 200;
+const int timestamp = 1234;
+const int isolatedUid1 = 30;
+const int isolatedUid2 = 40;
+const int isolatedNonAdditiveData = 32;
+const int isolatedAdditiveData = 31;
+const int hostUid = 20;
+const int hostNonAdditiveData = 22;
+const int hostAdditiveData = 21;
+const int attributionAtomTagId = 300;
-int uidAtomTagId = android::util::CPU_CLUSTER_TIME;
-int nonUidAtomTagId = android::util::SYSTEM_UPTIME;
-int timestamp = 1234;
-int isolatedUid = 30;
-int isolatedAdditiveData = 31;
-int isolatedNonAdditiveData = 32;
-int hostUid = 20;
-int hostAdditiveData = 21;
-int hostNonAdditiveData = 22;
-
-void extractIntoVector(vector<shared_ptr<LogEvent>> events,
- vector<vector<int>>& ret) {
- ret.clear();
- status_t err;
- for (const auto& event : events) {
- vector<int> vec;
- vec.push_back(event->GetInt(1, &err));
- vec.push_back(event->GetInt(2, &err));
- vec.push_back(event->GetInt(3, &err));
- ret.push_back(vec);
- }
+sp<MockUidMap> makeMockUidMap() {
+ return makeMockUidMapForOneHost(hostUid, {isolatedUid1, isolatedUid2});
}
-TEST(puller_util, MergeNoDimension) {
- vector<shared_ptr<LogEvent>> inputData;
- shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- // 30->22->31
- event->write(isolatedUid);
- event->write(hostNonAdditiveData);
- event->write(isolatedAdditiveData);
- event->init();
- inputData.push_back(event);
+} // anonymous namespace
- // 20->22->21
- event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- event->write(hostUid);
- event->write(hostNonAdditiveData);
- event->write(hostAdditiveData);
- event->init();
- inputData.push_back(event);
+TEST(PullerUtilTest, MergeNoDimension) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 30->22->31
+ makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData,
+ isolatedAdditiveData),
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
- .WillRepeatedly(Return(hostUid));
- EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
- .WillRepeatedly(ReturnArg<0>());
- mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ // 20->22->21
+ makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData,
+ hostAdditiveData),
+ };
- vector<vector<int>> actual;
- extractIntoVector(inputData, actual);
- vector<int> expectedV1 = {20, 22, 52};
- EXPECT_EQ(1, (int)actual.size());
- EXPECT_THAT(actual, Contains(expectedV1));
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
+
+ ASSERT_EQ(1, (int)data.size());
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
}
-TEST(puller_util, MergeWithDimension) {
- vector<shared_ptr<LogEvent>> inputData;
- shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- // 30->32->31
- event->write(isolatedUid);
- event->write(isolatedNonAdditiveData);
- event->write(isolatedAdditiveData);
- event->init();
- inputData.push_back(event);
+TEST(PullerUtilTest, MergeWithDimension) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 30->32->31
+ makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData,
+ isolatedAdditiveData),
- // 20->32->21
- event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- event->write(hostUid);
- event->write(isolatedNonAdditiveData);
- event->write(hostAdditiveData);
- event->init();
- inputData.push_back(event);
+ // 20->32->21
+ makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData,
+ hostAdditiveData),
- // 20->22->21
- event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- event->write(hostUid);
- event->write(hostNonAdditiveData);
- event->write(hostAdditiveData);
- event->init();
- inputData.push_back(event);
+ // 20->22->21
+ makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData,
+ hostAdditiveData),
+ };
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
- .WillRepeatedly(Return(hostUid));
- EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
- .WillRepeatedly(ReturnArg<0>());
- mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
- vector<vector<int>> actual;
- extractIntoVector(inputData, actual);
- vector<int> expectedV1 = {20, 22, 21};
- vector<int> expectedV2 = {20, 32, 52};
- EXPECT_EQ(2, (int)actual.size());
- EXPECT_THAT(actual, Contains(expectedV1));
- EXPECT_THAT(actual, Contains(expectedV2));
+ ASSERT_EQ(2, (int)data.size());
+
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
+
+ actualFieldValues = &data[1]->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value);
}
-TEST(puller_util, NoMergeHostUidOnly) {
- vector<shared_ptr<LogEvent>> inputData;
- shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- // 20->32->31
- event->write(hostUid);
- event->write(isolatedNonAdditiveData);
- event->write(isolatedAdditiveData);
- event->init();
- inputData.push_back(event);
+TEST(PullerUtilTest, NoMergeHostUidOnly) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 20->32->31
+ makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData,
+ isolatedAdditiveData),
- // 20->22->21
- event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- event->write(hostUid);
- event->write(hostNonAdditiveData);
- event->write(hostAdditiveData);
- event->init();
- inputData.push_back(event);
+ // 20->22->21
+ makeUidLogEvent(uidAtomTagId, timestamp, hostUid, hostNonAdditiveData,
+ hostAdditiveData),
+ };
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
- .WillRepeatedly(Return(hostUid));
- EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
- .WillRepeatedly(ReturnArg<0>());
- mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
- // 20->32->31
- // 20->22->21
- vector<vector<int>> actual;
- extractIntoVector(inputData, actual);
- vector<int> expectedV1 = {20, 32, 31};
- vector<int> expectedV2 = {20, 22, 21};
- EXPECT_EQ(2, (int)actual.size());
- EXPECT_THAT(actual, Contains(expectedV1));
- EXPECT_THAT(actual, Contains(expectedV2));
+ ASSERT_EQ(2, (int)data.size());
+
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
+
+ actualFieldValues = &data[1]->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value);
}
-TEST(puller_util, IsolatedUidOnly) {
- vector<shared_ptr<LogEvent>> inputData;
- shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- // 30->32->31
- event->write(hostUid);
- event->write(isolatedNonAdditiveData);
- event->write(isolatedAdditiveData);
- event->init();
- inputData.push_back(event);
+TEST(PullerUtilTest, IsolatedUidOnly) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 30->32->31
+ makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData,
+ isolatedAdditiveData),
- // 30->22->21
- event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- event->write(hostUid);
- event->write(hostNonAdditiveData);
- event->write(hostAdditiveData);
- event->init();
- inputData.push_back(event);
+ // 30->22->21
+ makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, hostNonAdditiveData,
+ hostAdditiveData),
+ };
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
- .WillRepeatedly(Return(hostUid));
- EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
- .WillRepeatedly(ReturnArg<0>());
- mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
- // 20->32->31
- // 20->22->21
- vector<vector<int>> actual;
- extractIntoVector(inputData, actual);
- vector<int> expectedV1 = {20, 32, 31};
- vector<int> expectedV2 = {20, 22, 21};
- EXPECT_EQ(2, (int)actual.size());
- EXPECT_THAT(actual, Contains(expectedV1));
- EXPECT_THAT(actual, Contains(expectedV2));
+ ASSERT_EQ(2, (int)data.size());
+
+ // 20->32->31
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(hostAdditiveData, actualFieldValues->at(2).mValue.int_value);
+
+ // 20->22->21
+ actualFieldValues = &data[1]->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(2).mValue.int_value);
}
-TEST(puller_util, MultipleIsolatedUidToOneHostUid) {
- vector<shared_ptr<LogEvent>> inputData;
- shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- // 30->32->31
- event->write(isolatedUid);
- event->write(isolatedNonAdditiveData);
- event->write(isolatedAdditiveData);
- event->init();
- inputData.push_back(event);
+TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUid) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 30->32->31
+ makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid1, isolatedNonAdditiveData,
+ isolatedAdditiveData),
- // 31->32->21
- event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- event->write(isolatedUid + 1);
- event->write(isolatedNonAdditiveData);
- event->write(hostAdditiveData);
- event->init();
- inputData.push_back(event);
+ // 31->32->21
+ makeUidLogEvent(uidAtomTagId, timestamp, isolatedUid2, isolatedNonAdditiveData,
+ hostAdditiveData),
- // 20->32->21
- event = make_shared<LogEvent>(uidAtomTagId, timestamp);
- event->write(hostUid);
- event->write(isolatedNonAdditiveData);
- event->write(hostAdditiveData);
- event->init();
- inputData.push_back(event);
+ // 20->32->21
+ makeUidLogEvent(uidAtomTagId, timestamp, hostUid, isolatedNonAdditiveData,
+ hostAdditiveData),
+ };
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
- mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, uidAtomTagId, additiveFields);
- vector<vector<int>> actual;
- extractIntoVector(inputData, actual);
- vector<int> expectedV1 = {20, 32, 73};
- EXPECT_EQ(1, (int)actual.size());
- EXPECT_THAT(actual, Contains(expectedV1));
+ ASSERT_EQ(1, (int)data.size());
+
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(3, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(1).mValue.int_value);
+ EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData,
+ actualFieldValues->at(2).mValue.int_value);
}
-TEST(puller_util, NoNeedToMerge) {
- vector<shared_ptr<LogEvent>> inputData;
- shared_ptr<LogEvent> event =
- make_shared<LogEvent>(nonUidAtomTagId, timestamp);
- // 32
- event->write(isolatedNonAdditiveData);
- event->init();
- inputData.push_back(event);
+TEST(PullerUtilTest, NoNeedToMerge) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 32->31
+ CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, isolatedNonAdditiveData,
+ isolatedAdditiveData),
- event = make_shared<LogEvent>(nonUidAtomTagId, timestamp);
- // 22
- event->write(hostNonAdditiveData);
- event->init();
- inputData.push_back(event);
+ // 22->21
+ CreateTwoValueLogEvent(nonUidAtomTagId, timestamp, hostNonAdditiveData,
+ hostAdditiveData),
- sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
- mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
+ };
- EXPECT_EQ(2, (int)inputData.size());
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, nonUidAtomTagId, {} /*no additive fields*/);
+
+ ASSERT_EQ(2, (int)data.size());
+
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(2, actualFieldValues->size());
+ EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(1).mValue.int_value);
+
+ actualFieldValues = &data[1]->getValues();
+ ASSERT_EQ(2, actualFieldValues->size());
+ EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ(hostAdditiveData, actualFieldValues->at(1).mValue.int_value);
+}
+
+TEST(PullerUtilTest, MergeNoDimensionAttributionChain) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 30->tag1->400->tag2->22->31
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
+ {"tag1", "tag2"}, hostNonAdditiveData, isolatedAdditiveData),
+
+ // 20->tag1->400->tag2->22->21
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
+ {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
+ };
+
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
+
+ ASSERT_EQ(1, (int)data.size());
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+ EXPECT_EQ(isolatedAdditiveData + hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
+}
+
+TEST(PullerUtilTest, MergeWithDimensionAttributionChain) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 200->tag1->30->tag2->32->31
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, isolatedUid1},
+ {"tag1", "tag2"}, isolatedNonAdditiveData,
+ isolatedAdditiveData),
+
+ // 200->tag1->20->tag2->32->21
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid},
+ {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData),
+
+ // 200->tag1->20->tag2->22->21
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {200, hostUid},
+ {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
+ };
+
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
+
+ ASSERT_EQ(2, (int)data.size());
+
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+ EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
+
+ actualFieldValues = &data[1]->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(200, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(hostUid, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+ EXPECT_EQ(hostAdditiveData + isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value);
+}
+
+TEST(PullerUtilTest, NoMergeHostUidOnlyAttributionChain) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 20->tag1->400->tag2->32->31
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
+ {"tag1", "tag2"}, isolatedNonAdditiveData,
+ isolatedAdditiveData),
+
+ // 20->tag1->400->tag2->22->21
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
+ {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
+ };
+
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
+
+ ASSERT_EQ(2, (int)data.size());
+
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+ EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
+
+ actualFieldValues = &data[1]->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+ EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value);
+}
+
+TEST(PullerUtilTest, IsolatedUidOnlyAttributionChain) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 30->tag1->400->tag2->32->31
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
+ {"tag1", "tag2"}, isolatedNonAdditiveData,
+ isolatedAdditiveData),
+
+ // 30->tag1->400->tag2->22->21
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
+ {"tag1", "tag2"}, hostNonAdditiveData, hostAdditiveData),
+ };
+
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
+
+ ASSERT_EQ(2, (int)data.size());
+
+ // 20->tag1->400->tag2->32->31
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(hostNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+ EXPECT_EQ(hostAdditiveData, actualFieldValues->at(5).mValue.int_value);
+
+ // 20->tag1->400->tag2->22->21
+ actualFieldValues = &data[1]->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+ EXPECT_EQ(isolatedAdditiveData, actualFieldValues->at(5).mValue.int_value);
+}
+
+TEST(PullerUtilTest, MultipleIsolatedUidToOneHostUidAttributionChain) {
+ vector<shared_ptr<LogEvent>> data = {
+ // 30->tag1->400->tag2->32->31
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid1, 400},
+ {"tag1", "tag2"}, isolatedNonAdditiveData,
+ isolatedAdditiveData),
+
+ // 31->tag1->400->tag2->32->21
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {isolatedUid2, 400},
+ {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData),
+
+ // 20->tag1->400->tag2->32->21
+ makeAttributionLogEvent(attributionAtomTagId, timestamp, {hostUid, 400},
+ {"tag1", "tag2"}, isolatedNonAdditiveData, hostAdditiveData),
+ };
+
+ sp<MockUidMap> uidMap = makeMockUidMap();
+ mapAndMergeIsolatedUidsToHostUid(data, uidMap, attributionAtomTagId, additiveFields);
+
+ ASSERT_EQ(1, (int)data.size());
+
+ const vector<FieldValue>* actualFieldValues = &data[0]->getValues();
+ ASSERT_EQ(6, actualFieldValues->size());
+ EXPECT_EQ(hostUid, actualFieldValues->at(0).mValue.int_value);
+ EXPECT_EQ("tag1", actualFieldValues->at(1).mValue.str_value);
+ EXPECT_EQ(400, actualFieldValues->at(2).mValue.int_value);
+ EXPECT_EQ("tag2", actualFieldValues->at(3).mValue.str_value);
+ EXPECT_EQ(isolatedNonAdditiveData, actualFieldValues->at(4).mValue.int_value);
+ EXPECT_EQ(isolatedAdditiveData + hostAdditiveData + hostAdditiveData,
+ actualFieldValues->at(5).mValue.int_value);
}
} // namespace statsd
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 2a43d9b..428c46f 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -13,7 +13,7 @@
// limitations under the License.
#include "src/guardrail/StatsdStats.h"
-#include "statslog.h"
+#include "statslog_statsdtest.h"
#include "tests/statsd_test_util.h"
#include <gtest/gtest.h>
@@ -42,7 +42,7 @@
StatsdStatsReport report;
bool good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(1, report.config_stats_size());
+ ASSERT_EQ(1, report.config_stats_size());
const auto& configReport = report.config_stats(0);
EXPECT_EQ(0, configReport.uid());
EXPECT_EQ(12345, configReport.id());
@@ -69,7 +69,7 @@
StatsdStatsReport report;
bool good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(1, report.config_stats_size());
+ ASSERT_EQ(1, report.config_stats_size());
const auto& configReport = report.config_stats(0);
// The invalid config should be put into icebox with a deletion time.
EXPECT_TRUE(configReport.has_deletion_time_sec());
@@ -89,7 +89,7 @@
StatsdStatsReport report;
bool good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(1, report.config_stats_size());
+ ASSERT_EQ(1, report.config_stats_size());
const auto& configReport = report.config_stats(0);
EXPECT_FALSE(configReport.has_deletion_time_sec());
@@ -97,7 +97,7 @@
stats.dumpStats(&output, false);
good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(1, report.config_stats_size());
+ ASSERT_EQ(1, report.config_stats_size());
const auto& configReport2 = report.config_stats(0);
EXPECT_TRUE(configReport2.has_deletion_time_sec());
}
@@ -145,21 +145,21 @@
StatsdStatsReport report;
bool good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(1, report.config_stats_size());
+ ASSERT_EQ(1, report.config_stats_size());
const auto& configReport = report.config_stats(0);
- EXPECT_EQ(2, configReport.broadcast_sent_time_sec_size());
- EXPECT_EQ(1, configReport.data_drop_time_sec_size());
- EXPECT_EQ(1, configReport.data_drop_bytes_size());
+ ASSERT_EQ(2, configReport.broadcast_sent_time_sec_size());
+ ASSERT_EQ(1, configReport.data_drop_time_sec_size());
+ ASSERT_EQ(1, configReport.data_drop_bytes_size());
EXPECT_EQ(123, configReport.data_drop_bytes(0));
- EXPECT_EQ(3, configReport.dump_report_time_sec_size());
- EXPECT_EQ(3, configReport.dump_report_data_size_size());
- EXPECT_EQ(2, configReport.activation_time_sec_size());
- EXPECT_EQ(1, configReport.deactivation_time_sec_size());
- EXPECT_EQ(1, configReport.annotation_size());
+ ASSERT_EQ(3, configReport.dump_report_time_sec_size());
+ ASSERT_EQ(3, configReport.dump_report_data_size_size());
+ ASSERT_EQ(2, configReport.activation_time_sec_size());
+ ASSERT_EQ(1, configReport.deactivation_time_sec_size());
+ ASSERT_EQ(1, configReport.annotation_size());
EXPECT_EQ(123, configReport.annotation(0).field_int64());
EXPECT_EQ(456, configReport.annotation(0).field_int32());
- EXPECT_EQ(2, configReport.matcher_stats_size());
+ ASSERT_EQ(2, configReport.matcher_stats_size());
// matcher1 is the first in the list
if (configReport.matcher_stats(0).id() == StringToId("matcher1")) {
EXPECT_EQ(2, configReport.matcher_stats(0).matched_times());
@@ -174,18 +174,18 @@
EXPECT_EQ(StringToId("matcher1"), configReport.matcher_stats(1).id());
}
- EXPECT_EQ(2, configReport.alert_stats_size());
+ ASSERT_EQ(2, configReport.alert_stats_size());
bool alert1first = configReport.alert_stats(0).id() == StringToId("alert1");
EXPECT_EQ(StringToId("alert1"), configReport.alert_stats(alert1first ? 0 : 1).id());
EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).alerted_times());
EXPECT_EQ(StringToId("alert2"), configReport.alert_stats(alert1first ? 1 : 0).id());
EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).alerted_times());
- EXPECT_EQ(1, configReport.condition_stats_size());
+ ASSERT_EQ(1, configReport.condition_stats_size());
EXPECT_EQ(StringToId("condition1"), configReport.condition_stats(0).id());
EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts());
- EXPECT_EQ(1, configReport.metric_stats_size());
+ ASSERT_EQ(1, configReport.metric_stats_size());
EXPECT_EQ(StringToId("metric1"), configReport.metric_stats(0).id());
EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts());
@@ -199,21 +199,21 @@
stats.dumpStats(&output, false);
good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(1, report.config_stats_size());
+ ASSERT_EQ(1, report.config_stats_size());
const auto& configReport2 = report.config_stats(0);
- EXPECT_EQ(1, configReport2.matcher_stats_size());
+ ASSERT_EQ(1, configReport2.matcher_stats_size());
EXPECT_EQ(StringToId("matcher99"), configReport2.matcher_stats(0).id());
EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times());
- EXPECT_EQ(1, configReport2.condition_stats_size());
+ ASSERT_EQ(1, configReport2.condition_stats_size());
EXPECT_EQ(StringToId("condition99"), configReport2.condition_stats(0).id());
EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts());
- EXPECT_EQ(1, configReport2.metric_stats_size());
+ ASSERT_EQ(1, configReport2.metric_stats_size());
EXPECT_EQ(StringToId("metric99tion99"), configReport2.metric_stats(0).id());
EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts());
- EXPECT_EQ(1, configReport2.alert_stats_size());
+ ASSERT_EQ(1, configReport2.alert_stats_size());
EXPECT_EQ(StringToId("alert99"), configReport2.alert_stats(0).id());
EXPECT_EQ(1, configReport2.alert_stats(0).alerted_times());
}
@@ -222,11 +222,11 @@
StatsdStats stats;
time_t now = time(nullptr);
// old event, we get it from the stats buffer. should be ignored.
- stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, 1000);
+ stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, 1000);
- stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 1);
- stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2);
- stats.noteAtomLogged(android::util::APP_CRASH_OCCURRED, now + 3);
+ stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, now + 1);
+ stats.noteAtomLogged(util::SENSOR_STATE_CHANGED, now + 2);
+ stats.noteAtomLogged(util::APP_CRASH_OCCURRED, now + 3);
vector<uint8_t> output;
stats.dumpStats(&output, false);
@@ -234,15 +234,15 @@
bool good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(2, report.atom_stats_size());
+ ASSERT_EQ(2, report.atom_stats_size());
bool sensorAtomGood = false;
bool dropboxAtomGood = false;
for (const auto& atomStats : report.atom_stats()) {
- if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 3) {
+ if (atomStats.tag() == util::SENSOR_STATE_CHANGED && atomStats.count() == 3) {
sensorAtomGood = true;
}
- if (atomStats.tag() == android::util::APP_CRASH_OCCURRED && atomStats.count() == 1) {
+ if (atomStats.tag() == util::APP_CRASH_OCCURRED && atomStats.count() == 1) {
dropboxAtomGood = true;
}
}
@@ -254,8 +254,8 @@
TEST(StatsdStatsTest, TestNonPlatformAtomLog) {
StatsdStats stats;
time_t now = time(nullptr);
- int newAtom1 = android::util::kMaxPushedAtomId + 1;
- int newAtom2 = android::util::kMaxPushedAtomId + 2;
+ int newAtom1 = StatsdStats::kMaxPushedAtomId + 1;
+ int newAtom2 = StatsdStats::kMaxPushedAtomId + 2;
stats.noteAtomLogged(newAtom1, now + 1);
stats.noteAtomLogged(newAtom1, now + 2);
@@ -267,7 +267,7 @@
bool good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(2, report.atom_stats_size());
+ ASSERT_EQ(2, report.atom_stats_size());
bool newAtom1Good = false;
bool newAtom2Good = false;
@@ -287,22 +287,27 @@
TEST(StatsdStatsTest, TestPullAtomStats) {
StatsdStats stats;
- stats.updateMinPullIntervalSec(android::util::DISK_SPACE, 3333L);
- stats.updateMinPullIntervalSec(android::util::DISK_SPACE, 2222L);
- stats.updateMinPullIntervalSec(android::util::DISK_SPACE, 4444L);
+ stats.updateMinPullIntervalSec(util::DISK_SPACE, 3333L);
+ stats.updateMinPullIntervalSec(util::DISK_SPACE, 2222L);
+ stats.updateMinPullIntervalSec(util::DISK_SPACE, 4444L);
- stats.notePull(android::util::DISK_SPACE);
- stats.notePullTime(android::util::DISK_SPACE, 1111L);
- stats.notePullDelay(android::util::DISK_SPACE, 1111L);
- stats.notePull(android::util::DISK_SPACE);
- stats.notePullTime(android::util::DISK_SPACE, 3333L);
- stats.notePullDelay(android::util::DISK_SPACE, 3335L);
- stats.notePull(android::util::DISK_SPACE);
- stats.notePullFromCache(android::util::DISK_SPACE);
- stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, true);
- stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, false);
- stats.notePullerCallbackRegistrationChanged(android::util::DISK_SPACE, true);
-
+ stats.notePull(util::DISK_SPACE);
+ stats.notePullTime(util::DISK_SPACE, 1111L);
+ stats.notePullDelay(util::DISK_SPACE, 1111L);
+ stats.notePull(util::DISK_SPACE);
+ stats.notePullTime(util::DISK_SPACE, 3333L);
+ stats.notePullDelay(util::DISK_SPACE, 3335L);
+ stats.notePull(util::DISK_SPACE);
+ stats.notePullFromCache(util::DISK_SPACE);
+ stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true);
+ stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, false);
+ stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true);
+ stats.notePullBinderCallFailed(util::DISK_SPACE);
+ 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);
@@ -310,9 +315,9 @@
bool good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(1, report.pulled_atom_stats_size());
+ ASSERT_EQ(1, report.pulled_atom_stats_size());
- EXPECT_EQ(android::util::DISK_SPACE, report.pulled_atom_stats(0).atom_id());
+ EXPECT_EQ(util::DISK_SPACE, report.pulled_atom_stats(0).atom_id());
EXPECT_EQ(3, report.pulled_atom_stats(0).total_pull());
EXPECT_EQ(1, report.pulled_atom_stats(0).total_pull_from_cache());
EXPECT_EQ(2222L, report.pulled_atom_stats(0).min_pull_interval_sec());
@@ -322,6 +327,16 @@
EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos());
EXPECT_EQ(2L, report.pulled_atom_stats(0).registered_count());
EXPECT_EQ(1L, report.pulled_atom_stats(0).unregistered_count());
+ 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) {
@@ -342,7 +357,7 @@
bool good = report.ParseFromArray(&output[0], output.size());
EXPECT_TRUE(good);
- EXPECT_EQ(2, report.atom_metric_stats().size());
+ ASSERT_EQ(2, report.atom_metric_stats().size());
auto atomStats = report.atom_metric_stats(0);
EXPECT_EQ(1000L, atomStats.metric_id());
@@ -401,11 +416,11 @@
const auto& configStats = stats.mConfigStats[key];
size_t maxCount = StatsdStats::kMaxTimestampCount;
- EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size());
- EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size());
- EXPECT_EQ(maxCount, configStats->dump_report_stats.size());
- EXPECT_EQ(maxCount, configStats->activation_time_sec.size());
- EXPECT_EQ(maxCount, configStats->deactivation_time_sec.size());
+ ASSERT_EQ(maxCount, configStats->broadcast_sent_time_sec.size());
+ ASSERT_EQ(maxCount, configStats->data_drop_time_sec.size());
+ ASSERT_EQ(maxCount, configStats->dump_report_stats.size());
+ ASSERT_EQ(maxCount, configStats->activation_time_sec.size());
+ ASSERT_EQ(maxCount, configStats->deactivation_time_sec.size());
// the oldest timestamp is the second timestamp in history
EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
@@ -435,13 +450,13 @@
StatsdStatsReport report;
EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
const int maxCount = StatsdStats::kMaxSystemServerRestarts;
- EXPECT_EQ(maxCount, (int)report.system_restart_sec_size());
+ ASSERT_EQ(maxCount, (int)report.system_restart_sec_size());
stats.noteSystemServerRestart(StatsdStats::kMaxSystemServerRestarts + 1);
output.clear();
stats.dumpStats(&output, false);
EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
- EXPECT_EQ(maxCount, (int)report.system_restart_sec_size());
+ ASSERT_EQ(maxCount, (int)report.system_restart_sec_size());
EXPECT_EQ(StatsdStats::kMaxSystemServerRestarts + 1, report.system_restart_sec(maxCount - 1));
}
@@ -462,19 +477,19 @@
StatsdStatsReport report;
EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
- EXPECT_EQ(2, report.activation_guardrail_stats_size());
+ ASSERT_EQ(2, report.activation_guardrail_stats_size());
bool uid1Good = false;
bool uid2Good = false;
for (const auto& guardrailTimes : report.activation_guardrail_stats()) {
if (uid1 == guardrailTimes.uid()) {
uid1Good = true;
- EXPECT_EQ(2, guardrailTimes.guardrail_met_sec_size());
+ ASSERT_EQ(2, guardrailTimes.guardrail_met_sec_size());
EXPECT_EQ(10, guardrailTimes.guardrail_met_sec(0));
EXPECT_EQ(20, guardrailTimes.guardrail_met_sec(1));
} else if (uid2 == guardrailTimes.uid()) {
int maxCount = StatsdStats::kMaxTimestampCount;
uid2Good = true;
- EXPECT_EQ(maxCount, guardrailTimes.guardrail_met_sec_size());
+ ASSERT_EQ(maxCount, guardrailTimes.guardrail_met_sec_size());
for (int i = 0; i < maxCount; i++) {
EXPECT_EQ(100 - maxCount + i, guardrailTimes.guardrail_met_sec(i));
}
@@ -486,6 +501,41 @@
EXPECT_TRUE(uid2Good);
}
+TEST(StatsdStatsTest, TestAtomErrorStats) {
+ StatsdStats stats;
+
+ int pushAtomTag = 100;
+ int pullAtomTag = 1000;
+ int numErrors = 10;
+
+ for (int i = 0; i < numErrors; i++) {
+ // We must call noteAtomLogged as well because only those pushed atoms
+ // that have been logged will have stats printed about them in the
+ // proto.
+ stats.noteAtomLogged(pushAtomTag, /*timeSec=*/0);
+ stats.noteAtomError(pushAtomTag, /*pull=*/false);
+
+ stats.noteAtomError(pullAtomTag, /*pull=*/true);
+ }
+
+ vector<uint8_t> output;
+ stats.dumpStats(&output, false);
+ StatsdStatsReport report;
+ EXPECT_TRUE(report.ParseFromArray(&output[0], output.size()));
+
+ // Check error count = numErrors for push atom
+ ASSERT_EQ(1, report.atom_stats_size());
+ const auto& pushedAtomStats = report.atom_stats(0);
+ EXPECT_EQ(pushAtomTag, pushedAtomStats.tag());
+ EXPECT_EQ(numErrors, pushedAtomStats.error_count());
+
+ // Check error count = numErrors for pull atom
+ ASSERT_EQ(1, report.pulled_atom_stats_size());
+ const auto& pulledAtomStats = report.pulled_atom_stats(0);
+ EXPECT_EQ(pullAtomTag, pulledAtomStats.atom_id());
+ EXPECT_EQ(numErrors, pulledAtomStats.atom_error_count());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp
index d6cd876..3a65456 100644
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp
@@ -44,23 +44,23 @@
sp<const AATest> aa4 = new AATest{4, emptyMetricId, emptyDimensionId};
sp<const AATest> aa8 = new AATest{8, emptyMetricId, emptyDimensionId};
- EXPECT_EQ(0u, ipq.size());
+ ASSERT_EQ(0u, ipq.size());
EXPECT_TRUE(ipq.empty());
ipq.push(aa4);
- EXPECT_EQ(1u, ipq.size());
+ ASSERT_EQ(1u, ipq.size());
EXPECT_FALSE(ipq.empty());
ipq.push(aa8);
- EXPECT_EQ(2u, ipq.size());
+ ASSERT_EQ(2u, ipq.size());
EXPECT_FALSE(ipq.empty());
ipq.remove(aa4);
- EXPECT_EQ(1u, ipq.size());
+ ASSERT_EQ(1u, ipq.size());
EXPECT_FALSE(ipq.empty());
ipq.remove(aa8);
- EXPECT_EQ(0u, ipq.size());
+ ASSERT_EQ(0u, ipq.size());
EXPECT_TRUE(ipq.empty());
}
@@ -126,17 +126,17 @@
sp<const AATest> aa4_b = new AATest{4, emptyMetricId, emptyDimensionId};
ipq.push(aa4_a);
- EXPECT_EQ(1u, ipq.size());
+ ASSERT_EQ(1u, ipq.size());
EXPECT_TRUE(ipq.contains(aa4_a));
EXPECT_FALSE(ipq.contains(aa4_b));
ipq.push(aa4_a);
- EXPECT_EQ(1u, ipq.size());
+ ASSERT_EQ(1u, ipq.size());
EXPECT_TRUE(ipq.contains(aa4_a));
EXPECT_FALSE(ipq.contains(aa4_b));
ipq.push(aa4_b);
- EXPECT_EQ(2u, ipq.size());
+ ASSERT_EQ(2u, ipq.size());
EXPECT_TRUE(ipq.contains(aa4_a));
EXPECT_TRUE(ipq.contains(aa4_b));
}
@@ -150,7 +150,7 @@
ipq.push(aa4);
ipq.remove(aa5);
- EXPECT_EQ(1u, ipq.size());
+ ASSERT_EQ(1u, ipq.size());
EXPECT_TRUE(ipq.contains(aa4));
EXPECT_FALSE(ipq.contains(aa5));
}
@@ -164,17 +164,17 @@
ipq.push(aa4_a);
ipq.push(aa4_b);
- EXPECT_EQ(2u, ipq.size());
+ ASSERT_EQ(2u, ipq.size());
EXPECT_TRUE(ipq.contains(aa4_a));
EXPECT_TRUE(ipq.contains(aa4_b));
ipq.remove(aa4_b);
- EXPECT_EQ(1u, ipq.size());
+ ASSERT_EQ(1u, ipq.size());
EXPECT_TRUE(ipq.contains(aa4_a));
EXPECT_FALSE(ipq.contains(aa4_b));
ipq.remove(aa4_a);
- EXPECT_EQ(0u, ipq.size());
+ ASSERT_EQ(0u, ipq.size());
EXPECT_FALSE(ipq.contains(aa4_a));
EXPECT_FALSE(ipq.contains(aa4_b));
}
@@ -205,22 +205,22 @@
ipq.push(c);
ipq.push(b);
ipq.push(a);
- EXPECT_EQ(3u, ipq.size());
+ ASSERT_EQ(3u, ipq.size());
ipq.pop();
- EXPECT_EQ(2u, ipq.size());
+ ASSERT_EQ(2u, ipq.size());
EXPECT_FALSE(ipq.contains(a));
EXPECT_TRUE(ipq.contains(b));
EXPECT_TRUE(ipq.contains(c));
ipq.pop();
- EXPECT_EQ(1u, ipq.size());
+ ASSERT_EQ(1u, ipq.size());
EXPECT_FALSE(ipq.contains(a));
EXPECT_FALSE(ipq.contains(b));
EXPECT_TRUE(ipq.contains(c));
ipq.pop();
- EXPECT_EQ(0u, ipq.size());
+ ASSERT_EQ(0u, ipq.size());
EXPECT_FALSE(ipq.contains(a));
EXPECT_FALSE(ipq.contains(b));
EXPECT_FALSE(ipq.contains(c));
diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
index f27d129..a15f95b 100644
--- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
+++ b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp
@@ -16,9 +16,12 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <stdio.h>
+
#include <thread>
-#include <stdio.h>
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
namespace android {
namespace os {
@@ -29,6 +32,20 @@
using std::unique_ptr;
+namespace {
+
+std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, 10);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+} // anonymous namespace
+
#ifdef __ANDROID__
TEST(LogEventQueue_test, TestGoodConsumer) {
LogEventQueue queue(50);
@@ -36,8 +53,7 @@
std::thread writer([&queue, timeBaseNs] {
for (int i = 0; i < 100; i++) {
int64_t oldestEventNs;
- bool success = queue.push(std::make_unique<LogEvent>(10, timeBaseNs + i * 1000),
- &oldestEventNs);
+ bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
EXPECT_TRUE(success);
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
@@ -63,8 +79,7 @@
int failure_count = 0;
int64_t oldestEventNs;
for (int i = 0; i < 100; i++) {
- bool success = queue.push(std::make_unique<LogEvent>(10, timeBaseNs + i * 1000),
- &oldestEventNs);
+ bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
if (!success) failure_count++;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
diff --git a/cmds/statsd/tests/metadata_util_test.cpp b/cmds/statsd/tests/metadata_util_test.cpp
new file mode 100644
index 0000000..7707890
--- /dev/null
+++ b/cmds/statsd/tests/metadata_util_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+#include <gtest/gtest.h>
+
+#include "metadata_util.h"
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(MetadataUtilTest, TestWriteAndReadMetricDimensionKey) {
+ HashableDimensionKey dim;
+ HashableDimensionKey dim2;
+ 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));
+
+ dim2.addValue(FieldValue(field1, value1));
+ dim2.addValue(FieldValue(field2, value2));
+
+ MetricDimensionKey dimKey(dim, dim2);
+
+ metadata::MetricDimensionKey metadataDimKey;
+ writeMetricDimensionKeyToMetadataDimensionKey(dimKey, &metadataDimKey);
+
+ MetricDimensionKey loadedDimKey = loadMetricDimensionKeyFromProto(metadataDimKey);
+
+ ASSERT_EQ(loadedDimKey, dimKey);
+ ASSERT_EQ(std::hash<MetricDimensionKey>{}(loadedDimKey),
+ std::hash<MetricDimensionKey>{}(dimKey));
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 67c704e..bb8e7bf 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -13,16 +13,19 @@
// limitations under the License.
#include "src/metrics/CountMetricProducer.h"
-#include "src/stats_log_util.h"
-#include "metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <math.h>
#include <stdio.h>
+
#include <vector>
+#include "metrics_test_helper.h"
+#include "src/stats_log_util.h"
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
+
using namespace testing;
using android::sp;
using std::set;
@@ -35,16 +38,44 @@
namespace os {
namespace statsd {
+
+namespace {
const ConfigKey kConfigKey(0, 12345);
+void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ AStatsEvent_writeString(statsEvent, uid.c_str());
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+} // namespace
+
+// Setup for parameterized tests.
+class CountMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
+
+INSTANTIATE_TEST_SUITE_P(CountMetricProducerTest_PartialBucket,
+ CountMetricProducerTest_PartialBucket,
+ testing::Values(APP_UPGRADE, BOOT_COMPLETE));
+
TEST(CountMetricProducerTest, TestFirstBucket) {
CountMetric metric;
metric.set_id(1);
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());
@@ -61,45 +92,46 @@
metric.set_id(1);
metric.set_bucket(ONE_MINUTE);
- LogEvent event1(tagId, bucketStartTimeNs + 1);
- event1.init();
- LogEvent event2(tagId, bucketStartTimeNs + 2);
- event2.init();
-
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);
+ makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
+
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// Flushes at event #2.
countProducer.flushIfNeededLocked(bucketStartTimeNs + 2);
- EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
// Flushes.
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(1UL, buckets.size());
+ ASSERT_EQ(1UL, buckets.size());
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(2LL, buckets[0].mCount);
// 1 matched event happens in bucket 2.
- LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2);
- event3.init();
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 2, tagId);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+
countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
- EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
@@ -107,11 +139,11 @@
// nothing happens in bucket 3. we should not record anything for bucket 3.
countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
- EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(2UL, buckets3.size());
+ ASSERT_EQ(2UL, buckets3.size());
}
TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
@@ -123,37 +155,38 @@
metric.set_bucket(ONE_MINUTE);
metric.set_condition(StringToId("SCREEN_ON"));
- LogEvent event1(1, bucketStartTimeNs + 1);
- event1.init();
-
- LogEvent event2(1, bucketStartTimeNs + 10);
- event2.init();
-
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);
+
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1);
countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
- EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+
+ ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
+
// Upon this match event, the matched event1 is flushed.
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1);
countProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
- EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- {
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(1UL, buckets.size());
- const auto& bucketInfo = buckets[0];
- EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
- EXPECT_EQ(1LL, bucketInfo.mCount);
- }
+
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
+ ASSERT_EQ(1UL, buckets.size());
+ const auto& bucketInfo = buckets[0];
+ EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
+ EXPECT_EQ(1LL, bucketInfo.mCount);
}
TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
@@ -172,50 +205,52 @@
buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
- LogEvent event1(tagId, bucketStartTimeNs + 1);
- event1.write("111"); // uid
- event1.init();
- ConditionKey key1;
- key1[StringToId("APP_IN_BACKGROUND_PER_UID")] =
- {getMockedDimensionKey(conditionTagId, 2, "111")};
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
- LogEvent event2(tagId, bucketStartTimeNs + 10);
- event2.write("222"); // uid
- event2.init();
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222");
+
+ ConditionKey key1;
+ key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
+ getMockedDimensionKey(conditionTagId, 2, "111")};
+
ConditionKey key2;
- key2[StringToId("APP_IN_BACKGROUND_PER_UID")] =
- {getMockedDimensionKey(conditionTagId, 2, "222")};
+ key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
+ 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));
+ EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
- CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
- bucketStartTimeNs, bucketStartTimeNs);
+ EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
+
+ CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/,
+ {ConditionState::kUnknown}, wizard, bucketStartTimeNs,
+ bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
- EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(1UL, buckets.size());
+ ASSERT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
EXPECT_EQ(1LL, bucketInfo.mCount);
}
-TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
+TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) {
sp<AlarmMonitor> alarmMonitor;
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+ int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
int tagId = 1;
int conditionTagId = 2;
@@ -226,57 +261,66 @@
Alert alert;
alert.set_num_buckets(3);
alert.set_trigger_if_sum_gt(2);
- LogEvent event1(tagId, bucketStartTimeNs + 1);
- event1.write("111"); // uid
- event1.init();
+
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);
EXPECT_TRUE(anomalyTracker != nullptr);
- // Bucket is flushed yet.
+ // Bucket is not flushed yet.
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
- // App upgrade forces bucket flush.
+ // App upgrade or boot complete forces bucket flush.
// Check that there's a past bucket and the bucket end is not adjusted.
- countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
- EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ((long long)bucketStartTimeNs,
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ countProducer.notifyAppUpgrade(eventTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ countProducer.onStatsdInitCompleted(eventTimeNs);
+ break;
+ }
+ ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
- EXPECT_EQ((long long)eventUpgradeTimeNs,
+ EXPECT_EQ(eventTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
- EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(0, countProducer.getCurrentBucketNum());
+ EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
// Anomaly tracker only contains full buckets.
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
// Next event occurs in same bucket as partial bucket created.
- LogEvent event2(tagId, bucketStartTimeNs + 59 * NS_PER_SEC + 10);
- event2.write("222"); // uid
- event2.init();
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
- EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
+ ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(0, countProducer.getCurrentBucketNum());
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
// Third event in following bucket.
- LogEvent event3(tagId, bucketStartTimeNs + 62 * NS_PER_SEC + 10);
- event3.write("333"); // uid
- event3.init();
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1, countProducer.getCurrentBucketNum());
EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
}
-TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
+TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) {
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
+ int64_t eventTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
int tagId = 1;
int conditionTagId = 2;
@@ -284,41 +328,48 @@
CountMetric metric;
metric.set_id(1);
metric.set_bucket(ONE_MINUTE);
- LogEvent event1(tagId, bucketStartTimeNs + 1);
- event1.write("111"); // uid
- event1.init();
+
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.
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
- EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
+ ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
- // App upgrade forces bucket flush.
- // Check that there's a past bucket and the bucket end is not adjusted.
- countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
- EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ((int64_t)bucketStartTimeNs,
+ // App upgrade or boot complete forces bucket flush.
+ // Check that there's a past bucket and the bucket end is not adjusted since the upgrade
+ // occurred after the bucket end time.
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ countProducer.notifyAppUpgrade(eventTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ countProducer.onStatsdInitCompleted(eventTimeNs);
+ break;
+ }
+ ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
- EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
// Next event occurs in same bucket as partial bucket created.
- LogEvent event2(tagId, bucketStartTimeNs + 70 * NS_PER_SEC + 10);
- event2.write("222"); // uid
- event2.init();
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
- EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
// Third event in following bucket.
- LogEvent event3(tagId, bucketStartTimeNs + 121 * NS_PER_SEC + 10);
- event3.write("333"); // uid
- event3.init();
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ((int64_t)eventUpgradeTimeNs,
+ ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ((int64_t)eventTimeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);
@@ -344,38 +395,39 @@
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);
int tagId = 1;
- LogEvent event1(tagId, bucketStartTimeNs + 1);
- event1.init();
- LogEvent event2(tagId, bucketStartTimeNs + 2);
- event2.init();
- LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1);
- event3.init();
- LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1);
- event4.init();
- LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2);
- event5.init();
- LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3);
- event6.init();
- LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
- event7.init();
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId);
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId);
+ LogEvent event5(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId);
+ LogEvent event6(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId);
+ LogEvent event7(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId);
// Two events in bucket #0.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
- EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// One event in bucket #2. No alarm as bucket #0 is trashed out.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
@@ -383,17 +435,37 @@
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
- EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
// Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+ std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
- EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
+ ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+ std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+}
+
+TEST(CountMetricProducerTest, TestOneWeekTimeUnit) {
+ CountMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_WEEK);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ int64_t oneDayNs = 24 * 60 * 60 * 1e9;
+ int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
+
+ CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard,
+ oneDayNs, fiveWeeksNs);
+
+ int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
+
+ EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(4, countProducer.mCurrentBucketNum);
+ EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs());
}
} // namespace statsd
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index b294cad..05cfa37b 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -13,17 +13,21 @@
// limitations under the License.
#include "src/metrics/DurationMetricProducer.h"
-#include "src/stats_log_util.h"
-#include "metrics_test_helper.h"
-#include "src/condition/ConditionWizard.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
+
#include <set>
#include <unordered_map>
#include <vector>
+#include "metrics_test_helper.h"
+#include "src/condition/ConditionWizard.h"
+#include "src/stats_log_util.h"
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
+
using namespace android::os::statsd;
using namespace testing;
using android::sp;
@@ -37,7 +41,26 @@
namespace os {
namespace statsd {
+
+namespace {
+
const ConfigKey kConfigKey(0, 12345);
+void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+} // namespace
+
+// Setup for parameterized tests.
+class DurationMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
+
+INSTANTIATE_TEST_SUITE_P(DurationMetricProducerTest_PartialBucket,
+ DurationMetricProducerTest_PartialBucket,
+ testing::Values(APP_UPGRADE, BOOT_COMPLETE));
TEST(DurationMetricTrackerTest, TestFirstBucket) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -47,9 +70,11 @@
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);
@@ -67,24 +92,26 @@
metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
int tagId = 1;
- LogEvent event1(tagId, bucketStartTimeNs + 1);
- event1.init();
- LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2);
- event2.init();
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId);
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+ DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {},
+ 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard,
+ dimensions, bucketStartTimeNs, bucketStartTimeNs);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
- EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
durationProducer.mPastBuckets.end());
const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(2UL, buckets.size());
+ ASSERT_EQ(2UL, buckets.size());
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration);
@@ -104,19 +131,21 @@
metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
int tagId = 1;
- LogEvent event1(tagId, bucketStartTimeNs + 1);
- event1.init();
- LogEvent event2(tagId, bucketStartTimeNs + 2);
- event2.init();
- LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
- event3.init();
- LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
- event4.init();
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ 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);
+ 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);
@@ -125,17 +154,17 @@
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
- EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
durationProducer.mPastBuckets.end());
const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(1UL, buckets2.size());
+ ASSERT_EQ(1UL, buckets2.size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
EXPECT_EQ(1LL, buckets2[0].mDuration);
@@ -152,19 +181,21 @@
metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
int tagId = 1;
- LogEvent event1(tagId, bucketStartTimeNs + 1);
- event1.init();
- LogEvent event2(tagId, bucketStartTimeNs + 2);
- event2.init();
- LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
- event3.init();
- LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
- event4.init();
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ 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);
+ 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());
@@ -172,21 +203,21 @@
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
- EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(1UL, buckets2.size());
+ ASSERT_EQ(1UL, buckets2.size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
EXPECT_EQ(1LL, buckets2[0].mDuration);
}
-TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
+TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) {
/**
* The duration starts from the first bucket, through the two partial buckets (10-70sec),
* another bucket, and ends at the beginning of the next full bucket.
@@ -198,10 +229,6 @@
*/
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
- int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
- int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
-
int tagId = 1;
DurationMetric metric;
@@ -210,40 +237,53 @@
metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
- LogEvent start_event(tagId, startTimeNs);
- start_event.init();
- durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
+ 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard,
+ dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+ int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, startTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
- durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
- EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+ ASSERT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
- EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs);
- EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration);
- EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketEndNs);
+ EXPECT_EQ(partialBucketSplitTimeNs - startTimeNs, buckets[0].mDuration);
+ EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
// We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
- LogEvent end_event(tagId, endTimeNs);
- end_event.init();
- durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
+ int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, endTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(3UL, buckets.size());
- EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs);
+ ASSERT_EQ(3UL, buckets.size());
+ EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs);
- EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration);
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - partialBucketSplitTimeNs, buckets[1].mDuration);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
EXPECT_EQ(bucketSizeNs, buckets[2].mDuration);
}
-TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
+TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollowingBucket) {
/**
* Expected buckets (start at 11s, upgrade at 75s, end at 135s):
* - [10,70]: 59 secs
@@ -252,10 +292,6 @@
*/
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
- int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
- int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
-
int tagId = 1;
DurationMetric metric;
@@ -264,47 +300,57 @@
metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
- LogEvent start_event(tagId, startTimeNs);
- start_event.init();
- durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
+ 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard,
+ dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+ int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, startTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
- durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
- EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+ ASSERT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
- EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs);
- EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
- EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketEndNs);
+ EXPECT_EQ(partialBucketSplitTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
+ EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
// We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
- LogEvent end_event(tagId, endTimeNs);
- end_event.init();
- durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
+ int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, endTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(3UL, buckets.size());
- EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs);
+ ASSERT_EQ(3UL, buckets.size());
+ EXPECT_EQ(partialBucketSplitTimeNs, buckets[2].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
- EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration);
+ EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - partialBucketSplitTimeNs,
+ buckets[2].mDuration);
}
-TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
+TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) {
sp<AlarmMonitor> alarmMonitor;
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
- int64_t startTimeNs = bucketStartTimeNs + 1;
- int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
-
int tagId = 1;
// Setup metric with alert.
@@ -318,117 +364,145 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+ DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
+ 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard,
+ dimensions, bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
- LogEvent start_event(tagId, startTimeNs);
- start_event.init();
- durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
- durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ int64_t startTimeNs = bucketStartTimeNs + 1;
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, startTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+
+ int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+
// We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
- LogEvent end_event(tagId, endTimeNs);
- end_event.init();
- durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
+ int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, endTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
}
-TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
+TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) {
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
- int64_t startTimeNs = bucketStartTimeNs + 1;
- int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
-
int tagId = 1;
DurationMetric metric;
metric.set_id(1);
metric.set_bucket(ONE_MINUTE);
metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
- LogEvent event1(tagId, startTimeNs);
- event1.write("111"); // uid
- event1.init();
+
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
- LogEvent start_event(tagId, startTimeNs);
- start_event.init();
- durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
+ 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard,
+ dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+ int64_t startTimeNs = bucketStartTimeNs + 1;
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, startTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
- durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+ int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
// We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
- LogEvent end_event(tagId, endTimeNs);
- end_event.init();
- durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, endTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(1UL, buckets.size());
+ ASSERT_EQ(1UL, buckets.size());
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
}
-TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
+TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket) {
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
- int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
- int64_t startTimeNs = bucketStartTimeNs + 1;
- int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
-
int tagId = 1;
DurationMetric metric;
metric.set_id(1);
metric.set_bucket(ONE_MINUTE);
metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
- LogEvent event1(tagId, startTimeNs);
- event1.write("111"); // uid
- event1.init();
+
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
FieldMatcher dimensions;
- DurationMetricProducer durationProducer(
- kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
- LogEvent start_event(tagId, startTimeNs);
- start_event.init();
- durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
+ DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
+ 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard,
+ dimensions, bucketStartTimeNs, bucketStartTimeNs);
+
+ int64_t startTimeNs = bucketStartTimeNs + 1;
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, startTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
- durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+ int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
// Stop occurs in the same partial bucket as created for the app upgrade.
- LogEvent end_event(tagId, endTimeNs);
- end_event.init();
- durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
- EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
+ int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, endTimeNs, tagId);
+ durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
+ ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
std::vector<DurationBucket> buckets =
durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
- EXPECT_EQ(1UL, buckets.size());
- EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs);
+ ASSERT_EQ(1UL, buckets.size());
+ EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs);
EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
}
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index d2fd95c..dfbb9da 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -13,14 +13,17 @@
// limitations under the License.
#include "src/metrics/EventMetricProducer.h"
-#include "metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
+
#include <vector>
+#include "metrics_test_helper.h"
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
+
using namespace testing;
using android::sp;
using std::set;
@@ -35,6 +38,17 @@
const ConfigKey kConfigKey(0, 12345);
+namespace {
+void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ AStatsEvent_writeString(statsEvent, str.c_str());
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+} // anonymous namespace
+
TEST(EventMetricProducerTest, TestNoCondition) {
int64_t bucketStartTimeNs = 10000000000;
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -43,19 +57,31 @@
EventMetric metric;
metric.set_id(1);
- LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1);
- LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 2);
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);
- // TODO(b/110561136): get the report and check the content after the ProtoOutputStream change
- // is done eventProducer.onDumpReport();
+ // Check dump report content.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
+ true /*erase data*/, FAST, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_event_metrics());
+ ASSERT_EQ(2, report.event_metrics().data_size());
+ EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos());
+ EXPECT_EQ(bucketStartTimeNs + 2, report.event_metrics().data(1).elapsed_timestamp_nanos());
}
TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) {
@@ -67,12 +93,16 @@
metric.set_id(1);
metric.set_condition(StringToId("SCREEN_ON"));
- LogEvent event1(1, bucketStartTimeNs + 1);
- LogEvent event2(1, bucketStartTimeNs + 10);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10);
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);
@@ -81,8 +111,16 @@
eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
- // TODO: get the report and check the content after the ProtoOutputStream change is done.
- // eventProducer.onDumpReport();
+ // Check dump report content.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
+ true /*erase data*/, FAST, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_event_metrics());
+ ASSERT_EQ(1, report.event_metrics().data_size());
+ EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos());
}
TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
@@ -100,30 +138,40 @@
buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
- LogEvent event1(tagId, bucketStartTimeNs + 1);
- EXPECT_TRUE(event1.write("111"));
- event1.init();
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1, "111");
ConditionKey key1;
- key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "111")};
+ key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
+ getMockedDimensionKey(conditionTagId, 2, "111")};
- LogEvent event2(tagId, bucketStartTimeNs + 10);
- EXPECT_TRUE(event2.write("222"));
- event2.init();
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ makeLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10, "222");
ConditionKey key2;
- key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
+ key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
+ getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
+ // Condition is false for first event.
+ EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
+ // Condition is true for second event.
+ EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
- 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);
- // TODO: get the report and check the content after the ProtoOutputStream change is done.
- // eventProducer.onDumpReport();
+ // Check dump report content.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
+ true /*erase data*/, FAST, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_event_metrics());
+ ASSERT_EQ(1, report.event_metrics().data_size());
+ EXPECT_EQ(bucketStartTimeNs + 10, report.event_metrics().data(0).elapsed_timestamp_nanos());
}
} // namespace statsd
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index b9553a8..5997bed 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -12,19 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/matchers/SimpleLogMatchingTracker.h"
#include "src/metrics/GaugeMetricProducer.h"
-#include "src/stats_log_util.h"
-#include "logd/LogEvent.h"
-#include "metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <math.h>
#include <stdio.h>
+
#include <vector>
+#include "logd/LogEvent.h"
+#include "metrics_test_helper.h"
+#include "src/matchers/SimpleLogMatchingTracker.h"
+#include "src/metrics/MetricProducer.h"
+#include "src/stats_log_util.h"
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
+
using namespace testing;
using android::sp;
using std::set;
@@ -38,6 +42,8 @@
namespace os {
namespace statsd {
+namespace {
+
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
const int64_t metricId = 123;
@@ -48,7 +54,30 @@
const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
-const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
+
+shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1,
+ int32_t value2) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeString(statsEvent, str1.c_str());
+ AStatsEvent_writeInt32(statsEvent, value2);
+
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+} // anonymous namespace
+
+// Setup for parameterized tests.
+class GaugeMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
+
+INSTANTIATE_TEST_SUITE_P(GaugeMetricProducerTest_PartialBucket,
+ GaugeMetricProducerTest_PartialBucket,
+ testing::Values(APP_UPGRADE, BOOT_COMPLETE));
/*
* Tests that the first bucket works correctly
@@ -75,13 +104,11 @@
// 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);
EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs());
@@ -103,60 +130,49 @@
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
atomMatcher.set_atom_id(tagId);
- sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
- new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event->write(3);
- event->write("some value");
- event->write(11);
- event->init();
- data->push_back(event);
+ data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
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;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(10);
- event->write("some value");
- event->write(11);
- event->init();
- allData.push_back(event);
+ allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11));
gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
EXPECT_EQ(10, it->mValue.int_value);
it++;
EXPECT_EQ(11, it->mValue.int_value);
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms
- .front().mFields->begin()->mValue.int_value);
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()
+ ->second.back()
+ .mGaugeAtoms.front()
+ .mFields->begin()
+ ->mValue.int_value);
allData.clear();
- std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
- event2->write(24);
- event2->write("some value");
- event2->write(25);
- event2->init();
- allData.push_back(event2);
+ allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25));
gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
EXPECT_EQ(24, it->mValue.int_value);
@@ -164,8 +180,8 @@
EXPECT_EQ(INT, it->mValue.getType());
EXPECT_EQ(25, it->mValue.int_value);
// One dimension.
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
EXPECT_EQ(10L, it->mValue.int_value);
@@ -174,10 +190,10 @@
EXPECT_EQ(11L, it->mValue.int_value);
gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
- EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
// One dimension.
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ ASSERT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
EXPECT_EQ(24L, it->mValue.int_value);
@@ -186,7 +202,7 @@
EXPECT_EQ(25L, it->mValue.int_value);
}
-TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
+TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) {
sp<AlarmMonitor> alarmMonitor;
GaugeMetric metric;
metric.set_id(metricId);
@@ -204,11 +220,12 @@
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
atomMatcher.set_atom_id(tagId);
- sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
- new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<EventMatcherWizard> eventMatcherWizard =
+ 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();
@@ -216,58 +233,64 @@
sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->init();
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
- gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs,
+ gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+ EXPECT_EQ(partialBucketSplitTimeNs,
+ gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
- EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
// Partial buckets are not sent to anomaly tracker.
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
// Create an event in the same partial bucket.
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
- event2->write(1);
- event2->write(10);
- event2->init();
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs,
+ gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+ EXPECT_EQ(partialBucketSplitTimeNs,
+ gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
+ EXPECT_EQ((int64_t)partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
// Partial buckets are not sent to anomaly tracker.
EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
// Next event should trigger creation of new bucket and send previous full bucket to anomaly
// tracker.
- shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
- event3->write(1);
- event3->write(10);
- event3->init();
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum);
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
// Next event should trigger creation of new bucket.
- shared_ptr<LogEvent> event4 =
- make_shared<LogEvent>(tagId, bucketStartTimeNs + 125 * NS_PER_SEC);
- event4->write(1);
- event4->write(10);
- event4->init();
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum);
- EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ ASSERT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
}
-TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
+TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) {
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -284,59 +307,59 @@
sp<EventMatcherWizard> eventMatcherWizard =
new EventMatcherWizard({new SimpleLogMatchingTracker(
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
.WillOnce(Return(false))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs);
- event->write("some value");
- event->write(2);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
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;
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write("some value");
- event->write(1);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
->mValue.int_value);
- gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs,
+ gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+ EXPECT_EQ(partialBucketSplitTimeNs,
+ gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
- EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
->mValue.int_value);
allData.clear();
- event = make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 1);
- event->write("some value");
- event->write(3);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3));
gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
@@ -358,38 +381,35 @@
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
atomMatcher.set_atom_id(tagId);
- sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
- new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
+ 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;
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write("some value");
- event->write(1);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
->mValue.int_value);
- gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
- EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ ASSERT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
@@ -411,51 +431,48 @@
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
atomMatcher.set_atom_id(tagId);
- sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
- new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+ int64_t conditionChangeNs = bucketStartTimeNs + 8;
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event->write("some value");
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
return true;
}));
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
- logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
+ {ConditionState::kUnknown}, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
gaugeProducer.prepareFirstBucket();
- gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ gaugeProducer.onConditionChanged(true, conditionChangeNs);
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
->mValue.int_value);
- EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+ ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write("some value");
- event->write(110);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
->mValue.int_value);
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()
->second.back()
.mGaugeAtoms.front()
@@ -464,8 +481,8 @@
gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()
->second.back()
.mGaugeAtoms.front()
@@ -485,75 +502,61 @@
dim->set_field(tagId);
dim->add_child()->set_field(1);
- dim = metric.mutable_dimensions_in_condition();
- dim->set_field(conditionTag);
- dim->add_child()->set_field(1);
-
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
atomMatcher.set_atom_id(tagId);
- sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
- new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*wizard, query(_, _, _, _, _, _))
+ EXPECT_CALL(*wizard, query(_, _, _))
.WillRepeatedly(
Invoke([](const int conditionIndex, const ConditionKey& conditionParameters,
- const vector<Matcher>& dimensionFields, const bool isSubsetDim,
- const bool isPartialLink,
- std::unordered_set<HashableDimensionKey>* dimensionKeySet) {
- dimensionKeySet->clear();
+ const bool isPartialLink) {
int pos[] = {1, 0, 0};
Field f(conditionTag, pos, 0);
HashableDimensionKey key;
key.mutableValues()->emplace_back(f, Value((int32_t)1000000));
- dimensionKeySet->insert(key);
return ConditionState::kTrue;
}));
+ int64_t sliceConditionChangeNs = bucketStartTimeNs + 8;
+
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event->write(1000);
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
return true;
}));
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
- logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
+ {ConditionState::kUnknown}, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
gaugeProducer.prepareFirstBucket();
- gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
+ gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first;
- EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(1UL, key.getDimensionKeyInCondition().getValues().size());
- EXPECT_EQ(1000000, key.getDimensionKeyInCondition().getValues()[0].mValue.int_value);
-
- EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+ ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(1000);
- event->write(110);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110));
gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
}
TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
@@ -561,9 +564,10 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(false));
GaugeMetric metric;
metric.set_id(metricId);
@@ -576,13 +580,13 @@
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
atomMatcher.set_atom_id(tagId);
- sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
- new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<EventMatcherWizard> eventMatcherWizard =
+ 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;
@@ -595,13 +599,11 @@
sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
int tagId = 1;
- std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event1->write("some value");
- event1->write(13);
- event1->init();
-
- gaugeProducer.onDataPulled({event1}, /** succeed */ true, bucketStartTimeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13));
+ gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
@@ -609,13 +611,12 @@
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
std::shared_ptr<LogEvent> event2 =
- std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20);
- event2->write("some value");
- event2->write(15);
- event2->init();
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15);
- gaugeProducer.onDataPulled({event2}, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ allData.clear();
+ allData.push_back(event2);
+ gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
@@ -623,14 +624,11 @@
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
- std::shared_ptr<LogEvent> event3 =
- std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10);
- event3->write("some value");
- event3->write(26);
- event3->init();
-
- gaugeProducer.onDataPulled({event3}, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ allData.clear();
+ allData.push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26));
+ gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs);
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()
->second.front()
.mFields->begin()
@@ -638,13 +636,11 @@
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
- // The event4 does not have the gauge field. Thus the current bucket value is 0.
- std::shared_ptr<LogEvent> event4 =
- std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10);
- event4->write("some value");
- event4->init();
- gaugeProducer.onDataPulled({event4}, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ // This event does not have the gauge field. Thus the current bucket value is 0.
+ allData.clear();
+ allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10));
+ gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs);
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty());
}
@@ -664,51 +660,49 @@
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
atomMatcher.set_atom_id(tagId);
- sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
- new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event->write(4);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
- event->write(5);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
return true;
}))
.WillOnce(Return(true));
int triggerId = 5;
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- logEventMatcherIndex, eventMatcherWizard,
- tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
+ wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
+ triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
gaugeProducer.prepareFirstBucket();
- vector<shared_ptr<LogEvent>> allData;
+ ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
- LogEvent trigger(triggerId, bucketStartTimeNs + 10);
- trigger.init();
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
- trigger.setElapsedTimestampNs(bucketStartTimeNs + 20);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
- EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
- trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
+ LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
+ CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+ triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+ ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+ triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size());
+ ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size());
EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin()
->second.back()
.mGaugeAtoms[0]
@@ -738,75 +732,131 @@
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
atomMatcher.set_atom_id(tagId);
- sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
- new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3);
- event->write(3);
- event->write(4);
- event->init();
- data->push_back(event);
+ data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event->write(4);
- event->write(5);
- event->init();
- data->push_back(event);
+ data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
- event->write(4);
- event->write(6);
- event->init();
- data->push_back(event);
+ data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
return true;
}))
.WillOnce(Return(true));
int triggerId = 5;
- GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- logEventMatcherIndex, eventMatcherWizard,
- tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
+ wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
+ triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
gaugeProducer.prepareFirstBucket();
- vector<shared_ptr<LogEvent>> allData;
+ LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
+ CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+ ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size());
+ ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+ triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+ ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+ triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
- LogEvent trigger(triggerId, bucketStartTimeNs + 3);
- trigger.init();
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
- trigger.setElapsedTimestampNs(bucketStartTimeNs + 10);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
- EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size());
- EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
- trigger.setElapsedTimestampNs(bucketStartTimeNs + 20);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
- EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
- trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1);
- gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
-
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.size());
+ ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.size());
auto bucketIt = gaugeProducer.mPastBuckets.begin();
- EXPECT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size());
+ ASSERT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size());
EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
bucketIt++;
- EXPECT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size());
+ ASSERT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size());
EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value);
}
+/*
+ * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
+ * is smaller than the "min_bucket_size_nanos" specified in the metric config.
+ */
+TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
+ GaugeMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(FIVE_MINUTES);
+ metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
+ metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _, _))
+ // Bucket start.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
+ return true;
+ }));
+
+ int triggerId = 5;
+ 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);
+ CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true,
+ FAST /* dump_latency */, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_gauge_metrics());
+ ASSERT_EQ(0, report.gauge_metrics().data_size());
+ ASSERT_EQ(1, report.gauge_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.gauge_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000),
+ report.gauge_metrics().skipped(0).end_bucket_elapsed_millis());
+ ASSERT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size());
+
+ auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0);
+ EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index bf04752..fda3daa 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -52,7 +52,6 @@
const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -63,9 +62,8 @@
int64_t bucketNum = 0;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
- false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- false, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
// Event starts again. This would not change anything as it already starts.
@@ -79,7 +77,7 @@
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(20LL, buckets[eventKey][0].mDuration);
}
@@ -88,7 +86,6 @@
const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -99,9 +96,8 @@
int64_t bucketNum = 0;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
- false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- false, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
@@ -114,7 +110,7 @@
tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(bucketSizeNs + 40 - 1, buckets[eventKey][0].mDuration);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[eventKey][0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs);
@@ -124,7 +120,6 @@
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -135,9 +130,8 @@
int64_t bucketNum = 0;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
- false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- false, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
// The event starts.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
@@ -155,7 +149,7 @@
EXPECT_TRUE(buckets.find(eventKey) == buckets.end());
tracker.flushIfNeeded(bucketStartTimeNs + 4 * bucketSizeNs, &buckets);
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ((3 * bucketSizeNs) + 20 - 1, buckets[eventKey][0].mDuration);
EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[eventKey][0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 4 * bucketSizeNs, buckets[eventKey][0].mBucketEndNs);
@@ -165,7 +159,6 @@
const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -176,9 +169,8 @@
int64_t bucketNum = 0;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
- true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- false, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
// 2 starts
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
@@ -195,14 +187,13 @@
bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(2 * bucketSizeNs + 5 - 1, buckets[eventKey][0].mDuration);
}
TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
const HashableDimensionKey conditionDimKey = key1;
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conditionKey1;
@@ -223,9 +214,8 @@
int64_t eventStopTimeNs = conditionStops2 + 8 * NS_PER_SEC;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- false, bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, true,
- false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
+ 0, bucketStartTimeNs, bucketSizeNs, true, false, {});
EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
@@ -233,20 +223,19 @@
tracker.noteConditionChanged(key1, false, conditionStops1);
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
- EXPECT_EQ(0U, buckets.size());
+ ASSERT_EQ(0U, buckets.size());
tracker.noteConditionChanged(key1, true, conditionStarts2);
tracker.noteConditionChanged(key1, false, conditionStops2);
tracker.noteStop(key1, eventStopTimeNs, false);
tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + 1, &buckets);
- EXPECT_EQ(1U, buckets.size());
+ ASSERT_EQ(1U, buckets.size());
vector<DurationBucket> item = buckets.begin()->second;
- EXPECT_EQ(1UL, item.size());
+ ASSERT_EQ(1UL, item.size());
EXPECT_EQ((int64_t)(13LL * NS_PER_SEC), item[0].mDuration);
}
TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conditionKey1;
@@ -273,9 +262,9 @@
sp<AlarmMonitor> alarmMonitor;
sp<DurationAnomalyTracker> anomalyTracker =
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- true, false, {anomalyTracker});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
+ {anomalyTracker});
tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
@@ -283,11 +272,11 @@
// Remove the anomaly alarm when the duration is no longer fully met.
tracker.noteConditionChanged(key1, false, eventStartTimeNs + 15 * NS_PER_SEC);
- EXPECT_EQ(0U, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(0U, anomalyTracker->mAlarms.size());
// Since the condition was off for 10 seconds, the anomaly should trigger 10 sec later.
tracker.noteConditionChanged(key1, true, eventStartTimeNs + 25 * NS_PER_SEC);
- EXPECT_EQ(1U, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(63ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
}
@@ -295,7 +284,6 @@
// This tests that we correctly compute the predicted time of an anomaly assuming that the current
// state continues forward as-is.
TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) {
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conditionKey1;
@@ -333,16 +321,16 @@
sp<AlarmMonitor> alarmMonitor;
sp<DurationAnomalyTracker> anomalyTracker =
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- true, false, {anomalyTracker});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
+ {anomalyTracker});
tracker.noteStart(key1, false, eventStartTimeNs, conditionKey1);
tracker.noteConditionChanged(key1, true, conditionStarts1);
tracker.noteConditionChanged(key1, false, conditionStops1);
tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2); // Condition is on already.
tracker.noteConditionChanged(key1, true, conditionStarts2);
- EXPECT_EQ(1U, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
auto alarm = anomalyTracker->mAlarms.begin()->second;
int64_t anomalyFireTimeSec = alarm->timestampSec;
EXPECT_EQ(conditionStarts2 + 36 * NS_PER_SEC,
@@ -353,7 +341,7 @@
// gets correctly taken into account in future predictAnomalyTimestampNs calculations.
std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarms({alarm});
anomalyTracker->informAlarmsFired(anomalyFireTimeSec * NS_PER_SEC, firedAlarms);
- EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(0u, anomalyTracker->mAlarms.size());
int64_t refractoryPeriodEndsSec = anomalyFireTimeSec + refPeriodSec;
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), refractoryPeriodEndsSec);
@@ -364,7 +352,7 @@
tracker.noteStop(key2, eventStopTimeNs, false);
tracker.noteStart(key1, true, eventStopTimeNs + 1000000, conditionKey1);
// Anomaly is ongoing, but we're still in the refractory period.
- EXPECT_EQ(1U, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ(refractoryPeriodEndsSec, (long long)(alarm->timestampSec));
@@ -381,7 +369,6 @@
// Suppose A starts, then B starts, and then A stops. We still need to set an anomaly based on the
// elapsed duration of B.
TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) {
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conditionKey1;
@@ -416,14 +403,14 @@
sp<AlarmMonitor> alarmMonitor;
sp<DurationAnomalyTracker> anomalyTracker =
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
- true, false, {anomalyTracker});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
+ {anomalyTracker});
tracker.noteStart(key1, true, eventStartTimeNs1, conditionKey1);
tracker.noteStart(key2, true, eventStartTimeNs2, conditionKey2);
tracker.noteStop(key1, eventStopTimeNs1, false);
- EXPECT_EQ(1U, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(1U, anomalyTracker->mAlarms.size());
auto alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ(eventStopTimeNs1 + 35 * NS_PER_SEC,
(unsigned long long)(alarm->timestampSec * NS_PER_SEC));
@@ -434,4 +421,4 @@
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
\ No newline at end of file
+#endif
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 7c2b423..1d6f7de 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -51,7 +51,6 @@
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -62,9 +61,9 @@
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
int64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, false, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
+ bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
+ false, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -75,7 +74,7 @@
tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration);
}
@@ -84,7 +83,6 @@
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -94,9 +92,8 @@
int64_t bucketNum = 0;
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, false, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
@@ -106,7 +103,7 @@
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration);
}
@@ -117,7 +114,6 @@
{getMockedDimensionKey(TagId, 1, "maps")};
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -127,9 +123,8 @@
int64_t bucketNum = 0;
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, false, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
@@ -138,7 +133,7 @@
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(2003LL, buckets[eventKey][0].mDuration);
}
@@ -147,7 +142,6 @@
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -158,9 +152,8 @@
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
int64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, false, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -168,7 +161,7 @@
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, ConditionKey());
EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
- EXPECT_EQ(2u, buckets[eventKey].size());
+ ASSERT_EQ(2u, buckets[eventKey].size());
EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
@@ -176,7 +169,7 @@
tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 12, false);
tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(2u, buckets[eventKey].size());
+ ASSERT_EQ(2u, buckets[eventKey].size());
EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
}
@@ -186,13 +179,12 @@
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
- EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)) // #4
+ EXPECT_CALL(*wizard, query(_, key1, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -203,9 +195,9 @@
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
int64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, true, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
+ bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
+ true, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -215,7 +207,7 @@
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(5LL, buckets[eventKey][0].mDuration);
}
@@ -224,13 +216,12 @@
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
- EXPECT_CALL(*wizard, query(_, key1, _, _, _, _))
+ EXPECT_CALL(*wizard, query(_, key1, _))
.Times(2)
.WillOnce(Return(ConditionState::kFalse))
.WillOnce(Return(ConditionState::kTrue));
@@ -243,9 +234,9 @@
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
int64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- false, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, true, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
+ bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
+ true, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
// condition to false; record duration 5n
@@ -257,7 +248,7 @@
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(1005LL, buckets[eventKey][0].mDuration);
}
@@ -266,13 +257,12 @@
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
- EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)) // #4
+ EXPECT_CALL(*wizard, query(_, key1, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
@@ -282,9 +272,8 @@
int64_t bucketNum = 0;
int64_t eventStartTimeNs = bucketStartTimeNs + 1;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, true, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
@@ -297,7 +286,7 @@
tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(15LL, buckets[eventKey][0].mDuration);
}
@@ -306,7 +295,6 @@
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -324,9 +312,9 @@
sp<AlarmMonitor> alarmMonitor;
sp<DurationAnomalyTracker> anomalyTracker =
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, true, false, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
+ bucketNum, bucketStartTimeNs, bucketSizeNs, true, false,
+ {anomalyTracker});
// Nothing in the past bucket.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
@@ -334,7 +322,7 @@
tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 3, false);
- EXPECT_EQ(0u, buckets[eventKey].size());
+ ASSERT_EQ(0u, buckets[eventKey].size());
int64_t event1StartTimeNs = eventStartTimeNs + 10;
tracker.noteStart(kEventKey1, true, event1StartTimeNs, ConditionKey());
@@ -347,7 +335,7 @@
tracker.noteStop(kEventKey1, event1StopTimeNs, false);
EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
- EXPECT_EQ(1u, buckets[eventKey].size());
+ ASSERT_EQ(1u, buckets[eventKey].size());
EXPECT_EQ(3LL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
buckets[eventKey][0].mDuration);
@@ -371,7 +359,6 @@
}
TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) {
- vector<Matcher> dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -387,7 +374,7 @@
sp<DurationAnomalyTracker> anomalyTracker =
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard, 1,
- dimensionInCondition,
+
true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, true, false, {anomalyTracker});
@@ -415,7 +402,7 @@
for (int j = 0; j < 3; j++) {
int64_t thresholdNs = j * bucketSizeNs + 5 * NS_PER_SEC;
for (int i = 0; i <= 7; ++i) {
- vector<Matcher> dimensionInCondition;
+
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -431,9 +418,8 @@
sp<AlarmMonitor> alarmMonitor;
sp<DurationAnomalyTracker> anomalyTracker =
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY,
- wizard, 1, dimensionInCondition,
- true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
+ OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, wizard,
+ 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
bucketSizeNs, true, false, {anomalyTracker});
int64_t eventStartTimeNs = bucketStartTimeNs + 9 * NS_PER_SEC;
@@ -472,7 +458,6 @@
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -491,20 +476,20 @@
sp<AlarmMonitor> alarmMonitor;
sp<DurationAnomalyTracker> anomalyTracker =
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs,
- bucketSizeNs, false, false, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
+ bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs,
+ false, false, {anomalyTracker});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
EXPECT_TRUE(tracker.mStarted.empty());
- EXPECT_EQ(10LL, tracker.mDuration); // 10ns
+ EXPECT_EQ(10LL, tracker.mStateKeyDurationMap[DEFAULT_DIMENSION_KEY].mDuration); // 10ns
- EXPECT_EQ(0u, tracker.mStarted.size());
+ ASSERT_EQ(0u, tracker.mStarted.size());
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 20, ConditionKey());
- EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
EXPECT_EQ((long long)(52ULL * NS_PER_SEC), // (10s + 1s + 1ns + 20ns) - 10ns + 40s, rounded up
(long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
// The alarm is set to fire at 52s, and when it does, an anomaly would be declared. However,
@@ -522,7 +507,6 @@
const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
- vector<Matcher> dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -541,34 +525,34 @@
sp<AlarmMonitor> alarmMonitor;
sp<DurationAnomalyTracker> anomalyTracker =
new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
- true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs,
- bucketSizeNs, false, false, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
+ bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, false,
+ false, {anomalyTracker});
- tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
- EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+ tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
+ ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
sp<const InternalAlarm> alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(55ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
tracker.noteStop(kEventKey1, 17 * NS_PER_SEC, false); // stop key1 (2 seconds later)
- EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(0u, anomalyTracker->mAlarms.size());
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
- tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again
- EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+ tracker.noteStart(kEventKey1, true, 22 * NS_PER_SEC, conkey); // start key1 again
+ ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
- tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2
- EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+ tracker.noteStart(kEventKey2, true, 32 * NS_PER_SEC, conkey); // start key2
+ ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
tracker.noteStop(kEventKey1, 47 * NS_PER_SEC, false); // stop key1
- EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(1u, anomalyTracker->mAlarms.size());
alarm = anomalyTracker->mAlarms.begin()->second;
EXPECT_EQ((long long)(60ULL * NS_PER_SEC), (long long)(alarm->timestampSec * NS_PER_SEC));
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 0U);
@@ -576,11 +560,11 @@
// Now, at 60s, which is 38s after key1 started again, we have reached 40s of 'on' time.
std::unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> firedAlarms({alarm});
anomalyTracker->informAlarmsFired(62 * NS_PER_SEC, firedAlarms);
- EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(0u, anomalyTracker->mAlarms.size());
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
tracker.noteStop(kEventKey2, 69 * NS_PER_SEC, false); // stop key2
- EXPECT_EQ(0u, anomalyTracker->mAlarms.size());
+ ASSERT_EQ(0u, anomalyTracker->mAlarms.size());
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(eventKey), 62U + refPeriodSec);
}
@@ -589,4 +573,4 @@
} // namespace android
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
\ No newline at end of file
+#endif
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 2262c76..5666501 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -12,21 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/matchers/SimpleLogMatchingTracker.h"
#include "src/metrics/ValueMetricProducer.h"
-#include "src/stats_log_util.h"
-#include "metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <math.h>
#include <stdio.h>
+
#include <vector>
+#include "metrics_test_helper.h"
+#include "src/matchers/SimpleLogMatchingTracker.h"
+#include "src/metrics/MetricProducer.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
using namespace testing;
using android::sp;
-using android::util::ProtoReader;
using std::make_shared;
using std::set;
using std::shared_ptr;
@@ -39,6 +41,8 @@
namespace os {
namespace statsd {
+namespace {
+
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
const int64_t metricId = 123;
@@ -56,10 +60,18 @@
static void assertPastBucketValuesSingleKey(
const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
const std::initializer_list<int>& expectedValuesList,
- const std::initializer_list<int64_t>& expectedDurationNsList) {
- std::vector<int> expectedValues(expectedValuesList);
- std::vector<int64_t> expectedDurationNs(expectedDurationNsList);
+ const std::initializer_list<int64_t>& expectedDurationNsList,
+ const std::initializer_list<int64_t>& expectedStartTimeNsList,
+ const std::initializer_list<int64_t>& expectedEndTimeNsList) {
+ vector<int> expectedValues(expectedValuesList);
+ vector<int64_t> expectedDurationNs(expectedDurationNsList);
+ vector<int64_t> expectedStartTimeNs(expectedStartTimeNsList);
+ vector<int64_t> expectedEndTimeNs(expectedEndTimeNsList);
+
ASSERT_EQ(expectedValues.size(), expectedDurationNs.size());
+ ASSERT_EQ(expectedValues.size(), expectedStartTimeNs.size());
+ ASSERT_EQ(expectedValues.size(), expectedEndTimeNs.size());
+
if (expectedValues.size() == 0) {
ASSERT_EQ(0, mPastBuckets.size());
return;
@@ -68,27 +80,23 @@
ASSERT_EQ(1, mPastBuckets.size());
ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
- auto buckets = mPastBuckets.begin()->second;
+ const vector<ValueBucket>& buckets = mPastBuckets.begin()->second;
for (int i = 0; i < expectedValues.size(); i++) {
EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
<< "Values differ at index " << i;
EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs)
<< "Condition duration value differ at index " << i;
+ EXPECT_EQ(expectedStartTimeNs[i], buckets[i].mBucketStartNs)
+ << "Start time differs at index " << i;
+ EXPECT_EQ(expectedEndTimeNs[i], buckets[i].mBucketEndNs)
+ << "End time differs at index " << i;
}
}
+} // anonymous namespace
+
class ValueMetricProducerTestHelper {
-
- public:
- static shared_ptr<LogEvent> createEvent(int64_t eventTimeNs, int64_t value) {
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventTimeNs);
- event->write(tagId);
- event->write(value);
- event->write(value);
- event->init();
- return event;
- }
-
+public:
static sp<ValueMetricProducer> createValueProducerNoConditions(
sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
UidMap uidMap;
@@ -98,19 +106,22 @@
new EventMatcherWizard({new SimpleLogMatchingTracker(
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
+ .WillOnce(Return());
+ 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);
@@ -118,15 +129,67 @@
new EventMatcherWizard({new SimpleLogMatchingTracker(
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
+ .WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _))
+ .WillRepeatedly(Return());
- sp<ValueMetricProducer> valueProducer =
- new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
- eventMatcherWizard, tagId, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ 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> 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);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
+ .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);
+ valueProducer->prepareFirstBucket();
+ return valueProducer;
+ }
+
+ static sp<ValueMetricProducer> createValueProducerWithConditionAndState(
+ sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
+ vector<int32_t> slicedStateAtoms,
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap,
+ ConditionState conditionAfterFirstBucketPrepared) {
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
+ .WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _))
+ .WillRepeatedly(Return());
+
+ sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+ kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown},
+ wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap);
+ valueProducer->prepareFirstBucket();
+ valueProducer->mCondition = conditionAfterFirstBucketPrepared;
return valueProducer;
}
@@ -145,8 +208,27 @@
metric.set_condition(StringToId("SCREEN_ON"));
return metric;
}
+
+ static ValueMetric createMetricWithState(string state) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ 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.
+class ValueMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
+
+INSTANTIATE_TEST_SUITE_P(ValueMetricProducerTest_PartialBucket,
+ ValueMetricProducerTest_PartialBucket,
+ testing::Values(APP_UPGRADE, BOOT_COMPLETE));
/*
* Tests that the first bucket works correctly
@@ -166,9 +248,9 @@
// 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));
@@ -196,8 +278,8 @@
// 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();
@@ -210,16 +292,13 @@
* Tests pulled atoms with no conditions
*/
TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(3);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
}));
@@ -228,63 +307,55 @@
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(11);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(11, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(8, curInterval.value.long_value);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- event->write(tagId);
- event->write(23);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(23, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(23, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(12, curInterval.value.long_value);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
- event->write(tagId);
- event->write(36);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(36, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(13, curInterval.value.long_value);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
@@ -293,28 +364,27 @@
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
}
-TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+TEST_P(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Initialize bucket.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write(tagId);
- event->write(1);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
return true;
}))
// Partial bucket.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](
+ int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
- event->write(tagId);
- event->write(5);
- event->init();
- data->push_back(event);
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs + 8, 5));
return true;
}));
@@ -324,34 +394,32 @@
// First bucket ends.
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
- event->write(tagId);
- event->write(2);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 2));
valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
// Partial buckets created in 2nd bucket.
- valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+ EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1, valueProducer->getCurrentBucketNum());
- // One full bucket and one partial bucket.
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second;
- EXPECT_EQ(2UL, buckets.size());
- // Full bucket (2 - 1)
- EXPECT_EQ(1, buckets[0].values[0].long_value);
- EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs);
- // Full bucket (5 - 3)
- EXPECT_EQ(3, buckets[1].values[0].long_value);
- // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2]
- EXPECT_EQ(2, buckets[1].mConditionTrueNs);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1, 3},
+ {bucketSizeNs, partialBucketSplitTimeNs - bucket2StartTimeNs},
+ {bucketStartTimeNs, bucket2StartTimeNs},
+ {bucket2StartTimeNs, partialBucketSplitTimeNs});
}
/*
* Tests pulled atoms with filtering
*/
TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -364,81 +432,67 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(3);
- event->write(3);
- event->init();
- data->push_back(event);
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
return true;
}));
sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
- kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- logEventMatcherIndex, eventMatcherWizard, tagId,
- bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
valueProducer->prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(3);
- event->write(11);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(11, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(8, curInterval.value.long_value);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- event->write(4);
- event->write(23);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket3StartTimeNs + 1, 4, 23));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// No new data seen, so data has been cleared.
- EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(11, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(8, curInterval.value.long_value);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
- event->write(3);
- event->write(36);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
// the base was reset
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(36, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
}
@@ -451,61 +505,54 @@
metric.set_use_absolute_value_on_reset(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(11);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(11, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- event->write(tagId);
- event->write(10);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(10, curInterval.base.long_value);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(10, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(10, curInterval.value.long_value);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
- event->write(tagId);
- event->write(36);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(36, curInterval.base.long_value);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(26, curInterval.value.long_value);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
@@ -518,57 +565,50 @@
TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(false));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(11);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(11, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- event->write(tagId);
- event->write(10);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(10, curInterval.base.long_value);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(10, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
- event->write(tagId);
- event->write(36);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(36, curInterval.base.long_value);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(26, curInterval.value.long_value);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
}
@@ -581,82 +621,81 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(130);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1); // Third condition change.
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- event->write(tagId);
- event->write(180);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
// startUpdated:false sum:0 start:100
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(100, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(1);
- event->write(110);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(110, curInterval.base.long_value);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(110, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(10, curInterval.value.long_value);
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(20, curInterval.value.long_value);
- EXPECT_EQ(false, curInterval.hasBase);
+ EXPECT_EQ(false, curBaseInfo.hasBase);
valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1},
+ {bucketStartTimeNs, bucket2StartTimeNs},
+ {bucket2StartTimeNs, bucket3StartTimeNs});
}
-TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
+TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
UidMap uidMap;
@@ -667,42 +706,58 @@
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();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- valueProducer.notifyAppUpgrade(bucketStartTimeNs + 150, "ANY.APP", 1, 1);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
+ int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150;
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10},
+ {partialBucketSplitTimeNs - bucketStartTimeNs},
+ {bucketStartTimeNs}, {partialBucketSplitTimeNs});
+ EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(0, valueProducer.getCurrentBucketNum());
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
- event2->write(1);
- event2->write(10);
- event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
+ // Event arrives after the bucket split.
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 20);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10},
+ {partialBucketSplitTimeNs - bucketStartTimeNs},
+ {bucketStartTimeNs}, {partialBucketSplitTimeNs});
+ EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(0, valueProducer.getCurrentBucketNum());
// Next value should create a new bucket.
- shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
- event3->write(1);
- event3->write(10);
- event3->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
- EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10, 20},
+ {partialBucketSplitTimeNs - bucketStartTimeNs,
+ bucket2StartTimeNs - partialBucketSplitTimeNs},
+ {bucketStartTimeNs, partialBucketSplitTimeNs},
+ {partialBucketSplitTimeNs, bucket2StartTimeNs});
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1, valueProducer.getCurrentBucketNum());
}
-TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -712,53 +767,53 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
.WillOnce(Return(true))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](
+ int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 149);
- event->write(tagId);
- event->write(120);
- event->init();
- data->push_back(event);
+ 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();
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(100);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150});
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+ EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1, valueProducer.getCurrentBucketNum());
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}, {bucket2StartTimeNs},
+ {partialBucketSplitTimeNs});
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- event->write(tagId);
- event->write(150);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150));
valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(20L,
- valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30},
- {150, bucketSizeNs - 150});
+ EXPECT_EQ(2, valueProducer.getCurrentBucketNum());
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, {150, bucketSizeNs - 150},
+ {bucket2StartTimeNs, partialBucketSplitTimeNs},
+ {partialBucketSplitTimeNs, bucket3StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
@@ -773,69 +828,72 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ 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,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(100);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150);
+ ASSERT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
}
-TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
+TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write(tagId);
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs,
+ bucket2StartTimeNs - 100); // Condition change to false time.
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100);
- event->write(tagId);
- event->write(120);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 120));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
- valueProducer->onConditionChanged(false, bucket2StartTimeNs-100);
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
EXPECT_FALSE(valueProducer->mCondition);
- valueProducer->notifyAppUpgrade(bucket2StartTimeNs-50, "ANY.APP", 1, 1);
+ int64_t partialBucketSplitTimeNs = bucket2StartTimeNs - 50;
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
// Expect one full buckets already done and starting a partial bucket.
- EXPECT_EQ(bucket2StartTimeNs-50, valueProducer->mCurrentBucketStartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
- EXPECT_EQ(bucketStartTimeNs,
- valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+ EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
+ EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20},
- {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)});
+ {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)},
+ {bucketStartTimeNs}, {partialBucketSplitTimeNs});
EXPECT_FALSE(valueProducer->mCondition);
}
@@ -851,35 +909,36 @@
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();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
- event2->write(1);
- event2->write(20);
- event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(30, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -894,59 +953,54 @@
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;
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has 1 slice
- EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15);
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
- event2->write(1);
- event2->write(20);
- event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer.mCurrentSlicedBucket.begin()->second[0];
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(20, curInterval.value.long_value);
- shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 30);
- event3->write(1);
- event3->write(30);
- event3->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 30);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
- shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 40);
- event4->write(1);
- event4->write(40);
- event4->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 40);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20});
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}, {bucketStartTimeNs},
+ {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -969,136 +1023,137 @@
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);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 1 * NS_PER_SEC, 10);
- shared_ptr<LogEvent> event1
- = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1 * NS_PER_SEC);
- event1->write(161);
- event1->write(10); // value of interest
- event1->init();
- shared_ptr<LogEvent> event2
- = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 + NS_PER_SEC);
- event2->write(162);
- event2->write(20); // value of interest
- event2->init();
- shared_ptr<LogEvent> event3
- = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC);
- event3->write(163);
- event3->write(130); // value of interest
- event3->init();
- shared_ptr<LogEvent> event4
- = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC);
- event4->write(35);
- event4->write(1); // value of interest
- event4->init();
- shared_ptr<LogEvent> event5
- = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
- event5->write(45);
- event5->write(150); // value of interest
- event5->init();
- shared_ptr<LogEvent> event6
- = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC);
- event6->write(25);
- event6->write(160); // value of interest
- event6->init();
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 2 + NS_PER_SEC, 20);
+
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event3, tagId,
+ bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, 130);
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event4, tagId,
+ bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC, 1);
+
+ LogEvent event5(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event5, tagId,
+ bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, 150);
+
+ LogEvent event6(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event6, tagId,
+ bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC, 160);
// Two events in bucket #0.
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// Value sum == 30 <= 130.
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// One event in bucket #2. No alarm as bucket #0 is trashed out.
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
// Value sum == 130 <= 130.
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// Three events in bucket #3.
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
// Anomaly at event 4 since Value sum == 131 > 130!
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
+ std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
// Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+ std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
// Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
- std::ceil(1.0 * event6->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+ std::ceil(1.0 * event6.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
}
// Test value metric no condition, the pull on bucket boundary come in time and too late
TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Return(true));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
vector<shared_ptr<LogEvent>> allData;
// pull 1
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(11);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
// startUpdated:true sum:0 start:11
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(11, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(11, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
// pull 2 at correct time
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- event->write(tagId);
- event->write(23);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
// tartUpdated:false sum:12
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(23, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(23, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs},
+ {bucket2StartTimeNs}, {bucket3StartTimeNs});
// pull 3 come late.
// The previous bucket gets closed with error. (Has start value 23, no ending)
// Another bucket gets closed with error. (No start, but ending with 36)
// The new bucket is back to normal.
allData.clear();
- event = make_shared<LogEvent>(tagId, bucket6StartTimeNs + 1);
- event->write(tagId);
- event->write(36);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
// startUpdated:false sum:12
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(36, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(36, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs},
+ {bucket2StartTimeNs}, {bucket3StartTimeNs});
+ // The 1st bucket is dropped because of no data
+ // The 3rd bucket is dropped due to multiple buckets being skipped.
+ ASSERT_EQ(2, valueProducer->mSkippedBuckets.size());
+
+ EXPECT_EQ(bucketStartTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
+ EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
+ ASSERT_EQ(1, valueProducer->mSkippedBuckets[0].dropEvents.size());
+ EXPECT_EQ(NO_DATA, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
+ EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].dropEvents[0].dropTimeNs);
+
+ EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[1].bucketStartTimeNs);
+ EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].bucketEndTimeNs);
+ ASSERT_EQ(1, valueProducer->mSkippedBuckets[1].dropEvents.size());
+ EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[1].dropEvents[0].reason);
+ EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].dropEvents[0].dropTimeNs);
}
/*
@@ -1109,56 +1164,58 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
// condition becomes false
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(120);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(100, curInterval.base.long_value);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
// pull on bucket boundary come late, condition change happens before it
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
- EXPECT_EQ(false, curInterval.hasBase);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
+ EXPECT_EQ(false, curBaseInfo.hasBase);
// Now the alarm is delivered.
// since the condition turned to off before this pull finish, it has no effect
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(false, curInterval.hasBase);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
}
@@ -1170,87 +1227,90 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
// condition becomes false
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(120);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
return true;
}))
// condition becomes true again
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 25);
- event->write(tagId);
- event->write(130);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
// startUpdated:false sum:0 start:100
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(100, curInterval.base.long_value);
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
// pull on bucket boundary come late, condition change happens before it
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(false, curInterval.hasBase);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
// condition changed to true again, before the pull alarm is delivered
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(130, curInterval.base.long_value);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(130, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
// Now the alarm is delivered, but it is considered late, the data will be used
// for the new bucket since it was just pulled.
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 50, 140));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(140, curInterval.base.long_value);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(140, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(10, curInterval.value.long_value);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
allData.clear();
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 160));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30},
- {bucketSizeNs - 8, bucketSizeNs - 24});
+ assertPastBucketValuesSingleKey(
+ valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24},
+ {bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
@@ -1266,36 +1326,35 @@
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();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
- event2->write(1);
- event2->write(20);
- event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
@@ -1311,38 +1370,34 @@
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();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
- event2->write(1);
- event2->write(20);
- event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(20, curInterval.value.long_value);
- valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
- /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); */
- /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); */
- /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
+ valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
@@ -1358,39 +1413,36 @@
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();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
- event2->write(1);
- event2->write(15);
- event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval;
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(1, curInterval.sampleSize);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(25, curInterval.value.long_value);
EXPECT_EQ(2, curInterval.sampleSize);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ ASSERT_EQ(1UL, valueProducer.mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value -
12.5) < epsilon);
@@ -1409,36 +1461,34 @@
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();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
- event2->write(1);
- event2->write(15);
- event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
+
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(25, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
@@ -1455,63 +1505,61 @@
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();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15);
- event2->write(1);
- event2->write(15);
- event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer.mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(10, curInterval.base.long_value);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(10, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(5, curInterval.value.long_value);
// no change in data.
- shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
- event3->write(1);
- event3->write(15);
- event3->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(15, curInterval.base.long_value);
- EXPECT_EQ(true, curInterval.hasValue);
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
- shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15);
- event4->write(1);
- event4->write(15);
- event4->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(15, curInterval.base.long_value);
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(15, curBaseInfo.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(0, curInterval.value.long_value);
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(15, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(0, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
@@ -1529,85 +1577,84 @@
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();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event1->write(1);
- event1->write(10);
- event1->write(20);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15);
- event2->write(1);
- event2->write(15);
- event2->write(22);
- event2->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
- // has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval0 =
- valueProducer.mCurrentSlicedBucket.begin()->second[0];
- ValueMetricProducer::Interval curInterval1 =
- valueProducer.mCurrentSlicedBucket.begin()->second[1];
- EXPECT_EQ(true, curInterval0.hasBase);
- EXPECT_EQ(10, curInterval0.base.long_value);
- EXPECT_EQ(false, curInterval0.hasValue);
- EXPECT_EQ(true, curInterval1.hasBase);
- EXPECT_EQ(20, curInterval1.base.long_value);
- EXPECT_EQ(false, curInterval1.hasValue);
+ LogEvent event1(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10, 20);
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+ LogEvent event2(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15, 22);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+ // has one slice
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(10, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(20, curBaseInfo.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
// has one slice
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1];
- EXPECT_EQ(true, curInterval0.hasValue);
- EXPECT_EQ(5, curInterval0.value.long_value);
- EXPECT_EQ(true, curInterval1.hasValue);
- EXPECT_EQ(2, curInterval1.value.long_value);
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(5, curInterval.value.long_value);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(2, curInterval.value.long_value);
// no change in first value field
- shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
- event3->write(1);
- event3->write(15);
- event3->write(25);
- event3->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1];
- EXPECT_EQ(true, curInterval0.hasBase);
- EXPECT_EQ(15, curInterval0.base.long_value);
- EXPECT_EQ(true, curInterval0.hasValue);
- EXPECT_EQ(true, curInterval1.hasBase);
- EXPECT_EQ(25, curInterval1.base.long_value);
- EXPECT_EQ(true, curInterval1.hasValue);
+ LogEvent event3(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15, 25);
- shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15);
- event4->write(1);
- event4->write(15);
- event4->write(29);
- event4->init();
- valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
- EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
- curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1];
- EXPECT_EQ(true, curInterval0.hasBase);
- EXPECT_EQ(15, curInterval0.base.long_value);
- EXPECT_EQ(true, curInterval0.hasValue);
- EXPECT_EQ(true, curInterval1.hasBase);
- EXPECT_EQ(29, curInterval1.base.long_value);
- EXPECT_EQ(true, curInterval1.hasValue);
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(15, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(25, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+
+ LogEvent event4(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15, 29);
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
+ ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(15, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+ curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(29, curBaseInfo.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size());
+ ASSERT_EQ(1UL, valueProducer.mPastBuckets.size());
+ ASSERT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ ASSERT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size());
+ ASSERT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size());
EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[0].mConditionTrueNs);
EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value);
@@ -1630,47 +1677,38 @@
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(1);
- event->write(3);
- event->init();
- data->push_back(event);
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
}));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
auto iter = valueProducer->mCurrentSlicedBucket.begin();
auto& interval1 = iter->second[0];
+ auto iterBase = valueProducer->mCurrentBaseInfo.begin();
+ auto& baseInfo1 = iterBase->second[0];
EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, interval1.hasBase);
- EXPECT_EQ(3, interval1.base.long_value);
+ EXPECT_EQ(true, baseInfo1.hasBase);
+ EXPECT_EQ(3, baseInfo1.base.long_value);
EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event1->write(2);
- event1->write(4);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event2->write(1);
- event2->write(11);
- event2->init();
- allData.push_back(event1);
- allData.push_back(event2);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, interval1.hasBase);
- EXPECT_EQ(11, interval1.base.long_value);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, baseInfo1.hasBase);
+ EXPECT_EQ(11, baseInfo1.base.long_value);
EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(8, interval1.value.long_value);
@@ -1680,15 +1718,23 @@
break;
}
}
+ auto itBase = valueProducer->mCurrentBaseInfo.begin();
+ for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) {
+ if (itBase != iterBase) {
+ break;
+ }
+ }
EXPECT_TRUE(it != iter);
+ EXPECT_TRUE(itBase != iterBase);
auto& interval2 = it->second[0];
+ auto& baseInfo2 = itBase->second[0];
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(4, interval2.base.long_value);
+ EXPECT_EQ(true, baseInfo2.hasBase);
+ EXPECT_EQ(4, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_EQ(4, interval2.value.long_value);
- EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
auto iterator = valueProducer->mPastBuckets.begin();
EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
EXPECT_EQ(8, iterator->second[0].values[0].long_value);
@@ -1707,107 +1753,100 @@
metric.set_use_zero_default_base(true);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(1);
- event->write(3);
- event->init();
- data->push_back(event);
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
}));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- auto iter = valueProducer->mCurrentSlicedBucket.begin();
- auto& interval1 = iter->second[0];
- EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, interval1.hasBase);
- EXPECT_EQ(3, interval1.base.long_value);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ const auto& it = valueProducer->mCurrentSlicedBucket.begin();
+ ValueMetricProducer::Interval& interval1 = it->second[0];
+ ValueMetricProducer::BaseInfo& baseInfo1 =
+ valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, baseInfo1.hasBase);
+ EXPECT_EQ(3, baseInfo1.base.long_value);
EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event1->write(2);
- event1->write(4);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event2->write(1);
- event2->write(11);
- event2->init();
- allData.push_back(event1);
- allData.push_back(event2);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, interval1.hasBase);
- EXPECT_EQ(11, interval1.base.long_value);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, baseInfo1.hasBase);
+ EXPECT_EQ(11, baseInfo1.base.long_value);
EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(8, interval1.value.long_value);
- auto it = valueProducer->mCurrentSlicedBucket.begin();
- for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
- if (it != iter) {
+ auto it2 = valueProducer->mCurrentSlicedBucket.begin();
+ for (; it2 != valueProducer->mCurrentSlicedBucket.end(); it2++) {
+ if (it2 != it) {
break;
}
}
- EXPECT_TRUE(it != iter);
- auto& interval2 = it->second[0];
- EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(4, interval2.base.long_value);
+ EXPECT_TRUE(it2 != it);
+ ValueMetricProducer::Interval& interval2 = it2->second[0];
+ ValueMetricProducer::BaseInfo& baseInfo2 =
+ valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+ EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(true, baseInfo2.hasBase);
+ EXPECT_EQ(4, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_EQ(4, interval2.value.long_value);
- EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
// next pull somehow did not happen, skip to end of bucket 3
allData.clear();
- event1 = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
- event1->write(2);
- event1->write(5);
- event1->init();
- allData.push_back(event1);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(5, interval2.base.long_value);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, baseInfo2.hasBase);
+ EXPECT_EQ(5, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
- EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
allData.clear();
- event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
- event1->write(2);
- event1->write(13);
- event1->init();
- allData.push_back(event1);
- event2 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
- event2->write(1);
- event2->write(5);
- event2->init();
- allData.push_back(event2);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 13));
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 5));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
- EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- auto it1 = std::next(valueProducer->mCurrentSlicedBucket.begin())->second[0];
- EXPECT_EQ(true, it1.hasBase);
- EXPECT_EQ(13, it1.base.long_value);
- EXPECT_EQ(false, it1.hasValue);
- EXPECT_EQ(8, it1.value.long_value);
- auto it2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, it2.hasBase);
- EXPECT_EQ(5, it2.base.long_value);
- EXPECT_EQ(false, it2.hasValue);
- EXPECT_EQ(5, it2.value.long_value);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Get new references now that entries have been deleted from the map
+ const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
+ const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
+ ASSERT_EQ(it3->second.size(), 1);
+ ASSERT_EQ(it4->second.size(), 1);
+ ValueMetricProducer::Interval& interval3 = it3->second[0];
+ ValueMetricProducer::Interval& interval4 = it4->second[0];
+ ValueMetricProducer::BaseInfo& baseInfo3 =
+ valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0];
+ ValueMetricProducer::BaseInfo& baseInfo4 =
+ valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0];
+
+ EXPECT_EQ(true, baseInfo3.hasBase);
+ EXPECT_EQ(5, baseInfo3.base.long_value);
+ EXPECT_EQ(false, interval3.hasValue);
+ EXPECT_EQ(5, interval3.value.long_value);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
- EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
+
+ EXPECT_EQ(true, baseInfo4.hasBase);
+ EXPECT_EQ(13, baseInfo4.base.long_value);
+ EXPECT_EQ(false, interval4.hasValue);
+ EXPECT_EQ(8, interval4.value.long_value);
+
+ ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
}
/*
@@ -1819,50 +1858,42 @@
metric.mutable_dimensions_in_what()->add_child()->set_field(1);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(1);
- event->write(3);
- event->init();
- data->push_back(event);
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
return true;
}));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
auto iter = valueProducer->mCurrentSlicedBucket.begin();
auto& interval1 = iter->second[0];
+ auto iterBase = valueProducer->mCurrentBaseInfo.begin();
+ auto& baseInfo1 = iterBase->second[0];
EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, interval1.hasBase);
- EXPECT_EQ(3, interval1.base.long_value);
+ EXPECT_EQ(true, baseInfo1.hasBase);
+ EXPECT_EQ(3, baseInfo1.base.long_value);
EXPECT_EQ(false, interval1.hasValue);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
+
vector<shared_ptr<LogEvent>> allData;
-
allData.clear();
- shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event1->write(2);
- event1->write(4);
- event1->init();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event2->write(1);
- event2->write(11);
- event2->init();
- allData.push_back(event1);
- allData.push_back(event2);
-
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(true, interval1.hasBase);
- EXPECT_EQ(11, interval1.base.long_value);
+
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, baseInfo1.hasBase);
+ EXPECT_EQ(11, baseInfo1.base.long_value);
EXPECT_EQ(false, interval1.hasValue);
EXPECT_EQ(8, interval1.value.long_value);
EXPECT_FALSE(interval1.seenNewData);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
auto it = valueProducer->mCurrentSlicedBucket.begin();
for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
@@ -1870,51 +1901,57 @@
break;
}
}
+ auto itBase = valueProducer->mCurrentBaseInfo.begin();
+ for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) {
+ if (itBase != iterBase) {
+ break;
+ }
+ }
EXPECT_TRUE(it != iter);
- auto& interval2 = it->second[0];
+ EXPECT_TRUE(itBase != iterBase);
+ auto interval2 = it->second[0];
+ auto baseInfo2 = itBase->second[0];
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(4, interval2.base.long_value);
+ EXPECT_EQ(true, baseInfo2.hasBase);
+ EXPECT_EQ(4, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
// next pull somehow did not happen, skip to end of bucket 3
allData.clear();
- event1 = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
- event1->write(2);
- event1->write(5);
- event1->init();
- allData.push_back(event1);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
- // Only one interval left. One was trimmed.
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Only one interval left. One was trimmed.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
- EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(5, interval2.base.long_value);
+ EXPECT_EQ(true, baseInfo2.hasBase);
+ EXPECT_EQ(5, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
allData.clear();
- event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1);
- event1->write(2);
- event1->write(14);
- event1->init();
- allData.push_back(event1);
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, interval2.hasBase);
- EXPECT_EQ(14, interval2.base.long_value);
+ baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, baseInfo2.hasBase);
+ EXPECT_EQ(14, baseInfo2.base.long_value);
EXPECT_EQ(false, interval2.hasValue);
EXPECT_FALSE(interval2.seenNewData);
ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
auto iterator = valueProducer->mPastBuckets.begin();
+ EXPECT_EQ(bucket4StartTimeNs, iterator->second[0].mBucketStartNs);
+ EXPECT_EQ(bucket5StartTimeNs, iterator->second[0].mBucketEndNs);
EXPECT_EQ(9, iterator->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
iterator++;
+ EXPECT_EQ(bucketStartTimeNs, iterator->second[0].mBucketStartNs);
+ EXPECT_EQ(bucket2StartTimeNs, iterator->second[0].mBucketEndNs);
EXPECT_EQ(8, iterator->second[0].values[0].long_value);
EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
}
@@ -1924,33 +1961,32 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// Used by onConditionChanged.
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(100, curInterval.base.long_value);
+ ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
vector<shared_ptr<LogEvent>> allData;
valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(false, curInterval.hasBase);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
}
@@ -1959,38 +1995,38 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // Condition change to true.
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}))
.WillOnce(Return(false));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(100, curInterval.base.long_value);
+ ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
valueProducer->onConditionChanged(false, bucketStartTimeNs + 20);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
EXPECT_EQ(false, curInterval.hasValue);
- EXPECT_EQ(false, curInterval.hasBase);
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
}
@@ -1998,41 +2034,39 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(50);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50));
return false;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to false.
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
return true;
}));
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.
// valueProducer->mCondition = ConditionState::kTrue;
valueProducer->onConditionChanged(true, bucketStartTimeNs);
- EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(false, curInterval.hasBase);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
}
@@ -2043,25 +2077,21 @@
metric.set_max_pull_delay_sec(0);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write(tagId);
- event->write(120);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120));
return true;
}));
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);
- EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
}
TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
@@ -2075,84 +2105,77 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ 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;
// Event should be skipped since it is from previous bucket.
// Pull should not be called.
valueProducer.onConditionChanged(true, bucketStartTimeNs);
- EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
}
TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write(tagId);
- event->write(100);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
return true;
}));
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);
valueProducer->mHasGlobalBase = true;
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(100, curInterval.base.long_value);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(100, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
}
-TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) {
+/*
+ * Tests that a bucket is marked invalid when a condition change pull fails.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
.WillOnce(Return(false))
// Second onConditionChanged
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(130);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-
- valueProducer->mCondition = ConditionState::kTrue;
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kTrue);
// Bucket start.
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write(1);
- event->write(110);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
// This will fail and should invalidate the whole bucket since we do not have all the data
@@ -2162,94 +2185,137 @@
// Bucket end.
allData.clear();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event2->write(1);
- event2->write(140);
- event2->init();
- allData.push_back(event2);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
// Contains base from last pull which was successful.
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(140, curInterval.base.long_value);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(140, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 10, false /* include partial bucket */, true,
+ FAST /* 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(bucket2StartTimeNs),
+ 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::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis());
}
-TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) {
+/*
+ * Tests that a bucket is marked invalid when the guardrail is hit.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
metric.mutable_dimensions_in_what()->set_field(tagId);
metric.mutable_dimensions_in_what()->add_child()->set_field(1);
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _, _))
// First onConditionChanged
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
for (int i = 0; i < 2000; i++) {
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write(i);
- event->write(i);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
}
return true;
}));
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->mCurrentBucketIsInvalid);
- EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ EXPECT_EQ(true, valueProducer->mCurrentBucketIsSkipped);
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mSkippedBuckets.size());
+
+ // Bucket 2 start.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 10));
+ valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+ // First bucket added to mSkippedBuckets after flush.
+ ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size());
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */,
+ true, FAST /* 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(bucket2StartTimeNs),
+ 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::DIMENSION_GUARDRAIL_REACHED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis());
}
-TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) {
+/*
+ * Tests that a bucket is marked invalid when the bucket's initial pull fails.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(120);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
return true;
}))
// Second onConditionChanged
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(130);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-
- valueProducer->mCondition = ConditionState::kTrue;
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kTrue);
// Bucket start.
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write(1);
- event->write(110);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
@@ -2257,105 +2323,131 @@
// Bucket end.
allData.clear();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event2->write(1);
- event2->write(140);
- event2->init();
- allData.push_back(event2);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
// Contains base from last pull which was successful.
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(140, curInterval.base.long_value);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(140, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */,
+ true, FAST /* 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(bucket2StartTimeNs),
+ 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::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis());
}
-TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) {
+/*
+ * Tests that a bucket is marked invalid when the bucket's final pull fails
+ * (i.e. failed pull on bucket boundary).
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(120);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
return true;
}))
// Second onConditionChanged
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8);
- event->write(tagId);
- event->write(130);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
-
- valueProducer->mCondition = ConditionState::kTrue;
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kTrue);
// Bucket start.
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
- event->write(1);
- event->write(110);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
- // This will fail and should invalidate the whole bucket since we do not have all the data
- // needed to compute the metric value when the screen was on.
valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
// Bucket end.
allData.clear();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event2->write(1);
- event2->write(140);
- event2->init();
- allData.push_back(event2);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
- // Last pull failed so based has been reset.
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
+ // Last pull failed so base has been reset.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(false, curInterval.hasBase);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */,
+ true, FAST /* 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(bucket2StartTimeNs),
+ 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::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
}
TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
// Start bucket.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(3);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
}));
@@ -2365,62 +2457,59 @@
// Bucket 2 start.
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(110);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
// Bucket 3 empty.
allData.clear();
- shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
- event2->init();
- allData.push_back(event2);
+ allData.push_back(CreateNoValuesLogEvent(tagId, bucket3StartTimeNs + 1));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
// Data has been trimmed.
- EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
}
TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(3);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
// Empty pull.
valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(false, curInterval.hasBase);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(false, valueProducer->mHasGlobalBase);
}
@@ -2429,46 +2518,42 @@
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First onConditionChanged
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(1);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(2);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(5);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 11);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval& curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2476,16 +2561,18 @@
vector<shared_ptr<LogEvent>> allData;
allData.clear();
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
// Data is empty, base should be reset.
- EXPECT_EQ(false, curInterval.hasBase);
- EXPECT_EQ(5, curInterval.base.long_value);
+ EXPECT_EQ(false, curBaseInfo.hasBase);
+ EXPECT_EQ(5, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
- EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1});
+ ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
@@ -2495,165 +2582,149 @@
metric.set_condition(StringToId("SCREEN_ON"));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
// First onConditionChanged
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(1);
- event->write(1);
- event->init();
- data->push_back(event);
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
// End of bucket
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(2);
- event->write(2);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 2));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// Key 1 should be reset since in not present in the most pull.
- EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
auto iterator = valueProducer->mCurrentSlicedBucket.begin();
- EXPECT_EQ(true, iterator->second[0].hasBase);
- EXPECT_EQ(2, iterator->second[0].base.long_value);
+ auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
+ EXPECT_EQ(true, baseInfoIter->second[0].hasBase);
+ EXPECT_EQ(2, baseInfoIter->second[0].base.long_value);
EXPECT_EQ(false, iterator->second[0].hasValue);
iterator++;
- EXPECT_EQ(false, iterator->second[0].hasBase);
- EXPECT_EQ(1, iterator->second[0].base.long_value);
+ baseInfoIter++;
+ EXPECT_EQ(false, baseInfoIter->second[0].hasBase);
+ EXPECT_EQ(1, baseInfoIter->second[0].base.long_value);
EXPECT_EQ(false, iterator->second[0].hasValue);
EXPECT_EQ(true, valueProducer->mHasGlobalBase);
}
-TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
- metric.mutable_dimensions_in_what()->set_field(tagId);
- metric.mutable_dimensions_in_what()->add_child()->set_field(1);
- metric.set_condition(StringToId("SCREEN_ON"));
-
- sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
- // Second onConditionChanged.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
- data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(2);
- event->write(2);
- event->init();
- data->push_back(event);
- return true;
- }));
-
- sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
- valueProducer->mCondition = ConditionState::kUnknown;
-
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
- valueProducer->onConditionChanged(true, bucketStartTimeNs + 20);
-
- // End of bucket
- vector<shared_ptr<LogEvent>> allData;
- allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(4);
- event->write(4);
- event->init();
- allData.push_back(event);
- valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-
- // Bucket is incomplete so it is mark as invalid, however the base is fine since the last pull
- // succeeded.
- EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
-}
-
-TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
+TEST_P(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Initialization.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// notifyAppUpgrade.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](
+ int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(
- bucketStartTimeNs + bucketSizeNs / 2, 10));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
return true;
}));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
- valueProducer->notifyAppUpgrade(bucketStartTimeNs + bucketSizeNs / 2, "com.foo", 10000, 1);
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
+ EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
+ EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9},
+ {partialBucketSplitTimeNs - bucketStartTimeNs},
+ {bucketStartTimeNs}, {partialBucketSplitTimeNs});
ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size());
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 1, 4));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4));
+ // Pull fails and arrives late.
valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1);
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9},
+ {partialBucketSplitTimeNs - bucketStartTimeNs},
+ {bucketStartTimeNs}, {partialBucketSplitTimeNs});
+ ASSERT_EQ(1, valueProducer->mSkippedBuckets.size());
+ ASSERT_EQ(2, valueProducer->mSkippedBuckets[0].dropEvents.size());
+ EXPECT_EQ(PULL_FAILED, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
+ EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[0].dropEvents[1].reason);
+ EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
+ EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
}
TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Second onConditionChanged.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
- data->push_back(
- ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 10, 5));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
return true;
}))
// Third onConditionChanged.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10);
data->clear();
- data->push_back(
- ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 10, 7));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7));
return true;
}));
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());
// End of first bucket
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 1, 4));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 4));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(true, curInterval.hasBase);
- EXPECT_EQ(5, curInterval.base.long_value);
+ auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(true, curBaseInfo.hasBase);
+ EXPECT_EQ(5, curBaseInfo.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
// Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10},
+ {bucket2StartTimeNs}, {bucket3StartTimeNs});
}
TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
@@ -2665,26 +2736,28 @@
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
allData.clear();
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
// Initialization.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}));
@@ -2692,148 +2765,160 @@
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
allData.clear();
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
-TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
+TEST_P(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Initialization.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// notifyAppUpgrade.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([partialBucketSplitTimeNs](
+ int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
data->clear();
- data->push_back(
- ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 2, 10));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
return true;
}));
sp<ValueMetricProducer> valueProducer =
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
- valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+ switch (GetParam()) {
+ case APP_UPGRADE:
+ valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
+ break;
+ case BOOT_COMPLETE:
+ valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
+ break;
+ }
// Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First on condition changed.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// Second on condition changed.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .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(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 3));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
- valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+ auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(2, curInterval.value.long_value);
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 1, 10));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}, {bucketStartTimeNs},
+ {bucket2StartTimeNs});
}
+// TODO: b/145705635 fix or delete this test
TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// First condition change.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
return true;
}))
// 2nd condition change.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
return true;
}))
// 3rd condition change.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 3, 10));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 3, 10));
valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3);
allData.clear();
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8);
valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
allData.clear();
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 30));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 30));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// There was not global base available so all buckets are invalid.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
-}
-
-static StatsLogReport outputStreamToProto(ProtoOutputStream* proto) {
- vector<uint8_t> bytes;
- bytes.resize(proto->size());
- size_t pos = 0;
- sp<ProtoReader> reader = proto->data();
- while (reader->readBuffer() != NULL) {
- size_t toRead = reader->currentToRead();
- std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead);
- pos += toRead;
- reader->move(toRead);
- }
-
- StatsLogReport report;
- report.ParseFromArray(bytes.data(), bytes.size());
- return report;
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
}
TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -2843,40 +2928,35 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
// Initial pull.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(1);
- event->write(1);
- event->init();
- data->push_back(event);
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer.onDumpReport(bucketStartTimeNs + 10,
- true /* include recent buckets */, true,
+ valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
FAST, &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
// Bucket is invalid since we did not pull when dump report was called.
- EXPECT_EQ(0, report.value_metrics().data_size());
+ ASSERT_EQ(0, report.value_metrics().data_size());
}
TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -2886,51 +2966,41 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
// Initial pull.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(1);
- event->write(1);
- event->init();
- data->push_back(event);
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
vector<shared_ptr<LogEvent>> allData;
allData.clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
- event->write(tagId);
- event->write(2);
- event->write(2);
- event->init();
- allData.push_back(event);
+ allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, tagId, 2, 2));
valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer.onDumpReport(bucket4StartTimeNs,
- false /* include recent buckets */, true,
- FAST, &strSet, &output);
+ valueProducer.onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST,
+ &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
// Previous bucket is part of the report.
- EXPECT_EQ(1, report.value_metrics().data_size());
+ ASSERT_EQ(1, report.value_metrics().data_size());
EXPECT_EQ(0, report.value_metrics().data(0).bucket_info(0).bucket_num());
}
TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
- ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
UidMap uidMap;
SimpleAtomMatcher atomMatcher;
@@ -2940,46 +3010,40 @@
atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
- EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// Initial pull.
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
- event->write(tagId);
- event->write(1);
- event->write(1);
- event->init();
- data->push_back(event);
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
return true;
}))
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
- event->write(tagId);
- event->write(3);
- event->write(3);
- event->init();
- data->push_back(event);
+ data->push_back(
+ CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10, tagId, 3, 3));
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer.prepareFirstBucket();
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer.onDumpReport(bucketStartTimeNs + 10,
- true /* include recent buckets */, true,
+ valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
NO_TIME_CONSTRAINTS, &strSet, &output);
StatsLogReport report = outputStreamToProto(&output);
- EXPECT_EQ(1, report.value_metrics().data_size());
- EXPECT_EQ(1, report.value_metrics().data(0).bucket_info_size());
+ ASSERT_EQ(1, report.value_metrics().data_size());
+ ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
}
@@ -2992,11 +3056,12 @@
ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 10));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 10));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30);
// Bucket should have been completed.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
}
TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
@@ -3004,44 +3069,48 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(
- bucketStartTimeNs + 30, 10));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
return true;
}))
// condition becomes false
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(
- bucketStartTimeNs + 50, 20));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20));
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);
// has one slice
- EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(false, curInterval.hasBase);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(20, curInterval.value.long_value);
-
// Now the alarm is delivered. Condition is off though.
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(false, curInterval.hasBase);
+ curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
}
@@ -3050,29 +3119,31 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(
- bucketStartTimeNs + 30, 10));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
return true;
}));
sp<ValueMetricProducer> valueProducer =
- ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
- valueProducer->mCondition = ConditionState::kFalse;
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
// Now the alarm is delivered. Condition is off though.
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 30));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8},
+ {bucketStartTimeNs}, {bucket2StartTimeNs});
ValueMetricProducer::Interval curInterval =
valueProducer->mCurrentSlicedBucket.begin()->second[0];
- EXPECT_EQ(false, curInterval.hasBase);
+ ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+ EXPECT_EQ(false, curBaseInfo.hasBase);
EXPECT_EQ(false, curInterval.hasValue);
}
@@ -3082,16 +3153,16 @@
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;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 30));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// Condition was always false.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
}
TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
@@ -3099,29 +3170,1887 @@
metric.set_use_diff(false);
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(tagId, _))
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
// condition becomes true
- .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
data->clear();
- data->push_back(ValueMetricProducerTestHelper::createEvent(
- bucketStartTimeNs + 30, 10));
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
return true;
}))
.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);
// Now the alarm is delivered. Condition is off though.
vector<shared_ptr<LogEvent>> allData;
- allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 30));
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
// No buckets, we had a failure.
- assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
+ assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {});
+}
+
+/*
+ * Test that DUMP_REPORT_REQUESTED dump reason is logged.
+ *
+ * For the bucket to be marked invalid during a dump report requested,
+ * three things must be true:
+ * - we want to include the current partial bucket
+ * - we need a pull (metric is pulled and condition is true)
+ * - the dump latency must be FAST
+ */
+
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 20);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 40, true /* include recent buckets */, true,
+ FAST /* 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(bucketStartTimeNs + 40),
+ 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::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late condition
+ * change event (i.e. the condition change occurs in the wrong bucket).
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
+
+ // Bucket boundary pull.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ // Late condition change event.
+ valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent 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());
+
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ 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(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());
+}
+
+/*
+ * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late accumulate
+ * event (i.e. the accumulate events call occurs in the wrong bucket).
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) {
+ 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 + 50);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 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 + 100);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
+
+ // Bucket boundary pull.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ allData.clear();
+ allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 20));
+
+ // Late accumulateEvents event.
+ valueProducer->accumulateEvents(allData, bucket2StartTimeNs - 100, bucket2StartTimeNs - 100);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent 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());
+
+ EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
+ 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());
+
+ 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());
+}
+
+/*
+ * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
+ * when a metric is initialized.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
+ 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 + 50);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 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, bucketStartTimeNs + 10000);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(
+ pullerManager, metric, ConditionState::kUnknown);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent 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(dumpReportTimeNs),
+ 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(dumpReportTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that PULL_FAILED dump reason is logged due to a pull failure in
+ * #pullAndMatchEventsLocked.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
+ 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 + 50);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
+ return true;
+ }))
+ // Dump report requested, pull fails.
+ .WillOnce(Return(false));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent 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(dumpReportTimeNs),
+ 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::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that MULTIPLE_BUCKETS_SKIPPED dump reason is logged when a log event
+ * skips over more than one bucket.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) {
+ 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;
+ }))
+ // Dump report requested.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+
+ // Condition change event that skips forward by three buckets.
+ valueProducer->onConditionChanged(false, bucket4StartTimeNs + 10);
+
+ int64_t dumpTimeNs = bucket4StartTimeNs + 1000;
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(dumpTimeNs, true /* 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(2, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(bucket4StartTimeNs),
+ 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::MULTIPLE_BUCKETS_SKIPPED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucket4StartTimeNs + 10), dropEvent.drop_time_millis());
+
+ // This bucket is skipped because a dumpReport with include current buckets is called.
+ // This creates a new bucket from bucket4StartTimeNs to dumpTimeNs in which we have no data
+ // since the condition is false for the entire bucket interval.
+ EXPECT_EQ(NanoToMillis(bucket4StartTimeNs),
+ report.value_metrics().skipped(1).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(dumpTimeNs),
+ 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::NO_DATA, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(dumpTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
+ * is smaller than the "min_bucket_size_nanos" specified in the metric config.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+ metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
+
+ 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;
+ }))
+ // Dump report requested.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 9000000, 15));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+ ConditionState::kFalse);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent 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(dumpReportTimeNs),
+ 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::BUCKET_TOO_SMALL, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that NO_DATA dump reason is logged when a flushed bucket contains no data.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(
+ pullerManager, metric, ConditionState::kFalse);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 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(1, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
+ 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(dumpReportTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that all buckets are dropped due to condition unknown until the first onConditionChanged.
+ */
+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, ConditionState::kUnknown);
+
+ // 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 + 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(3, report.value_metrics().skipped_size());
+
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+ report.value_metrics().skipped(0).start_bucket_elapsed_millis());
+ 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::CONDITION_UNKNOWN, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), dropEvent.drop_time_millis());
+
+ EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
+ report.value_metrics().skipped(1).start_bucket_elapsed_millis());
+ 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());
+}
+
+/*
+ * Test multiple bucket drop events in the same bucket.
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
+ // Condition change to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(
+ pullerManager, metric, ConditionState::kUnknown);
+
+ // Condition change event.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
+ valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
+ FAST /* 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(dumpReportTimeNs),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ ASSERT_EQ(2, 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(bucketStartTimeNs + 10), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(1);
+ EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test that the number of logged bucket drop events is capped at the maximum.
+ * The maximum is currently 10 and is set in MetricProducer::maxDropEventsReached().
+ */
+TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ // First condition change event.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ for (int i = 0; i < 2000; i++) {
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
+ }
+ return true;
+ }))
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10));
+ return true;
+ }));
+
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithCondition(
+ pullerManager, metric, ConditionState::kUnknown);
+
+ // First condition change event causes guardrail to be reached.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
+
+ // 2-10 condition change events result in failed pulls.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 30);
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 70);
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 90);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 100);
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 150);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 170);
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 190);
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 200);
+
+ // Condition change event 11
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 220);
+
+ // Check dump report.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
+ // Because we already have 10 dump events in the current bucket,
+ // this case should not be added to the list of dump events.
+ valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true,
+ FAST /* 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(dumpReportTimeNs),
+ report.value_metrics().skipped(0).end_bucket_elapsed_millis());
+ ASSERT_EQ(10, 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(bucketStartTimeNs + 10), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(1);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 30), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(2);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 50), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(3);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 70), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(4);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 90), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(5);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 100), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(6);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 150), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(7);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 170), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(8);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 190), dropEvent.drop_time_millis());
+
+ dropEvent = report.value_metrics().skipped(0).drop_event(9);
+ EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
+ EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 200), dropEvent.drop_time_millis());
+}
+
+/*
+ * Test metric with a simple sliced state
+ * - Increasing values
+ * - Using diff
+ * - Second field is value field
+ */
+TEST(ValueMetricProducerTest, TestSlicedState) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Screen state change to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ return true;
+ }))
+ // Screen state change to OFF.
+ .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, 9));
+ return true;
+ }))
+ // Screen state change to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ 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, bucketStartTimeNs + 50);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ 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().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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(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(
+ bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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(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.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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_TRUE(it->second[0].hasValue);
+ EXPECT_EQ(4, it->second[0].value.long_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ it++;
+ 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.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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_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_TRUE(it->second[0].hasValue);
+ EXPECT_EQ(4, it->second[0].value.long_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ it++;
+ 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.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
+ NO_TIME_CONSTRAINTS, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(3, report.value_metrics().data_size());
+
+ 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());
+ EXPECT_EQ(13, report.value_metrics().data(1).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(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+
+ data = report.value_metrics().data(2);
+ ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
+ EXPECT_EQ(12, report.value_metrics().data(2).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(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+}
+
+/*
+ * Test metric with sliced state with map
+ * - Increasing values
+ * - Using diff
+ * - Second field is value field
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Screen state change to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ return true;
+ }))
+ // Screen state change to VR has no pull because it is in the same
+ // state group as ON.
+
+ // Screen state change to ON has no pull because it is in the same
+ // state group as VR.
+
+ // Screen state change to OFF.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ 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, bucketStartTimeNs + 50);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ return true;
+ }));
+
+ const StateMap& stateMap =
+ CreateScreenStateOnOffMap(/*screen on id=*/321, /*screen off id=*/123);
+ const StateMap_StateGroup screenOnGroup = stateMap.group(0);
+ const StateMap_StateGroup screenOffGroup = stateMap.group(1);
+
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ for (auto group : stateMap.group()) {
+ for (auto value : group.value()) {
+ stateGroupMap[SCREEN_STATE_ATOM_ID][value] = group.group_id();
+ }
+ }
+
+ 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().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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(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(
+ bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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(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.
+ // Both ON and VR are in the same state group, so the base should not change.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
+ android::view::DisplayStateEnum::DISPLAY_STATE_VR);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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(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.
+ // Both ON and VR are in the same state group, so the base should not change.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12,
+ android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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(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.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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_TRUE(it->second[0].hasValue);
+ EXPECT_EQ(16, it->second[0].value.long_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ it++;
+ 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.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
+ NO_TIME_CONSTRAINTS, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(3, report.value_metrics().data_size());
+
+ 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());
+ EXPECT_EQ(16, report.value_metrics().data(1).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_group_id());
+ EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id());
+
+ data = report.value_metrics().data(2);
+ ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
+ EXPECT_EQ(9, report.value_metrics().data(2).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_group_id());
+ EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id());
+}
+
+/*
+ * Test metric that slices by state with a primary field and has dimensions
+ * - Increasing values
+ * - Using diff
+ * - Second field is value field
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+
+ MetricStateLink* stateLink = metric.add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 3));
+ return true;
+ }))
+ // Uid 1 process state change from kStateUnknown -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
+ data->clear();
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 1 /*uid*/, 6));
+
+ // This event should be skipped.
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 2 /*uid*/, 8));
+ return true;
+ }))
+ // Uid 2 process state change from kStateUnknown -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40);
+ data->clear();
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 2 /*uid*/, 9));
+
+ // This event should be skipped.
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 1 /*uid*/, 12));
+ return true;
+ }))
+ // Uid 1 process state change from Foreground -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20);
+ data->clear();
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 1 /*uid*/, 13));
+
+ // This event should be skipped.
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 2 /*uid*/, 11));
+ return true;
+ }))
+ // Uid 1 process state change from Background -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40);
+ data->clear();
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 1 /*uid*/, 17));
+
+ // This event should be skipped.
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 2 /*uid */, 15));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50);
+ data->clear();
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 2 /*uid*/, 20));
+ data->push_back(
+ CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 1 /*uid*/, 21));
+ 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().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+ // Bucket status after metric initialized.
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {uid 1}.
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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}
+ 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++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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}
+ 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(
+ bucketStartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {uid 1}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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}.
+ 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++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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}
+ 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(
+ bucketStartTimeNs + 40, 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {uid 1}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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}.
+ 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++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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}
+ 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.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 10));
+ allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 15));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ // Buckets flushed after end of first bucket.
+ // None of the buckets should have a value.
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(4UL, valueProducer->mPastBuckets.size());
+ 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_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(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++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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(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(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++;
+ 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(
+ bucket2StartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(4UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension key {uid 2}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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(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++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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}
+ 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);
+ 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);
+ EXPECT_FALSE(it->second[0].hasValue);
+
+ // Bucket status after uid 1 process state change Background->Foreground.
+ uidProcessEvent = CreateUidProcessStateChangedEvent(
+ bucket2StartTimeNs + 40, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+ ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension key {uid 2}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ 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(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++;
+ 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}
+ 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++;
+ 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;
+ valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true,
+ NO_TIME_CONSTRAINTS, &strSet, &output);
+
+ StatsLogReport report = outputStreamToProto(&output);
+ EXPECT_TRUE(report.has_value_metrics());
+ ASSERT_EQ(5, report.value_metrics().data_size());
+
+ auto data = report.value_metrics().data(0);
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(4, report.value_metrics().data(0).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(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+
+ 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());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size());
+ EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
+ EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long());
+
+ 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());
+ EXPECT_TRUE(data.slice_by_state(0).has_value());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size());
+ EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long());
+ 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
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
index 7b9c0d6..108df04b 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
@@ -26,10 +26,23 @@
return dimension;
}
+HashableDimensionKey getMockedDimensionKeyLongValue(int tagId, int key, int64_t value) {
+ HashableDimensionKey dimension;
+ int pos[] = {key, 0, 0};
+ dimension.addValue(FieldValue(Field(tagId, pos, 0), Value(value)));
+
+ return dimension;
+}
+
MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) {
return MetricDimensionKey(getMockedDimensionKey(tagId, key, value), DEFAULT_DIMENSION_KEY);
}
+MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t value) {
+ return MetricDimensionKey(DEFAULT_DIMENSION_KEY,
+ getMockedDimensionKeyLongValue(tagId, key, value));
+}
+
void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher) {
matcher->set_field(tagId);
}
@@ -41,4 +54,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 97c1072..eeb38a4 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -26,33 +26,39 @@
class MockConditionWizard : public ConditionWizard {
public:
- MOCK_METHOD6(query,
+ MOCK_METHOD3(query,
ConditionState(const int conditionIndex, const ConditionKey& conditionParameters,
- const vector<Matcher>& dimensionFields,
- const bool isSubsetDim, const bool isPartialLink,
- std::unordered_set<HashableDimensionKey>* dimensionKeySet));
+ const bool isPartialLink));
};
class MockStatsPullerManager : public StatsPullerManager {
public:
- MOCK_METHOD4(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver,
- int64_t nextPulltimeNs, int64_t intervalNs));
- MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
- MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
-};
-
-class MockUidMap : public UidMap {
- public:
- MOCK_CONST_METHOD1(getHostUidOrSelf, int(int uid));
+ MOCK_METHOD5(RegisterReceiver,
+ void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver,
+ int64_t nextPulltimeNs, int64_t intervalNs));
+ MOCK_METHOD3(UnRegisterReceiver,
+ void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver));
+ MOCK_METHOD5(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids));
+ MOCK_METHOD5(Pull,
+ bool(const int pullCode, const vector<int32_t>& uids, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data, bool useUids));
+ MOCK_METHOD2(RegisterPullUidProvider,
+ void(const ConfigKey& configKey, wp<PullUidProvider> provider));
+ MOCK_METHOD2(UnregisterPullUidProvider,
+ void(const ConfigKey& configKey, wp<PullUidProvider> provider));
};
HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
+HashableDimensionKey getMockedDimensionKeyLongValue(int tagId, int key, int64_t value);
+MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t value);
+
// Utils to build FieldMatcher proto for simple one-depth atoms.
void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher);
void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher);
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index 73d1fd7..e384b6a 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -12,17 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <gtest/gtest.h>
+#include "src/shell/ShellSubscriber.h"
+#include <gtest/gtest.h>
+#include <stdio.h>
#include <unistd.h>
+
+#include <vector>
+
#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
#include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h"
-#include "src/shell/ShellSubscriber.h"
+#include "stats_event.h"
#include "tests/metrics/metrics_test_helper.h"
-
-#include <stdio.h>
-#include <vector>
+#include "tests/statsd_test_util.h"
using namespace android::os::statsd;
using android::sp;
@@ -34,27 +37,6 @@
#ifdef __ANDROID__
-class MyResultReceiver : public BnResultReceiver {
-public:
- Mutex mMutex;
- Condition mCondition;
- bool mHaveResult = false;
- int32_t mResult = 0;
-
- virtual void send(int32_t resultCode) {
- AutoMutex _l(mMutex);
- mResult = resultCode;
- mHaveResult = true;
- mCondition.signal();
- }
-
- int32_t waitForResult() {
- AutoMutex _l(mMutex);
- mCondition.waitRelative(mMutex, 1000000000);
- return mResult;
- }
-};
-
void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
sp<MockStatsPullerManager> pullerManager,
const vector<std::shared_ptr<LogEvent>>& pushedEvents,
@@ -67,10 +49,7 @@
ASSERT_EQ(0, pipe(fds_data));
size_t bufferSize = config.ByteSize();
-
// write the config to pipe, first write size of the config
- vector<uint8_t> size_buffer(sizeof(bufferSize));
- std::memcpy(size_buffer.data(), &bufferSize, sizeof(bufferSize));
write(fds_config[1], &bufferSize, sizeof(bufferSize));
// then write config itself
vector<uint8_t> buffer(bufferSize);
@@ -79,11 +58,10 @@
close(fds_config[1]);
sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
- sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
// mimic a binder thread that a shell subscriber runs on. it would block.
- std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
- shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver, -1);
+ std::thread reader([&shellClient, &fds_config, &fds_data] {
+ shellClient->startNewSubscription(fds_config[0], fds_data[1], /*timeoutSec=*/-1);
});
reader.detach();
@@ -108,26 +86,37 @@
// wait for the data to be written.
std::this_thread::sleep_for(100ms);
- int expected_data_size = expectedData.ByteSize();
+ // Because we might receive heartbeats from statsd, consisting of data sizes
+ // of 0, encapsulate reads within a while loop.
+ bool readAtom = false;
+ while (!readAtom) {
+ // Read the atom size.
+ size_t dataSize = 0;
+ read(fds_data[0], &dataSize, sizeof(dataSize));
+ if (dataSize == 0) continue;
+ EXPECT_EQ(expectedData.ByteSize(), int(dataSize));
- // now read from the pipe. firstly read the atom size.
- size_t dataSize = 0;
- EXPECT_EQ((int)sizeof(dataSize), read(fds_data[0], &dataSize, sizeof(dataSize)));
- EXPECT_EQ(expected_data_size, (int)dataSize);
+ // Read that much data in proto binary format.
+ vector<uint8_t> dataBuffer(dataSize);
+ EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
- // then read that much data which is the atom in proto binary format
- vector<uint8_t> dataBuffer(dataSize);
- EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
+ // Make sure the received bytes can be parsed to an atom.
+ ShellData receivedAtom;
+ EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
- // make sure the received bytes can be parsed to an atom
- ShellData receivedAtom;
- EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
+ // Serialize the expected atom to byte array and compare to make sure
+ // they are the same.
+ vector<uint8_t> expectedAtomBuffer(expectedData.ByteSize());
+ expectedData.SerializeToArray(expectedAtomBuffer.data(), expectedData.ByteSize());
+ EXPECT_EQ(expectedAtomBuffer, dataBuffer);
- // serialze the expected atom to bytes. and compare. to make sure they are the same.
- vector<uint8_t> atomBuffer(expected_data_size);
- expectedData.SerializeToArray(&atomBuffer[0], expected_data_size);
- EXPECT_EQ(atomBuffer, dataBuffer);
+ readAtom = true;
+ }
+
close(fds_data[0]);
+ if (reader.joinable()) {
+ reader.join();
+ }
}
TEST(ShellSubscriberTest, testPushedSubscription) {
@@ -136,11 +125,10 @@
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
vector<std::shared_ptr<LogEvent>> pushedList;
- std::shared_ptr<LogEvent> event1 =
- std::make_shared<LogEvent>(29 /*screen_state_atom_id*/, 1000 /*timestamp*/);
- event1->write(::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
- event1->init();
- pushedList.push_back(event1);
+ // Create the LogEvent from an AStatsEvent
+ std::unique_ptr<LogEvent> logEvent = CreateScreenStateChangedEvent(
+ 1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ pushedList.push_back(std::move(logEvent));
// create a simple config to get screen events
ShellSubscription config;
@@ -183,29 +171,33 @@
return config;
}
+shared_ptr<LogEvent> makeCpuActiveTimeAtom(int32_t uid, int64_t timeMillis) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, 10016);
+ AStatsEvent_overwriteTimestamp(statsEvent, 1111L);
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_writeInt64(statsEvent, timeMillis);
+
+ std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
} // namespace
TEST(ShellSubscriberTest, testPulledSubscription) {
sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- EXPECT_CALL(*pullerManager, Pull(10016, _))
- .WillRepeatedly(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ const vector<int32_t> uids = {AID_SYSTEM};
+ EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _, _))
+ .WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t,
+ vector<std::shared_ptr<LogEvent>>* data, bool) {
data->clear();
- shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, 1111L);
- event->write(kUid1);
- event->write(kCpuTime1);
- event->init();
- data->push_back(event);
- // another event
- event = make_shared<LogEvent>(tagId, 1111L);
- event->write(kUid2);
- event->write(kCpuTime2);
- event->init();
- data->push_back(event);
+ data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1));
+ data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2));
return true;
}));
-
runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(),
getExpectedShellData());
}
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
new file mode 100644
index 0000000..6516c15
--- /dev/null
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "state/StateTracker.h"
+
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+
+#include "state/StateListener.h"
+#include "state/StateManager.h"
+#include "state/StateTracker.h"
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const int32_t timestampNs = 1000;
+
+/**
+ * Mock StateListener class for testing.
+ * Stores primary key and state pairs.
+ */
+class TestStateListener : public virtual StateListener {
+public:
+ TestStateListener(){};
+
+ virtual ~TestStateListener(){};
+
+ struct Update {
+ Update(const HashableDimensionKey& key, int state) : mKey(key), mState(state){};
+ HashableDimensionKey mKey;
+ int mState;
+ };
+
+ std::vector<Update> updates;
+
+ void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
+ const HashableDimensionKey& primaryKey, const FieldValue& oldState,
+ const FieldValue& newState) {
+ updates.emplace_back(primaryKey, newState.mValue.int_value);
+ }
+};
+
+int getStateInt(StateManager& mgr, int atomId, const HashableDimensionKey& queryKey) {
+ FieldValue output;
+ mgr.getStateValue(atomId, queryKey, &output);
+ return output.mValue.int_value;
+}
+
+// START: build event functions.
+// Incorrect event - missing fields
+std::unique_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName,
+ int state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, 1000);
+
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_writeString(statsEvent, packageName.c_str());
+ // Missing field 3 - using_alert_window.
+ AStatsEvent_writeInt32(statsEvent, state);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+// Incorrect event - exclusive state has wrong type
+std::unique_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, 1000);
+
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_writeString(statsEvent, packageName.c_str());
+ AStatsEvent_writeInt32(statsEvent, true); // using_alert_window
+ AStatsEvent_writeString(statsEvent, "string"); // exclusive state: string instead of int
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+// END: build event functions.
+
+TEST(StateListenerTest, TestStateListenerWeakPointer) {
+ sp<TestStateListener> listener = new TestStateListener();
+ wp<TestStateListener> wListener = listener;
+ listener = nullptr; // let go of listener
+ EXPECT_TRUE(wListener.promote() == nullptr);
+}
+
+TEST(StateManagerTest, TestStateManagerGetInstance) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager& mgr = StateManager::getInstance();
+ mgr.clear();
+
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ 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
+ *
+ * - StateManager will create a new StateTracker if it doesn't already exist
+ * and then register the listener to the StateTracker.
+ * - If a listener is already registered to a StateTracker, it is not added again.
+ * - StateTrackers are only created for atoms that are state atoms.
+ */
+TEST(StateTrackerTest, TestRegisterListener) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ StateManager mgr;
+
+ // Register listener to non-existing StateTracker
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
+
+ // Register listener to existing StateTracker
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
+
+ // Register already registered listener to existing StateTracker
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(2, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
+
+ // Register listener to non-state atom
+ mgr.registerListener(util::BATTERY_LEVEL_CHANGED, listener2);
+ EXPECT_EQ(2, mgr.getStateTrackersCount());
+}
+
+/**
+ * Test unregistering listeners from StateTrackers
+ *
+ * - StateManager will unregister listeners from a StateTracker only if the
+ * StateTracker exists and the listener is registered to the StateTracker.
+ * - Once all listeners are removed from a StateTracker, the StateTracker
+ * is also removed.
+ */
+TEST(StateTrackerTest, TestUnregisterListener) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ StateManager mgr;
+
+ // Unregister listener from non-existing StateTracker
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
+
+ // Unregister non-registered listener from existing StateTracker
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
+ mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
+
+ // Unregister second-to-last listener from StateTracker
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener2);
+ mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
+
+ // Unregister last listener from StateTracker
+ mgr.unregisterListener(util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
+}
+
+/**
+ * Test a binary state atom with nested counting.
+ *
+ * To go from an "ON" state to an "OFF" state with nested counting, we must see
+ * an equal number of "OFF" events as "ON" events.
+ * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state.
+ * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state.
+ */
+TEST(StateTrackerTest, TestStateChangeNested) {
+ sp<TestStateListener> listener = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener);
+
+ std::vector<int> attributionUids1 = {1000};
+ std::vector<string> attributionTags1 = {"tag"};
+
+ std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(timestampNs, attributionUids1,
+ attributionTags1, "wakelockName");
+ mgr.onLogEvent(*event1);
+ ASSERT_EQ(1, listener->updates.size());
+ EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1, listener->updates[0].mState);
+ listener->updates.clear();
+
+ std::unique_ptr<LogEvent> event2 = CreateAcquireWakelockEvent(
+ timestampNs + 1000, attributionUids1, attributionTags1, "wakelockName");
+ mgr.onLogEvent(*event2);
+ ASSERT_EQ(0, listener->updates.size());
+
+ std::unique_ptr<LogEvent> event3 = CreateReleaseWakelockEvent(
+ timestampNs + 2000, attributionUids1, attributionTags1, "wakelockName");
+ mgr.onLogEvent(*event3);
+ ASSERT_EQ(0, listener->updates.size());
+
+ std::unique_ptr<LogEvent> event4 = CreateReleaseWakelockEvent(
+ timestampNs + 3000, attributionUids1, attributionTags1, "wakelockName");
+ mgr.onLogEvent(*event4);
+ ASSERT_EQ(1, listener->updates.size());
+ EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(0, listener->updates[0].mState);
+}
+
+/**
+ * Test a state atom with a reset state.
+ *
+ * If the reset state value is seen, every state in the map is set to the default
+ * state and every listener is notified.
+ */
+TEST(StateTrackerTest, TestStateChangeReset) {
+ sp<TestStateListener> listener = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener);
+
+ std::vector<int> attributionUids1 = {1000};
+ std::vector<string> attributionTags1 = {"tag1"};
+ std::vector<int> attributionUids2 = {2000};
+
+ std::unique_ptr<LogEvent> event1 =
+ CreateBleScanStateChangedEvent(timestampNs, attributionUids1, attributionTags1,
+ BleScanStateChanged::ON, false, false, false);
+ mgr.onLogEvent(*event1);
+ ASSERT_EQ(1, listener->updates.size());
+ EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+ FieldValue stateFieldValue;
+ mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue);
+ EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value);
+ listener->updates.clear();
+
+ std::unique_ptr<LogEvent> event2 =
+ CreateBleScanStateChangedEvent(timestampNs + 1000, attributionUids2, attributionTags1,
+ BleScanStateChanged::ON, false, false, false);
+ mgr.onLogEvent(*event2);
+ ASSERT_EQ(1, listener->updates.size());
+ EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+ mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, listener->updates[0].mKey, &stateFieldValue);
+ EXPECT_EQ(BleScanStateChanged::ON, stateFieldValue.mValue.int_value);
+ listener->updates.clear();
+
+ std::unique_ptr<LogEvent> event3 =
+ CreateBleScanStateChangedEvent(timestampNs + 2000, attributionUids2, attributionTags1,
+ BleScanStateChanged::RESET, false, false, false);
+ mgr.onLogEvent(*event3);
+ ASSERT_EQ(2, listener->updates.size());
+ for (const TestStateListener::Update& update : listener->updates) {
+ EXPECT_EQ(BleScanStateChanged::OFF, update.mState);
+
+ mgr.getStateValue(util::BLE_SCAN_STATE_CHANGED, update.mKey, &stateFieldValue);
+ EXPECT_EQ(BleScanStateChanged::OFF, stateFieldValue.mValue.int_value);
+ }
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states without primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
+
+ // log event
+ std::unique_ptr<LogEvent> event = CreateScreenStateChangedEvent(
+ timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ ASSERT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
+ EXPECT_EQ(2, listener1->updates[0].mState);
+
+ // 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));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with one primary key.
+ */
+TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1);
+
+ // log event
+ std::unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent(
+ timestampNs, 1000 /*uid*/, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ ASSERT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1002, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey;
+ getUidProcessKey(1000 /* uid */, &queryKey);
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+ getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey));
+}
+
+TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1);
+
+ // Log event.
+ std::vector<int> attributionUids = {1001};
+ std::vector<string> attributionTags = {"tag1"};
+
+ std::unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timestampNs, attributionUids,
+ attributionTags, "wakelockName");
+ mgr.onLogEvent(*event);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED));
+
+ // Check listener was updated.
+ ASSERT_EQ(1, listener1->updates.size());
+ ASSERT_EQ(3, listener1->updates[0].mKey.getValues().size());
+ EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
+ EXPECT_EQ("wakelockName", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
+ EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState);
+
+ // Check StateTracker was updated by querying for state.
+ HashableDimensionKey queryKey;
+ getPartialWakelockKey(1001 /* uid */, "wakelockName", &queryKey);
+ EXPECT_EQ(WakelockStateChanged::ACQUIRE,
+ getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey));
+
+ // No state stored for this query key.
+ HashableDimensionKey queryKey2;
+ getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
+ EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
+ getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2));
+
+ // Partial query fails.
+ HashableDimensionKey queryKey3;
+ getPartialWakelockKey(1001 /* uid */, &queryKey3);
+ EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/,
+ getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with multiple primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
+
+ // log event
+ std::unique_ptr<LogEvent> event = CreateOverlayStateChangedEvent(
+ timestampNs, 1000 /* uid */, "package1", true /*using_alert_window*/,
+ OverlayStateChanged::ENTERED);
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ ASSERT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey;
+ getOverlayKey(1000 /* uid */, "package1", &queryKey);
+ EXPECT_EQ(OverlayStateChanged::ENTERED,
+ getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged
+ * when there is an error extracting state from log event. Listener is not
+ * updated of state change.
+ */
+TEST(StateTrackerTest, TestStateChangeEventError) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event1 =
+ buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
+ std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
+
+ // check listener was updated
+ mgr.onLogEvent(*event1);
+ ASSERT_EQ(0, listener1->updates.size());
+ mgr.onLogEvent(*event2);
+ ASSERT_EQ(0, listener1->updates.size());
+}
+
+TEST(StateTrackerTest, TestStateQuery) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ sp<TestStateListener> listener3 = new TestStateListener();
+ sp<TestStateListener> listener4 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
+ mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2);
+ mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3);
+ mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4);
+
+ std::unique_ptr<LogEvent> event1 = CreateUidProcessStateChangedEvent(
+ timestampNs, 1000 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ std::unique_ptr<LogEvent> event2 = CreateUidProcessStateChangedEvent(
+ timestampNs + 1000, 1001 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value:
+ // 1003
+ std::unique_ptr<LogEvent> event3 = CreateUidProcessStateChangedEvent(
+ timestampNs + 2000, 1002 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000
+ std::unique_ptr<LogEvent> event4 = CreateUidProcessStateChangedEvent(
+ timestampNs + 3000, 1001 /*uid*/,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ std::unique_ptr<LogEvent> event5 = CreateScreenStateChangedEvent(
+ timestampNs + 4000, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ std::unique_ptr<LogEvent> event6 = CreateOverlayStateChangedEvent(
+ timestampNs + 5000, 1000 /*uid*/, "package1", true /*using_alert_window*/,
+ OverlayStateChanged::ENTERED);
+ std::unique_ptr<LogEvent> event7 = CreateOverlayStateChangedEvent(
+ timestampNs + 6000, 1000 /*uid*/, "package2", true /*using_alert_window*/,
+ OverlayStateChanged::EXITED);
+
+ std::vector<int> attributionUids = {1005};
+ std::vector<string> attributionTags = {"tag"};
+
+ std::unique_ptr<LogEvent> event8 = CreateAcquireWakelockEvent(
+ timestampNs + 7000, attributionUids, attributionTags, "wakelock1");
+ std::unique_ptr<LogEvent> event9 = CreateReleaseWakelockEvent(
+ timestampNs + 8000, attributionUids, attributionTags, "wakelock2");
+
+ mgr.onLogEvent(*event1);
+ mgr.onLogEvent(*event2);
+ mgr.onLogEvent(*event3);
+ mgr.onLogEvent(*event5);
+ mgr.onLogEvent(*event5);
+ mgr.onLogEvent(*event6);
+ mgr.onLogEvent(*event7);
+ mgr.onLogEvent(*event8);
+ mgr.onLogEvent(*event9);
+
+ // Query for UidProcessState of uid 1001
+ HashableDimensionKey queryKey1;
+ getUidProcessKey(1001, &queryKey1);
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
+ getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+ // Query for UidProcessState of uid 1004 - not in state map
+ HashableDimensionKey queryKey2;
+ getUidProcessKey(1004, &queryKey2);
+ EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED,
+ queryKey2)); // default state
+
+ // Query for UidProcessState of uid 1001 - after change in state
+ mgr.onLogEvent(*event4);
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+ getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+ // Query for ScreenState
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+
+ // Query for OverlayState of uid 1000, package name "package2"
+ HashableDimensionKey queryKey3;
+ getOverlayKey(1000, "package2", &queryKey3);
+ EXPECT_EQ(OverlayStateChanged::EXITED,
+ getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3));
+
+ // Query for WakelockState of uid 1005, tag 2
+ HashableDimensionKey queryKey4;
+ getPartialWakelockKey(1005, "wakelock2", &queryKey4);
+ EXPECT_EQ(WakelockStateChanged::RELEASE,
+ getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4));
+
+ // Query for WakelockState of uid 1005, tag 1
+ HashableDimensionKey queryKey5;
+ getPartialWakelockKey(1005, "wakelock1", &queryKey5);
+ EXPECT_EQ(WakelockStateChanged::ACQUIRE,
+ getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5));
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 2c4f3c7..cee8372 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -14,10 +14,33 @@
#include "statsd_test_util.h"
+#include <aidl/android/util/StatsEventParcel.h>
+#include "stats_event.h"
+
+using aidl::android::util::StatsEventParcel;
+using std::shared_ptr;
+
namespace android {
namespace os {
namespace statsd {
+StatsLogReport outputStreamToProto(ProtoOutputStream* proto) {
+ vector<uint8_t> bytes;
+ bytes.resize(proto->size());
+ size_t pos = 0;
+ sp<ProtoReader> reader = proto->data();
+
+ while (reader->readBuffer() != NULL) {
+ size_t toRead = reader->currentToRead();
+ std::memcpy(&((bytes)[pos]), reader->readBuffer(), toRead);
+ pos += toRead;
+ reader->move(toRead);
+ }
+
+ StatsLogReport report;
+ report.ParseFromArray(bytes.data(), bytes.size());
+ return report;
+}
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
AtomMatcher atom_matcher;
@@ -28,7 +51,7 @@
}
AtomMatcher CreateTemperatureAtomMatcher() {
- return CreateSimpleAtomMatcher("TemperatureMatcher", android::util::TEMPERATURE);
+ return CreateSimpleAtomMatcher("TemperatureMatcher", util::TEMPERATURE);
}
AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
@@ -36,7 +59,7 @@
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED);
+ simple_atom_matcher->set_atom_id(util::SCHEDULED_JOB_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(3); // State field.
field_value_matcher->set_eq_int(state);
@@ -57,7 +80,7 @@
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
+ simple_atom_matcher->set_atom_id(util::SCREEN_BRIGHTNESS_CHANGED);
return atom_matcher;
}
@@ -65,7 +88,7 @@
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId("UidProcessStateChanged"));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
+ simple_atom_matcher->set_atom_id(util::UID_PROCESS_STATE_CHANGED);
return atom_matcher;
}
@@ -74,7 +97,7 @@
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED);
+ simple_atom_matcher->set_atom_id(util::WAKELOCK_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(4); // State field.
field_value_matcher->set_eq_int(state);
@@ -94,7 +117,7 @@
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::BATTERY_SAVER_MODE_STATE_CHANGED);
+ simple_atom_matcher->set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(1); // State field.
field_value_matcher->set_eq_int(state);
@@ -112,19 +135,39 @@
"BatterySaverModeStop", BatterySaverModeStateChanged::OFF);
}
-
-AtomMatcher CreateScreenStateChangedAtomMatcher(
- const string& name, android::view::DisplayStateEnum state) {
+AtomMatcher CreateBatteryStateChangedAtomMatcher(const string& name,
+ BatteryPluggedStateEnum state) {
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED);
+ simple_atom_matcher->set_atom_id(util::PLUGGED_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(1); // State field.
field_value_matcher->set_eq_int(state);
return atom_matcher;
}
+AtomMatcher CreateBatteryStateNoneMatcher() {
+ return CreateBatteryStateChangedAtomMatcher("BatteryPluggedNone",
+ BatteryPluggedStateEnum::BATTERY_PLUGGED_NONE);
+}
+
+AtomMatcher CreateBatteryStateUsbMatcher() {
+ return CreateBatteryStateChangedAtomMatcher("BatteryPluggedUsb",
+ BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
+}
+
+AtomMatcher CreateScreenStateChangedAtomMatcher(
+ const string& name, android::view::DisplayStateEnum state) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_id(StringToId(name));
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(util::SCREEN_STATE_CHANGED);
+ auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+ field_value_matcher->set_field(1); // State field.
+ field_value_matcher->set_eq_int(state);
+ return atom_matcher;
+}
AtomMatcher CreateScreenTurnedOnAtomMatcher() {
return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
@@ -141,7 +184,7 @@
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED);
+ simple_atom_matcher->set_atom_id(util::SYNC_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(3); // State field.
field_value_matcher->set_eq_int(state);
@@ -161,7 +204,7 @@
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ simple_atom_matcher->set_atom_id(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(4); // Activity field.
field_value_matcher->set_eq_int(state);
@@ -183,7 +226,7 @@
AtomMatcher atom_matcher;
atom_matcher.set_id(StringToId(name));
auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
- simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ simple_atom_matcher->set_atom_id(util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
field_value_matcher->set_field(3); // Process state field.
field_value_matcher->set_eq_int(state);
@@ -211,6 +254,14 @@
return predicate;
}
+Predicate CreateDeviceUnpluggedPredicate() {
+ Predicate predicate;
+ predicate.set_id(StringToId("DeviceUnplugged"));
+ predicate.mutable_simple_predicate()->set_start(StringToId("BatteryPluggedNone"));
+ predicate.mutable_simple_predicate()->set_stop(StringToId("BatteryPluggedUsb"));
+ return predicate;
+}
+
Predicate CreateScreenIsOnPredicate() {
Predicate predicate;
predicate.set_id(StringToId("ScreenIsOn"));
@@ -251,6 +302,95 @@
return predicate;
}
+State CreateScreenState() {
+ State state;
+ state.set_id(StringToId("ScreenState"));
+ state.set_atom_id(util::SCREEN_STATE_CHANGED);
+ return state;
+}
+
+State CreateUidProcessState() {
+ State state;
+ state.set_id(StringToId("UidProcessState"));
+ state.set_atom_id(util::UID_PROCESS_STATE_CHANGED);
+ return state;
+}
+
+State CreateOverlayState() {
+ State state;
+ state.set_id(StringToId("OverlayState"));
+ state.set_atom_id(util::OVERLAY_STATE_CHANGED);
+ return state;
+}
+
+State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId) {
+ State state;
+ state.set_id(StringToId("ScreenStateOnOff"));
+ state.set_atom_id(util::SCREEN_STATE_CHANGED);
+
+ auto map = CreateScreenStateOnOffMap(screenOnId, screenOffId);
+ *state.mutable_map() = map;
+
+ return state;
+}
+
+State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) {
+ State state;
+ state.set_id(StringToId("ScreenStateSimpleOnOff"));
+ state.set_atom_id(util::SCREEN_STATE_CHANGED);
+
+ auto map = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId);
+ *state.mutable_map() = map;
+
+ return state;
+}
+
+StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId) {
+ StateMap_StateGroup group;
+ group.set_group_id(screenOnId);
+ group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_VR);
+ group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND);
+ return group;
+}
+
+StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId) {
+ StateMap_StateGroup group;
+ group.set_group_id(screenOffId);
+ group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE);
+ group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND);
+ return group;
+}
+
+StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId) {
+ StateMap_StateGroup group;
+ group.set_group_id(screenOnId);
+ group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ return group;
+}
+
+StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId) {
+ StateMap_StateGroup group;
+ group.set_group_id(screenOffId);
+ group.add_value(android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ return group;
+}
+
+StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId) {
+ StateMap map;
+ *map.add_group() = CreateScreenStateOnGroup(screenOnId);
+ *map.add_group() = CreateScreenStateOffGroup(screenOffId);
+ return map;
+}
+
+StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId) {
+ StateMap map;
+ *map.add_group() = CreateScreenStateSimpleOnGroup(screenOnId);
+ *map.add_group() = CreateScreenStateSimpleOffGroup(screenOffId);
+ return map;
+}
+
void addPredicateToPredicateCombination(const Predicate& predicate,
Predicate* combinationPredicate) {
combinationPredicate->mutable_combination()->add_predicate(predicate.id());
@@ -292,173 +432,551 @@
return dimensions;
}
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
- const android::view::DisplayStateEnum state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs);
- EXPECT_TRUE(event->write(state));
- event->init();
- return event;
+FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId,
+ const std::vector<Position>& positions,
+ const std::vector<int>& fields) {
+ FieldMatcher dimensions = CreateAttributionUidDimensions(atomId, positions);
+
+ for (const int field : fields) {
+ dimensions.add_child()->set_field(field);
+ }
+ return dimensions;
}
-std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(
- android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
- EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON));
- event->init();
- return event;
+// START: get primary key functions
+void getUidProcessKey(int uid, HashableDimensionKey* key) {
+ int pos1[] = {1, 0, 0};
+ Field field1(27 /* atom id */, pos1, 0 /* depth */);
+ Value value1((int32_t)uid);
+
+ key->addValue(FieldValue(field1, value1));
}
-std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(
- android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
- EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF));
- event->init();
- return event;
+void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) {
+ int pos1[] = {1, 0, 0};
+ int pos2[] = {2, 0, 0};
+
+ Field field1(59 /* atom id */, pos1, 0 /* depth */);
+ Field field2(59 /* atom id */, pos2, 0 /* depth */);
+
+ Value value1((int32_t)uid);
+ Value value2(packageName);
+
+ key->addValue(FieldValue(field1, value1));
+ key->addValue(FieldValue(field2, value2));
}
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
- int level, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
- EXPECT_TRUE(event->write(level));
- event->init();
- return event;
+void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key) {
+ int pos1[] = {1, 1, 1};
+ int pos3[] = {2, 0, 0};
+ int pos4[] = {3, 0, 0};
+ Field field1(10 /* atom id */, pos1, 2 /* depth */);
+
+ Field field3(10 /* atom id */, pos3, 0 /* depth */);
+ Field field4(10 /* atom id */, pos4, 0 /* depth */);
+
+ Value value1((int32_t)uid);
+ Value value3((int32_t)1 /*partial*/);
+ Value value4(tag);
+
+ key->addValue(FieldValue(field1, value1));
+ key->addValue(FieldValue(field3, value3));
+ key->addValue(FieldValue(field4, value4));
}
-std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& jobName,
- const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs);
- event->write(attributions);
- event->write(jobName);
- event->write(state);
- event->init();
- return event;
+void getPartialWakelockKey(int uid, HashableDimensionKey* key) {
+ int pos1[] = {1, 1, 1};
+ int pos3[] = {2, 0, 0};
+
+ Field field1(10 /* atom id */, pos1, 2 /* depth */);
+ Field field3(10 /* atom id */, pos3, 0 /* depth */);
+
+ Value value1((int32_t)uid);
+ Value value3((int32_t)1 /*partial*/);
+
+ key->addValue(FieldValue(field1, value1));
+ key->addValue(FieldValue(field3, value3));
+}
+// END: get primary key functions
+
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags) {
+ vector<const char*> cTags(attributionTags.size());
+ for (int i = 0; i < cTags.size(); i++) {
+ cTags[i] = attributionTags[i].c_str();
+ }
+
+ AStatsEvent_writeAttributionChain(statsEvent,
+ reinterpret_cast<const uint32_t*>(attributionUids.data()),
+ cTags.data(), attributionUids.size());
}
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
- const std::vector<AttributionNodeInternal>& attributions,
- const string& name, uint64_t timestampNs) {
- return CreateScheduledJobStateChangedEvent(
- attributions, name, ScheduledJobStateChanged::STARTED, timestampNs);
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
+ AStatsEvent_build(statsEvent);
+
+ size_t size;
+ uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+ logEvent->parseBuffer(buf, size);
+
+ AStatsEvent_release(statsEvent);
}
-// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
- const std::vector<AttributionNodeInternal>& attributions,
- const string& name, uint64_t timestampNs) {
- return CreateScheduledJobStateChangedEvent(
- attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs);
+void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- const WakelockStateChanged::State state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs);
- event->write(attributions);
- event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
- event->write(wakelockName);
- event->write(state);
- event->init();
- return event;
-}
-
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- uint64_t timestampNs) {
- return CreateWakelockStateChangedEvent(
- attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- uint64_t timestampNs) {
- return CreateWakelockStateChangedEvent(
- attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
- const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(
- android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
- event->write(uid);
- event->write("pkg_name");
- event->write("class_name");
- event->write(state);
- event->init();
- return event;
-}
-
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
- return CreateActivityForegroundStateChangedEvent(
- uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
- return CreateActivityForegroundStateChangedEvent(
- uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- const SyncStateChanged::State state, uint64_t timestampNs) {
- auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
- event->write(attributions);
- event->write(name);
- event->write(state);
- event->init();
- return event;
-}
-
-std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- uint64_t timestampNs) {
- return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- uint64_t timestampNs) {
- return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
- const int uid, const ProcessLifeCycleStateChanged::State state, uint64_t timestampNs) {
- auto logEvent = std::make_unique<LogEvent>(
- android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs);
- logEvent->write(uid);
- logEvent->write("");
- logEvent->write(state);
- logEvent->init();
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2) {
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ CreateTwoValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2);
return logEvent;
}
-std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) {
- return CreateProcessLifeCycleStateChangedEvent(
- uid, ProcessLifeCycleStateChanged::CRASHED, timestampNs);
+void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value1);
+ AStatsEvent_writeInt32(statsEvent, value2);
+ AStatsEvent_writeInt32(statsEvent, value3);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
}
-std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
- int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs) {
- auto logEvent = std::make_unique<LogEvent>(
- android::util::ISOLATED_UID_CHANGED, timestampNs);
- logEvent->write(hostUid);
- logEvent->write(isolatedUid);
- logEvent->write(is_create);
- logEvent->init();
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3) {
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ CreateThreeValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2, value3);
+ return logEvent;
+}
+
+void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
+ int32_t value) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, value);
+ AStatsEvent_writeInt32(statsEvent, value);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) {
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ CreateRepeatedValueLogEvent(logEvent.get(), atomId, eventTimeNs, value);
+ return logEvent;
+}
+
+void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ parseStatsEventToLogEvent(statsEvent, logEvent);
+}
+
+shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) {
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ CreateNoValuesLogEvent(logEvent.get(), atomId, eventTimeNs);
+ return logEvent;
+}
+
+shared_ptr<LogEvent> makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1,
+ int data2) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_writeInt32(statsEvent, data1);
+ AStatsEvent_writeInt32(statsEvent, data2);
+
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+shared_ptr<LogEvent> makeAttributionLogEvent(int atomId, int64_t eventTimeNs,
+ const vector<int>& uids, const vector<string>& tags,
+ int data1, int data2) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, atomId);
+ AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+ writeAttribution(statsEvent, uids, tags);
+ AStatsEvent_writeInt32(statsEvent, data1);
+ AStatsEvent_writeInt32(statsEvent, data2);
+
+ shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+sp<MockUidMap> makeMockUidMapForOneHost(int hostUid, const vector<int>& isolatedUids) {
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(ReturnArg<0>());
+ for (const int isolatedUid : isolatedUids) {
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid)).WillRepeatedly(Return(hostUid));
+ }
+
+ return uidMap;
+}
+
+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);
+ AStatsEvent_writeInt32(statsEvent, state);
+ 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>(loggerUid, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ 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());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ 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());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ AStatsEvent_writeInt32(statsEvent, state);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::SCREEN_BRIGHTNESS_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+ AStatsEvent_writeInt32(statsEvent, level);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
+ const vector<int>& attributionUids, const vector<string>& attributionTags,
+ const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_writeString(statsEvent, jobName.c_str());
+ AStatsEvent_writeInt32(statsEvent, state);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& jobName) {
+ return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+ ScheduledJobStateChanged::STARTED, timestampNs);
+}
+
+// Create log event when scheduled job finishes.
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& jobName) {
+ return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+ ScheduledJobStateChanged::FINISHED, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& wakelockName,
+ const WakelockStateChanged::State state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::WAKELOCK_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
+ AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeString(statsEvent, wakelockName.c_str());
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeInt32(statsEvent, state);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& wakelockName) {
+ return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags,
+ wakelockName, WakelockStateChanged::ACQUIRE);
+}
+
+std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& wakelockName) {
+ return CreateWakelockStateChangedEvent(timestampNs, attributionUids, attributionTags,
+ wakelockName, WakelockStateChanged::RELEASE);
+}
+
+std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
+ uint64_t timestampNs, const int uid, const ActivityForegroundStateChanged::State state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_writeString(statsEvent, "pkg_name");
+ AStatsEvent_writeString(statsEvent, "class_name");
+ AStatsEvent_writeInt32(statsEvent, state);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid) {
+ return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
+ ActivityForegroundStateChanged::BACKGROUND);
+}
+
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid) {
+ return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
+ ActivityForegroundStateChanged::FOREGROUND);
+}
+
+std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& name,
+ const SyncStateChanged::State state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_writeString(statsEvent, name.c_str());
+ AStatsEvent_writeInt32(statsEvent, state);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& name) {
+ return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
+ SyncStateChanged::ON);
+}
+
+std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& name) {
+ return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
+ SyncStateChanged::OFF);
+}
+
+std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
+ uint64_t timestampNs, const int uid, const ProcessLifeCycleStateChanged::State state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_writeString(statsEvent, "");
+ AStatsEvent_writeInt32(statsEvent, state);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid) {
+ return CreateProcessLifeCycleStateChangedEvent(timestampNs, uid,
+ ProcessLifeCycleStateChanged::CRASHED);
+}
+
+std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::APP_CRASH_OCCURRED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_writeString(statsEvent, "eventType");
+ AStatsEvent_writeString(statsEvent, "processName");
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid,
+ int isolatedUid, bool is_create) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::ISOLATED_UID_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ AStatsEvent_writeInt32(statsEvent, hostUid);
+ AStatsEvent_writeInt32(statsEvent, isolatedUid);
+ AStatsEvent_writeInt32(statsEvent, is_create);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
+ uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::UID_PROCESS_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeInt32(statsEvent, state);
+ 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());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const BleScanStateChanged::State state,
+ const bool filtered, const bool firstMatch,
+ const bool opportunistic) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ writeAttribution(statsEvent, attributionUids, attributionTags);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, true);
+ AStatsEvent_writeInt32(statsEvent, state);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_EXCLUSIVE_STATE, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_STATE_NESTED, true);
+ if (state == util::BLE_SCAN_STATE_CHANGED__STATE__RESET) {
+ AStatsEvent_addInt32Annotation(statsEvent, ANNOTATION_ID_TRIGGER_STATE_RESET,
+ util::BLE_SCAN_STATE_CHANGED__STATE__OFF);
+ }
+ AStatsEvent_writeBool(statsEvent, filtered); // filtered
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeBool(statsEvent, firstMatch); // first match
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeBool(statsEvent, opportunistic); // opportunistic
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+
+ std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+ parseStatsEventToLogEvent(statsEvent, logEvent.get());
+ return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid,
+ const string& packageName,
+ const bool usingAlertWindow,
+ const OverlayStateChanged::State state) {
+ AStatsEvent* statsEvent = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
+ AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+ AStatsEvent_writeInt32(statsEvent, uid);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_IS_UID, true);
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeString(statsEvent, packageName.c_str());
+ AStatsEvent_addBoolAnnotation(statsEvent, util::ANNOTATION_ID_PRIMARY_FIELD, true);
+ AStatsEvent_writeBool(statsEvent, usingAlertWindow);
+ AStatsEvent_writeInt32(statsEvent, state);
+ 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());
return logEvent;
}
sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
- const StatsdConfig& config, const ConfigKey& key) {
- sp<UidMap> uidMap = new UidMap();
+ const StatsdConfig& config, const ConfigKey& key,
+ const shared_ptr<IPullAtomCallback>& puller,
+ const int32_t atomTag, const sp<UidMap> uidMap) {
sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ if (puller != nullptr) {
+ pullerManager->RegisterPullAtomCallback(/*uid=*/0, atomTag, NS_PER_SEC, NS_PER_SEC * 10, {},
+ puller);
+ }
sp<AlarmMonitor> anomalyAlarmMonitor =
- new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){});
+ new AlarmMonitor(1,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+ [](const shared_ptr<IStatsCompanionService>&){});
sp<AlarmMonitor> periodicAlarmMonitor =
- new AlarmMonitor(1, [](const sp<IStatsCompanionService>&, int64_t){},
- [](const sp<IStatsCompanionService>&){});
+ new AlarmMonitor(1,
+ [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+ [](const shared_ptr<IStatsCompanionService>&){});
sp<StatsLogProcessor> processor =
new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
timeBaseNs, [](const ConfigKey&) { return true; },
@@ -467,13 +985,6 @@
return processor;
}
-AttributionNodeInternal CreateAttribution(const int& uid, const string& tag) {
- AttributionNodeInternal attribution;
- attribution.set_uid(uid);
- attribution.set_tag(tag);
- return attribution;
-}
-
void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
std::sort(events->begin(), events->end(),
[](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
@@ -485,22 +996,25 @@
return static_cast<int64_t>(std::hash<std::string>()(str));
}
-void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) {
+void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
+ const int uid, const string& tag) {
EXPECT_EQ(value.field(), atomId);
+ ASSERT_EQ(value.value_tuple().dimensions_value_size(), 2);
// Attribution field.
EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
- // Uid only.
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value_size(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).field(), 1);
- EXPECT_EQ(value.value_tuple().dimensions_value(0)
- .value_tuple().dimensions_value(0).value_int(), uid);
+ // Uid field.
+ ASSERT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(value.value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
+ uid);
+ // Tag field.
+ EXPECT_EQ(value.value_tuple().dimensions_value(1).field(), 3);
+ EXPECT_EQ(value.value_tuple().dimensions_value(1).value_str(), tag);
}
-void ValidateUidDimension(const DimensionsValue& value, int atomId, int uid) {
+void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid) {
EXPECT_EQ(value.field(), atomId);
- EXPECT_EQ(value.value_tuple().dimensions_value_size(), 1);
+ ASSERT_EQ(value.value_tuple().dimensions_value_size(), 1);
// Attribution field.
EXPECT_EQ(value.value_tuple().dimensions_value(0).field(), 1);
// Uid only.
@@ -514,7 +1028,7 @@
void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid) {
EXPECT_EQ(value.field(), atomId);
- EXPECT_GT(value.value_tuple().dimensions_value_size(), node_idx);
+ ASSERT_GT(value.value_tuple().dimensions_value_size(), node_idx);
// Attribution field.
EXPECT_EQ(value.value_tuple().dimensions_value(node_idx).field(), 1);
EXPECT_EQ(value.value_tuple().dimensions_value(node_idx)
@@ -526,7 +1040,7 @@
void ValidateAttributionUidAndTagDimension(
const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag) {
EXPECT_EQ(value.field(), atomId);
- EXPECT_GT(value.value_tuple().dimensions_value_size(), node_idx);
+ ASSERT_GT(value.value_tuple().dimensions_value_size(), node_idx);
// Attribution field.
EXPECT_EQ(1, value.value_tuple().dimensions_value(node_idx).field());
// Uid only.
@@ -545,7 +1059,7 @@
void ValidateAttributionUidAndTagDimension(
const DimensionsValue& value, int atomId, int uid, const std::string& tag) {
EXPECT_EQ(value.field(), atomId);
- EXPECT_EQ(1, value.value_tuple().dimensions_value_size());
+ ASSERT_EQ(1, value.value_tuple().dimensions_value_size());
// Attribution field.
EXPECT_EQ(1, value.value_tuple().dimensions_value(0).field());
// Uid only.
@@ -597,6 +1111,27 @@
}
}
+bool LessThan(const google::protobuf::RepeatedPtrField<StateValue>& s1,
+ const google::protobuf::RepeatedPtrField<StateValue>& s2) {
+ if (s1.size() != s2.size()) {
+ return s1.size() < s2.size();
+ }
+ for (int i = 0; i < s1.size(); i++) {
+ const StateValue& state1 = s1[i];
+ const StateValue& state2 = s2[i];
+ if (state1.atom_id() != state2.atom_id()) {
+ return state1.atom_id() < state2.atom_id();
+ }
+ if (state1.value() != state2.value()) {
+ return state1.value() < state2.value();
+ }
+ if (state1.group_id() != state2.group_id()) {
+ return state1.group_id() < state2.group_id();
+ }
+ }
+ return false;
+}
+
bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2) {
if (s1.field() != s2.field()) {
return s1.field() < s2.field();
@@ -645,7 +1180,7 @@
return false;
}
- return LessThan(s1.dimInCondition, s2.dimInCondition);
+ return LessThan(s1.stateValues, s2.stateValues);
}
void backfillStringInDimension(const std::map<uint64_t, string>& str_map,
@@ -825,6 +1360,34 @@
}
}
+Status FakeSubsystemSleepCallback::onPullAtom(int atomTag,
+ const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
+ // Convert stats_events into StatsEventParcels.
+ std::vector<StatsEventParcel> parcels;
+ for (int i = 1; i < 3; i++) {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, atomTag);
+ std::string subsystemName = "subsystem_name_";
+ subsystemName = subsystemName + std::to_string(i);
+ AStatsEvent_writeString(event, subsystemName.c_str());
+ AStatsEvent_writeString(event, "subsystem_subname foo");
+ AStatsEvent_writeInt64(event, /*count= */ i);
+ AStatsEvent_writeInt64(event, /*time_millis= */ i * 100);
+ AStatsEvent_build(event);
+ size_t size;
+ uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
+
+ StatsEventParcel p;
+ // vector.assign() creates a copy, but this is inevitable unless
+ // stats_event.h/c uses a vector as opposed to a buffer.
+ p.buffer.assign(buffer, buffer + size);
+ parcels.push_back(std::move(p));
+ AStatsEvent_release(event);
+ }
+ resultReceiver->pullFinished(atomTag, /*success=*/true, parcels);
+ return Status::ok();
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 635c583..3dcf4ec 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -14,20 +14,47 @@
#pragma once
+#include <aidl/android/os/BnPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomResultReceiver.h>
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
+
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "src/StatsLogProcessor.h"
-#include "src/logd/LogEvent.h"
#include "src/hash.h"
+#include "src/logd/LogEvent.h"
+#include "src/packages/UidMap.h"
#include "src/stats_log_util.h"
-#include "statslog.h"
+#include "stats_event.h"
+#include "statslog_statsdtest.h"
namespace android {
namespace os {
namespace statsd {
+using namespace testing;
+using ::aidl::android::os::BnPullAtomCallback;
+using ::aidl::android::os::IPullAtomCallback;
+using ::aidl::android::os::IPullAtomResultReceiver;
+using android::util::ProtoReader;
using google::protobuf::RepeatedPtrField;
+using Status = ::ndk::ScopedAStatus;
+
+const int SCREEN_STATE_ATOM_ID = util::SCREEN_STATE_CHANGED;
+const int UID_PROCESS_STATE_ATOM_ID = util::UID_PROCESS_STATE_CHANGED;
+
+enum BucketSplitEvent { APP_UPGRADE, BOOT_COMPLETE };
+
+class MockUidMap : public UidMap {
+public:
+ MOCK_METHOD(int, getHostUidOrSelf, (int uid), (const));
+ MOCK_METHOD(std::set<int32_t>, getAppUid, (const string& package), (const));
+};
+
+// Converts a ProtoOutputStream to a StatsLogReport proto.
+StatsLogReport outputStreamToProto(ProtoOutputStream* proto);
// Create AtomMatcher proto to simply match a specific atom type.
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
@@ -53,6 +80,12 @@
// Create AtomMatcher proto for stopping battery save mode.
AtomMatcher CreateBatterySaverModeStopAtomMatcher();
+// Create AtomMatcher proto for battery state none mode.
+AtomMatcher CreateBatteryStateNoneMatcher();
+
+// Create AtomMatcher proto for battery state usb mode.
+AtomMatcher CreateBatteryStateUsbMatcher();
+
// Create AtomMatcher proto for process state changed.
AtomMatcher CreateUidProcessStateChangedAtomMatcher();
@@ -95,6 +128,9 @@
// Create Predicate proto for battery saver mode.
Predicate CreateBatterySaverModePredicate();
+// Create Predicate proto for device unplogged mode.
+Predicate CreateDeviceUnpluggedPredicate();
+
// Create Predicate proto for holding wakelock.
Predicate CreateHoldingWakelockPredicate();
@@ -104,6 +140,39 @@
// Create a Predicate proto for app is in background.
Predicate CreateIsInBackgroundPredicate();
+// Create State proto for screen state atom.
+State CreateScreenState();
+
+// Create State proto for uid process state atom.
+State CreateUidProcessState();
+
+// Create State proto for overlay state atom.
+State CreateOverlayState();
+
+// Create State proto for screen state atom with on/off map.
+State CreateScreenStateWithOnOffMap(int64_t screenOnId, int64_t screenOffId);
+
+// Create State proto for screen state atom with simple on/off map.
+State CreateScreenStateWithSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId);
+
+// Create StateGroup proto for ScreenState ON group
+StateMap_StateGroup CreateScreenStateOnGroup(int64_t screenOnId);
+
+// Create StateGroup proto for ScreenState OFF group
+StateMap_StateGroup CreateScreenStateOffGroup(int64_t screenOffId);
+
+// Create StateGroup proto for simple ScreenState ON group
+StateMap_StateGroup CreateScreenStateSimpleOnGroup(int64_t screenOnId);
+
+// Create StateGroup proto for simple ScreenState OFF group
+StateMap_StateGroup CreateScreenStateSimpleOffGroup(int64_t screenOffId);
+
+// Create StateMap proto for ScreenState ON/OFF map
+StateMap CreateScreenStateOnOffMap(int64_t screenOnId, int64_t screenOffId);
+
+// Create StateMap proto for simple ScreenState ON/OFF map
+StateMap CreateScreenStateSimpleOnOffMap(int64_t screenOnId, int64_t screenOffId);
+
// Add a predicate to the predicate combination.
void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
@@ -118,76 +187,156 @@
FieldMatcher CreateAttributionUidDimensions(const int atomId,
const std::vector<Position>& positions);
+FieldMatcher CreateAttributionUidAndOtherDimensions(const int atomId,
+ const std::vector<Position>& positions,
+ const std::vector<int>& fields);
+
+// START: get primary key functions
+// These functions take in atom field information and create FieldValues which are stored in the
+// given HashableDimensionKey.
+void getUidProcessKey(int uid, HashableDimensionKey* key);
+
+void getOverlayKey(int uid, string packageName, HashableDimensionKey* key);
+
+void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey* key);
+
+void getPartialWakelockKey(int uid, HashableDimensionKey* key);
+// END: get primary key functions
+
+void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
+ const vector<string>& attributionTags);
+
+// Builds statsEvent to get buffer that is parsed into logEvent then releases statsEvent.
+void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
+
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2);
+
+void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2);
+
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3);
+
+void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+ int32_t value2, int32_t value3);
+
+// The repeated value log event helpers create a log event with two int fields, both
+// set to the same value. This is useful for testing metrics that are only interested
+// in the value of the second field but still need the first field to be populated.
+std::shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs,
+ int32_t value);
+
+void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
+ int32_t value);
+
+std::shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs);
+
+void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs);
+
+std::shared_ptr<LogEvent> makeUidLogEvent(int atomId, int64_t eventTimeNs, int uid, int data1,
+ int data2);
+
+std::shared_ptr<LogEvent> makeAttributionLogEvent(int atomId, int64_t eventTimeNs,
+ const vector<int>& uids,
+ const vector<string>& tags, int data1, int data2);
+
+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(
- const android::view::DisplayStateEnum state, uint64_t timestampNs);
+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(
- int level, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level);
// Create log event when scheduled job starts.
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
- const std::vector<AttributionNodeInternal>& attributions,
- const string& name, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& jobName);
// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
- const std::vector<AttributionNodeInternal>& attributions,
- const string& name, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const string& jobName);
// Create log event when battery saver starts.
std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
// Create log event when battery saver stops.
std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
+// Create log event when battery state changes.
+std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestampNs, const BatteryPluggedStateEnum state);
+
// Create log event for app moving to background.
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid);
// Create log event for app moving to foreground.
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid);
// Create log event when the app sync starts.
-std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, const vector<int>& uids,
+ const vector<string>& tags, const string& name);
// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& name,
- uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs, const vector<int>& uids,
+ const vector<string>& tags, const string& name);
// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateAppCrashEvent(
- const int uid, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid);
+
+// Create log event for an app crash.
+std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid);
// Create log event for acquiring wakelock.
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs, const vector<int>& uids,
+ const vector<string>& tags,
+ const string& wakelockName);
// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
- const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
- uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(uint64_t timestampNs, const vector<int>& uids,
+ const vector<string>& tags,
+ const string& wakelockName);
// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
- int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, int hostUid,
+ int isolatedUid, bool is_create);
-// Helper function to create an AttributionNodeInternal proto.
-AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
+// Create log event for uid process state change.
+std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
+ uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state);
+
+std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
+ const vector<int>& attributionUids,
+ const vector<string>& attributionTags,
+ const BleScanStateChanged::State state,
+ const bool filtered, const bool firstMatch,
+ const bool opportunistic);
+
+std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid,
+ const string& packageName,
+ const bool usingAlertWindow,
+ const OverlayStateChanged::State state);
// Create a statsd log event processor upon the start time in seconds, config and key.
-sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs,
- const int64_t currentTimeNs,
- const StatsdConfig& config, const ConfigKey& key);
+sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
+ const StatsdConfig& config, const ConfigKey& key,
+ const shared_ptr<IPullAtomCallback>& puller = nullptr,
+ const int32_t atomTag = 0 /*for puller only*/,
+ const sp<UidMap> = new UidMap());
// Util function to sort the log events by timestamp.
void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
int64_t StringToId(const string& str);
+void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
+ const int uid, const string& tag);
void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid);
void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid);
void ValidateAttributionUidAndTagDimension(
@@ -196,12 +345,14 @@
const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag);
struct DimensionsPair {
- DimensionsPair(DimensionsValue m1, DimensionsValue m2) : dimInWhat(m1), dimInCondition(m2){};
+ DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField<StateValue> m2)
+ : dimInWhat(m1), stateValues(m2){};
DimensionsValue dimInWhat;
- DimensionsValue dimInCondition;
+ google::protobuf::RepeatedPtrField<StateValue> stateValues;
};
+bool LessThan(const StateValue& s1, const StateValue& s2);
bool LessThan(const DimensionsValue& s1, const DimensionsValue& s2);
bool LessThan(const DimensionsPair& s1, const DimensionsPair& s2);
@@ -233,6 +384,12 @@
const google::protobuf::RepeatedPtrField<DimensionsValue>& leafValues,
DimensionsValue* dimension);
+class FakeSubsystemSleepCallback : public BnPullAtomCallback {
+public:
+ Status onPullAtom(int atomTag,
+ const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override;
+};
+
template <typename T>
void backfillDimensionPath(const DimensionsValue& whatPath,
const DimensionsValue& conditionPath,
@@ -264,7 +421,7 @@
for (int i = 0; i < metricData.data_size(); ++i) {
dimensionIndexMap.insert(
std::make_pair(DimensionsPair(metricData.data(i).dimensions_in_what(),
- metricData.data(i).dimensions_in_condition()),
+ metricData.data(i).slice_by_state()),
i));
}
for (const auto& itr : dimensionIndexMap) {
@@ -319,4 +476,4 @@
}
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
index 9e15e99..74eafbf 100644
--- a/cmds/statsd/tests/storage/StorageManager_test.cpp
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <android-base/unique_fd.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <stdio.h>
@@ -39,47 +40,19 @@
bool result;
- result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
- trainInfo.status, trainInfo.experimentIds);
+ result = StorageManager::writeTrainInfo(trainInfo);
EXPECT_TRUE(result);
InstallTrainInfo trainInfoResult;
- result = StorageManager::readTrainInfo(trainInfoResult);
+ result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
EXPECT_TRUE(result);
EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
- EXPECT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
+ ASSERT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName);
EXPECT_EQ(trainInfo.status, trainInfoResult.status);
- EXPECT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
- EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
-}
-
-TEST(StorageManagerTest, TrainInfoReadWriteEmptyTrainNameTest) {
- InstallTrainInfo trainInfo;
- trainInfo.trainVersionCode = 12345;
- trainInfo.trainName = "";
- trainInfo.status = 1;
- const char* expIds = "test_ids";
- trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
-
- bool result;
-
- result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
- trainInfo.status, trainInfo.experimentIds);
-
- EXPECT_TRUE(result);
-
- InstallTrainInfo trainInfoResult;
- result = StorageManager::readTrainInfo(trainInfoResult);
- EXPECT_TRUE(result);
-
- EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
- EXPECT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
- EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName);
- EXPECT_EQ(trainInfo.status, trainInfoResult.status);
- EXPECT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
+ ASSERT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
}
@@ -93,20 +66,19 @@
bool result;
- result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
- trainInfo.status, trainInfo.experimentIds);
+ result = StorageManager::writeTrainInfo(trainInfo);
EXPECT_TRUE(result);
InstallTrainInfo trainInfoResult;
- result = StorageManager::readTrainInfo(trainInfoResult);
+ result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
EXPECT_TRUE(result);
EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
- EXPECT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
+ ASSERT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName);
EXPECT_EQ(trainInfo.status, trainInfoResult.status);
- EXPECT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
+ ASSERT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
}
diff --git a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
new file mode 100644
index 0000000..32cecd3
--- /dev/null
+++ b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+#include "utils/MultiConditionTrigger.h"
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <set>
+#include <thread>
+#include <vector>
+
+#ifdef __ANDROID__
+
+using namespace std;
+using std::this_thread::sleep_for;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(MultiConditionTrigger, TestMultipleConditions) {
+ int numConditions = 5;
+ string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5";
+ set<string> conditionNames = {t1, t2, t3, t4, t5};
+
+ mutex lock;
+ condition_variable cv;
+ bool triggerCalled = false;
+
+ // Mark done as true and notify in the done.
+ MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
+ {
+ lock_guard lg(lock);
+ triggerCalled = true;
+ }
+ cv.notify_all();
+ });
+
+ vector<thread> threads;
+ 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] = 1;
+ trigger.markComplete(conditionName);
+ });
+ i++;
+ }
+
+ unique_lock<mutex> unique_lk(lock);
+ cv.wait(unique_lk, [&triggerCalled] {
+ return triggerCalled;
+ });
+
+ for (i = 0; i < numConditions; i++) {
+ EXPECT_EQ(done[i], 1);
+ }
+
+ for (i = 0; i < numConditions; i++) {
+ threads[i].join();
+ }
+}
+
+TEST(MultiConditionTrigger, TestNoConditions) {
+ mutex lock;
+ condition_variable cv;
+ bool triggerCalled = false;
+
+ MultiConditionTrigger trigger({}, [&lock, &cv, &triggerCalled] {
+ {
+ lock_guard lg(lock);
+ triggerCalled = true;
+ }
+ cv.notify_all();
+ });
+
+ unique_lock<mutex> unique_lk(lock);
+ cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
+ EXPECT_TRUE(triggerCalled);
+ // Ensure that trigger occurs immediately if no events need to be completed.
+}
+
+TEST(MultiConditionTrigger, TestMarkCompleteCalledBySameCondition) {
+ string t1 = "t1", t2 = "t2";
+ set<string> conditionNames = {t1, t2};
+
+ mutex lock;
+ condition_variable cv;
+ bool triggerCalled = false;
+
+ MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] {
+ {
+ lock_guard lg(lock);
+ triggerCalled = true;
+ }
+ cv.notify_all();
+ });
+
+ trigger.markComplete(t1);
+ trigger.markComplete(t1);
+
+ // Ensure that the trigger still hasn't fired.
+ {
+ lock_guard lg(lock);
+ EXPECT_FALSE(triggerCalled);
+ }
+
+ trigger.markComplete(t2);
+ unique_lock<mutex> unique_lk(lock);
+ cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
+ EXPECT_TRUE(triggerCalled);
+}
+
+TEST(MultiConditionTrigger, TestTriggerOnlyCalledOnce) {
+ string t1 = "t1";
+ set<string> conditionNames = {t1};
+
+ mutex lock;
+ condition_variable cv;
+ bool triggerCalled = false;
+ int triggerCount = 0;
+
+ MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled, &triggerCount] {
+ {
+ lock_guard lg(lock);
+ triggerCount++;
+ triggerCalled = true;
+ }
+ cv.notify_all();
+ });
+
+ trigger.markComplete(t1);
+
+ // Ensure that the trigger fired.
+ {
+ unique_lock<mutex> unique_lk(lock);
+ cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; });
+ EXPECT_TRUE(triggerCalled);
+ EXPECT_EQ(triggerCount, 1);
+ triggerCalled = false;
+ }
+
+ trigger.markComplete(t1);
+
+ // Ensure that the trigger does not fire again.
+ {
+ unique_lock<mutex> unique_lk(lock);
+ cv.wait_for(unique_lk, chrono::milliseconds(5), [&triggerCalled] { return triggerCalled; });
+ EXPECT_FALSE(triggerCalled);
+ EXPECT_EQ(triggerCount, 1);
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tools/dogfood/Android.bp b/cmds/statsd/tools/dogfood/Android.bp
deleted file mode 100644
index bb494a6..0000000
--- a/cmds/statsd/tools/dogfood/Android.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2017 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.
-//
-//
-
-android_app {
- name: "StatsdDogfood",
- platform_apis: true,
-
- srcs: ["src/**/*.java"],
-
- resource_dirs: ["res"],
- static_libs: [
- "platformprotoslite",
- "statsdprotolite",
- ],
-
- privileged: true,
- dex_preopt: {
- enabled: false,
- },
- certificate: "platform",
- optimize: {
- enabled: false,
- },
-}
diff --git a/cmds/statsd/tools/dogfood/AndroidManifest.xml b/cmds/statsd/tools/dogfood/AndroidManifest.xml
deleted file mode 100644
index 52673fb..0000000
--- a/cmds/statsd/tools/dogfood/AndroidManifest.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.statsd.dogfood"
- android:sharedUserId="android.uid.system"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-permission android:name="android.permission.DUMP" />
-
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name"
- android:launchMode="singleTop" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
-
- <service android:name=".MainActivity$ReceiverIntentService" android:exported="true" />
- </application>
-</manifest>
diff --git a/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 55621cc..0000000
--- a/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 11ec206..0000000
--- a/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 7c02b78..0000000
--- a/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 915d914..0000000
--- a/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
deleted file mode 100644
index 784ed40..0000000
--- a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
+++ /dev/null
@@ -1,162 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, 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.
-*/
--->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <Button
- android:id="@+id/push_config"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@android:color/holo_green_light"
- android:text="@string/push_config"/>
- <Button
- android:id="@+id/set_receiver"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@android:color/holo_green_light"
- android:text="@string/set_receiver"/>
- <Button
- android:id="@+id/remove_receiver"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@android:color/holo_green_light"
- android:text="@string/remove_receiver"/>
-
- <LinearLayout android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button android:id="@+id/app_a_wake_lock_acquire1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/app_a_get_wl1"/>
- <Button android:id="@+id/app_a_wake_lock_release1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/app_a_release_wl1"/>
- </LinearLayout>
-
- <LinearLayout android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button android:id="@+id/app_a_wake_lock_acquire2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/app_a_get_wl2"/>
- <Button android:id="@+id/app_a_wake_lock_release2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/app_a_release_wl2"/>
- </LinearLayout>
-
- <LinearLayout android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button android:id="@+id/app_b_wake_lock_acquire1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/app_b_get_wl1"/>
- <Button android:id="@+id/app_b_wake_lock_release1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/app_b_release_wl1"/>
- </LinearLayout>
- <LinearLayout android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button android:id="@+id/app_b_wake_lock_acquire2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/app_b_get_wl2"/>
- <Button android:id="@+id/app_b_wake_lock_release2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/app_b_release_wl2"/>
- </LinearLayout>
-
- <LinearLayout android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button android:id="@+id/plug"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/plug"/>
-
- <Button android:id="@+id/unplug"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/unplug"/>
- </LinearLayout>
-
- <LinearLayout android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button android:id="@+id/screen_on"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/screen_on"/>
-
- <Button android:id="@+id/screen_off"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/screen_off"/>
- </LinearLayout>
-
- <LinearLayout android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/custom_start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/custom_start" />
-
- <Button
- android:id="@+id/custom_stop"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/custom_stop" />
- </LinearLayout>
-
- <Button android:id="@+id/dump"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@android:color/holo_purple"
- android:text="@string/dump"/>
-
- <TextView
- android:id="@+id/header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/report_header"/>
-
- <TextView
- android:id="@+id/report_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </LinearLayout>
-
-</ScrollView>
\ No newline at end of file
diff --git a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
deleted file mode 100644
index d050061..0000000
--- a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/dogfood/res/values/strings.xml b/cmds/statsd/tools/dogfood/res/values/strings.xml
deleted file mode 100644
index 60948a1..0000000
--- a/cmds/statsd/tools/dogfood/res/values/strings.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
-
- <string name="app_name">Statsd Dogfood</string>
-
- <string name="statsd_running">Statsd Running</string>
- <string name="statsd_not_running">Statsd NOT Running</string>
-
- <string name="push_config">Push baseline config</string>
- <string name="set_receiver">Set pendingintent</string>
- <string name="remove_receiver">Remove pendingintent</string>
-
- <string name="app_a_foreground">App A foreground</string>
- <string name="app_b_foreground">App B foreground</string>
-
-
- <string name="app_a_get_wl1">App A get wl_1</string>
- <string name="app_a_release_wl1">App A release wl_1</string>
-
- <string name="app_a_get_wl2">App A get wl_2</string>
- <string name="app_a_release_wl2">App A release wl_2</string>
-
- <string name="app_b_get_wl1">App B get wl_1</string>
- <string name="app_b_release_wl1">App B release wl_1</string>
-
- <string name="app_b_get_wl2">App B get wl_2</string>
- <string name="app_b_release_wl2">App B release wl_2</string>
-
- <string name="plug">Plug</string>
- <string name="unplug">Unplug</string>
-
- <string name="screen_on">Screen On</string>
- <string name="screen_off">Screen Off</string>
-
- <string name="custom_start">App hook start</string>
- <string name="custom_stop">App hook stop</string>
-
- <string name="dump">DumpReport</string>
- <string name="report_header">Report details</string>
-</resources>
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
deleted file mode 100644
index b6b16e4..0000000
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.dogfood;
-
-import android.text.format.DateFormat;
-
-import com.android.os.StatsLog;
-
-import java.util.List;
-
-public class DisplayProtoUtils {
- public static void displayLogReport(StringBuilder sb, StatsLog.ConfigMetricsReportList reports) {
- sb.append("ConfigKey: ");
- if (reports.hasConfigKey()) {
- com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey();
- sb.append("\tuid: ").append(key.getUid()).append(" name: ").append(key.getId())
- .append("\n");
- }
-
- for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
- sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
- sb.append("Last report time:").append(getDateStr(report.getLastReportElapsedNanos())).
- append("\n");
- sb.append("Current report time:").append(getDateStr(report.getCurrentReportElapsedNanos())).
- append("\n");
- for (StatsLog.StatsLogReport log : report.getMetricsList()) {
- sb.append("\n\n");
- sb.append("metric id: ").append(log.getMetricId()).append("\n");
-
- switch (log.getDataCase()) {
- case DURATION_METRICS:
- sb.append("Duration metric data\n");
- displayDurationMetricData(sb, log);
- break;
- case EVENT_METRICS:
- sb.append("Event metric data\n");
- displayEventMetricData(sb, log);
- break;
- case COUNT_METRICS:
- sb.append("Count metric data\n");
- displayCountMetricData(sb, log);
- break;
- case GAUGE_METRICS:
- sb.append("Gauge metric data\n");
- displayGaugeMetricData(sb, log);
- break;
- case VALUE_METRICS:
- sb.append("Value metric data\n");
- displayValueMetricData(sb, log);
- break;
- case DATA_NOT_SET:
- sb.append("No metric data\n");
- break;
- }
- }
- }
- }
-
- public static String getDateStr(long nanoSec) {
- return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString();
- }
-
- private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) {
- sb.append(dimensionValue.getField()).append(":");
- if (dimensionValue.hasValueBool()) {
- sb.append(dimensionValue.getValueBool());
- } else if (dimensionValue.hasValueFloat()) {
- sb.append(dimensionValue.getValueFloat());
- } else if (dimensionValue.hasValueInt()) {
- sb.append(dimensionValue.getValueInt());
- } else if (dimensionValue.hasValueStr()) {
- sb.append(dimensionValue.getValueStr());
- } else if (dimensionValue.hasValueTuple()) {
- sb.append("{");
- for (StatsLog.DimensionsValue child :
- dimensionValue.getValueTuple().getDimensionsValueList()) {
- displayDimension(sb, child);
- }
- sb.append("}");
- }
- sb.append(" ");
- }
-
- public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- StatsLog.StatsLogReport.DurationMetricDataWrapper durationMetricDataWrapper
- = log.getDurationMetrics();
- sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
- for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
- sb.append("dimension_in_what: ");
- displayDimension(sb, duration.getDimensionsInWhat());
- sb.append("\n");
- if (duration.hasDimensionsInCondition()) {
- sb.append("dimension_in_condition: ");
- displayDimension(sb, duration.getDimensionsInCondition());
- sb.append("\n");
- }
-
- for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) {
- sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-")
- .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ")
- .append(info.getDurationNanos()).append(" ns\n");
- }
- }
- }
-
- public static void displayEventMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- sb.append("Contains ").append(log.getEventMetrics().getDataCount()).append(" events\n");
- StatsLog.StatsLogReport.EventMetricDataWrapper eventMetricDataWrapper =
- log.getEventMetrics();
- for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) {
- sb.append(getDateStr(event.getElapsedTimestampNanos())).append(": ");
- sb.append(event.getAtom().getPushedCase().toString()).append("\n");
- }
- }
-
- public static void displayCountMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- StatsLog.StatsLogReport.CountMetricDataWrapper countMetricDataWrapper
- = log.getCountMetrics();
- sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
- for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
- sb.append("dimension_in_what: ");
- displayDimension(sb, count.getDimensionsInWhat());
- sb.append("\n");
- if (count.hasDimensionsInCondition()) {
- sb.append("dimension_in_condition: ");
- displayDimension(sb, count.getDimensionsInCondition());
- sb.append("\n");
- }
-
- for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) {
- sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-")
- .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ")
- .append(info.getCount()).append("\n");
- }
- }
- }
-
- public static void displayGaugeMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- sb.append("Display me!");
- }
-
- public static void displayValueMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- sb.append("Display me!");
- }
-}
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
deleted file mode 100644
index 4f4dd01..0000000
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.dogfood;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.app.IntentService;
-import android.app.StatsManager;
-import android.app.StatsManager.StatsUnavailableException;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.StatsLog;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.os.IStatsManager;
-import android.os.ServiceManager;
-
-import java.io.InputStream;
-
-import static com.android.statsd.dogfood.DisplayProtoUtils.displayLogReport;
-
-public class MainActivity extends Activity {
- private final static String TAG = "StatsdDogfood";
- private final static long CONFIG_ID = 987654321;
-
- final int[] mUids = {11111111, 2222222};
- StatsManager mStatsManager;
- TextView mReportText;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_main);
-
- findViewById(R.id.app_a_wake_lock_acquire1).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(0, "wl_1");
- }
- });
-
- findViewById(R.id.app_b_wake_lock_acquire1).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(1, "wl_1");
- }
- });
-
- findViewById(R.id.app_a_wake_lock_acquire2).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(0, "wl_2");
- }
- });
-
- findViewById(R.id.app_b_wake_lock_acquire2).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(1, "wl_2");
- }
- });
-
- findViewById(R.id.app_a_wake_lock_release1).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(0, "wl_1");
- }
- });
-
-
- findViewById(R.id.app_b_wake_lock_release1).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(1, "wl_1");
- }
- });
-
- findViewById(R.id.app_a_wake_lock_release2).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(0, "wl_2");
- }
- });
-
-
- findViewById(R.id.app_b_wake_lock_release2).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(1, "wl_2");
- }
- });
-
-
- findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
- StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_AC);
- }
- });
-
- findViewById(R.id.unplug).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
- StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_NONE);
- }
- });
-
- findViewById(R.id.screen_on).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
- StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON);
- }
- });
-
- findViewById(R.id.screen_off).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
- StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF);
- }
- });
-
- findViewById(R.id.custom_start).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- StatsLog.logStart(8);
- }
- });
-
- findViewById(R.id.custom_stop).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- StatsLog.logStop(8);
- }
- });
-
- mReportText = (TextView) findViewById(R.id.report_text);
-
- findViewById(R.id.dump).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (!statsdRunning()) {
- return;
- }
- if (mStatsManager != null) {
- try {
- byte[] data = mStatsManager.getReports(CONFIG_ID);
- if (data != null) {
- displayData(data);
- return;
- }
- } catch (StatsUnavailableException e) {
- Log.e(TAG, "Failed to get data from statsd", e);
- }
- mReportText.setText("Failed!");
- }
- }
- });
-
- findViewById(R.id.push_config).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- try {
- if (!statsdRunning()) {
- return;
- }
- Resources res = getResources();
- InputStream inputStream = res.openRawResource(R.raw.statsd_baseline_config);
-
- byte[] config = new byte[inputStream.available()];
- inputStream.read(config);
- if (mStatsManager != null) {
- try {
- mStatsManager.addConfig(CONFIG_ID, config);
- Toast.makeText(
- MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show();
- } catch (StatsUnavailableException | IllegalArgumentException e) {
- Toast.makeText(MainActivity.this, "Config push FAILED!",
- Toast.LENGTH_LONG).show();
- }
- }
- } catch (Exception e) {
- Toast.makeText(MainActivity.this, "failed to read config", Toast.LENGTH_LONG);
- }
- }
- });
-
- PendingIntent pi = PendingIntent.getService(this, 0,
- new Intent(this, ReceiverIntentService.class), PendingIntent.FLAG_UPDATE_CURRENT);
- findViewById(R.id.set_receiver).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- try {
- if (!statsdRunning()) {
- return;
- }
- if (mStatsManager != null) {
- try {
- mStatsManager.setFetchReportsOperation(pi, CONFIG_ID);
- Toast.makeText(MainActivity.this,
- "Receiver specified to pending intent", Toast.LENGTH_LONG)
- .show();
- } catch (StatsUnavailableException e) {
- Toast.makeText(MainActivity.this, "Statsd did not set receiver",
- Toast.LENGTH_LONG)
- .show();
- }
- }
- } catch (Exception e) {
- Toast.makeText(MainActivity.this, "failed to set receiver", Toast.LENGTH_LONG);
- }
- }
- });
- findViewById(R.id.remove_receiver).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- try {
- if (!statsdRunning()) {
- return;
- }
- if (mStatsManager != null) {
- try {
- mStatsManager.setFetchReportsOperation(null, CONFIG_ID);
- Toast.makeText(MainActivity.this, "Receiver remove", Toast.LENGTH_LONG)
- .show();
- } catch (StatsUnavailableException e) {
- Toast.makeText(MainActivity.this, "Statsd did not remove receiver",
- Toast.LENGTH_LONG)
- .show();
- }
- }
- } catch (Exception e) {
- Toast.makeText(
- MainActivity.this, "failed to remove receiver", Toast.LENGTH_LONG);
- }
- }
- });
- mStatsManager = (StatsManager) getSystemService("stats");
- }
-
- private boolean statsdRunning() {
- if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) {
- Log.d(TAG, "Statsd not running");
- Toast.makeText(MainActivity.this, "Statsd NOT running!", Toast.LENGTH_LONG).show();
- return false;
- }
- return true;
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- Log.d(TAG, "new intent: " + intent.getIntExtra("pkg", 0));
- int pkg = intent.getIntExtra("pkg", 0);
- String name = intent.getStringExtra("name");
- if (intent.hasExtra("acquire")) {
- onWakeLockAcquire(pkg, name);
- } else if (intent.hasExtra("release")) {
- onWakeLockRelease(pkg, name);
- }
- }
-
- private void displayData(byte[] data) {
- com.android.os.StatsLog.ConfigMetricsReportList reports = null;
- boolean good = false;
- if (data != null) {
- try {
- reports = com.android.os.StatsLog.ConfigMetricsReportList.parseFrom(data);
- good = true;
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- // display it in the text view.
- }
- }
- int size = data == null ? 0 : data.length;
- StringBuilder sb = new StringBuilder();
- sb.append(good ? "Proto parsing OK!" : "Proto parsing Error!");
- sb.append(" size:").append(size).append("\n");
-
- if (good && reports != null) {
- displayLogReport(sb, reports);
- mReportText.setText(sb.toString());
- }
- }
-
-
- private void onWakeLockAcquire(int id, String name) {
- if (id > 1) {
- Log.d(TAG, "invalid pkg id");
- return;
- }
- int[] uids = new int[]{mUids[id]};
- String[] tags = new String[]{"acquire"};
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
- StatsLog.WAKELOCK_STATE_CHANGED__TYPE__PARTIAL_WAKE_LOCK, name,
- StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
- StringBuilder sb = new StringBuilder();
- sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
- .append(", ").append(name).append(", 1);");
- Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();
- }
-
- private void onWakeLockRelease(int id, String name) {
- if (id > 1) {
- Log.d(TAG, "invalid pkg id");
- return;
- }
- int[] uids = new int[]{mUids[id]};
- String[] tags = new String[]{"release"};
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
- StatsLog.WAKELOCK_STATE_CHANGED__TYPE__PARTIAL_WAKE_LOCK, name,
- StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
- StringBuilder sb = new StringBuilder();
- sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
- .append(", ").append(name).append(", 0);");
- Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();
- }
-
- public static class ReceiverIntentService extends IntentService {
- public ReceiverIntentService() {
- super("ReceiverIntentService");
- }
-
- /**
- * The IntentService calls this method from the default worker thread with
- * the intent that started the service. When this method returns, IntentService
- * stops the service, as appropriate.
- */
- @Override
- protected void onHandleIntent(Intent intent) {
- Log.i(TAG, "Received notification that we should call getData");
- }
- }
-}
diff --git a/cmds/statsd/tools/loadtest/Android.bp b/cmds/statsd/tools/loadtest/Android.bp
deleted file mode 100644
index bf87fc5..0000000
--- a/cmds/statsd/tools/loadtest/Android.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2017 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.
-//
-//
-
-android_app {
- name: "StatsdLoadtest",
- platform_apis: true,
-
- srcs: ["src/**/*.java"],
-
- resource_dirs: ["res"],
- static_libs: [
- "platformprotoslite",
- "statsdprotolite",
- ],
-
- certificate: "platform",
- privileged: true,
- dex_preopt: {
- enabled: false,
- },
- optimize: {
- enabled: false,
- },
-}
diff --git a/cmds/statsd/tools/loadtest/AndroidManifest.xml b/cmds/statsd/tools/loadtest/AndroidManifest.xml
deleted file mode 100644
index 2bf8ca9..0000000
--- a/cmds/statsd/tools/loadtest/AndroidManifest.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.statsd.loadtest"
- android:sharedUserId="android.uid.system"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-permission android:name="android.permission.DUMP" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name" >
- <activity
- android:name=".LoadtestActivity"
- android:label="@string/app_name"
- android:launchMode="singleTop" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <receiver android:name=".LoadtestActivity$PusherAlarmReceiver" />
- <receiver android:name=".LoadtestActivity$StopperAlarmReceiver"/>
- <receiver android:name=".PerfData$PerfAlarmReceiver"/>
- </application>
-</manifest>
diff --git a/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 55621cc..0000000
--- a/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 11ec206..0000000
--- a/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 7c02b78..0000000
--- a/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 915d914..0000000
--- a/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
deleted file mode 100644
index d6f8047..0000000
--- a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
+++ /dev/null
@@ -1,208 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, 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.
-*/
--->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <LinearLayout
- android:id="@+id/outside"
- android:clickable="true"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginRight="10dp"
- android:layout_marginLeft="10dp"
- android:orientation="vertical">
- <requestFocus />
-
- <LinearLayout
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
- <TextView
- android:textSize="30dp"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/replication_label" />
- <EditText
- android:id="@+id/replication"
- android:inputType="number"
- android:layout_weight="1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLength="4"
- android:text="@integer/replication_default"
- android:textSize="30dp"/>
- </LinearLayout>
-
- <LinearLayout
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
- <TextView
- android:textSize="30dp"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/bucket_label" />
- <Spinner
- android:id="@+id/bucket_spinner"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:prompt="@string/bucket_label"/>
- </LinearLayout>
-
- <LinearLayout
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
- <TextView
- android:textSize="30dp"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/period_label" />
- <EditText
- android:id="@+id/period"
- android:inputType="number"
- android:layout_weight="1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:maxLength="3"
- android:text="@integer/period_default"
- android:textSize="30dp"/>
- </LinearLayout>
-
- <LinearLayout
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
- <TextView
- android:textSize="30dp"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/burst_label" />
- <EditText
- android:id="@+id/burst"
- android:inputType="number"
- android:layout_weight="1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLength="4"
- android:text="@integer/burst_default"
- android:textSize="30dp"/>
- </LinearLayout>
-
- <LinearLayout
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
- <TextView
- android:textSize="30dp"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/duration_label" />
- <EditText
- android:id="@+id/duration"
- android:inputType="number"
- android:layout_weight="1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:maxLength="4"
- android:text="@integer/duration_default"
- android:textSize="30dp"/>
- </LinearLayout>
- <CheckBox
- android:id="@+id/placebo"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/placebo"
- android:checked="false" />
-
- <LinearLayout
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <CheckBox
- android:id="@+id/include_count"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/count"
- android:checked="true"/>
- <CheckBox
- android:id="@+id/include_duration"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/duration"
- android:checked="true"/>
- <CheckBox
- android:id="@+id/include_event"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/event"
- android:checked="true"/>
- <CheckBox
- android:id="@+id/include_value"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/value"
- android:checked="true"/>
- <CheckBox
- android:id="@+id/include_gauge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/gauge"
- android:checked="true"/>
- </LinearLayout>
-
- <Space
- android:layout_width="1dp"
- android:layout_height="30dp"/>
-
- <Button
- android:id="@+id/start_stop"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="#ffff0000"
- android:text="@string/start"
- android:textSize="50dp"/>
-
- <Space
- android:layout_width="1dp"
- android:layout_height="30dp"/>
-
- <Space
- android:layout_width="1dp"
- android:layout_height="30dp"/>
-
- <TextView
- android:id="@+id/report_text"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </LinearLayout>
-
-</ScrollView>
diff --git a/cmds/statsd/tools/loadtest/res/layout/spinner_item.xml b/cmds/statsd/tools/loadtest/res/layout/spinner_item.xml
deleted file mode 100644
index b03da06..0000000
--- a/cmds/statsd/tools/loadtest/res/layout/spinner_item.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="30dp"
- android:gravity="left"
- android:padding="5dip"
- />
diff --git a/cmds/statsd/tools/loadtest/res/raw/loadtest_config b/cmds/statsd/tools/loadtest/res/raw/loadtest_config
deleted file mode 100755
index 2422190..0000000
--- a/cmds/statsd/tools/loadtest/res/raw/loadtest_config
+++ /dev/null
Binary files differ
diff --git a/cmds/statsd/tools/loadtest/res/values/integers.xml b/cmds/statsd/tools/loadtest/res/values/integers.xml
deleted file mode 100644
index c2407d3..0000000
--- a/cmds/statsd/tools/loadtest/res/values/integers.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <integer name="burst_default">1</integer>
- <integer name="period_default">2</integer>
- <integer name="replication_default">1</integer>
- <integer name="duration_default">240</integer>
-</resources>
diff --git a/cmds/statsd/tools/loadtest/res/values/strings.xml b/cmds/statsd/tools/loadtest/res/values/strings.xml
deleted file mode 100644
index e8ae3f8..0000000
--- a/cmds/statsd/tools/loadtest/res/values/strings.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <string name="app_name">Statsd Loadtest</string>
- <string name="bucket_label">bucket size (mins): </string>
- <string name="burst_label">burst: </string>
- <string name="bucket_default">FIVE_MINUTES</string>
- <string name="placebo">placebo</string>
- <string name="period_label">logging period (secs): </string>
- <string name="replication_label">metric replication: </string>
- <string name="duration_label">test duration (mins): </string>
- <string name="start">  Start  </string>
- <string name="stop">  Stop  </string>
- <string name="count"> count </string>
- <string name="duration"> duration </string>
- <string name="event"> event </string>
- <string name="value"> value </string>
- <string name="gauge"> gauge </string>
-
-</resources>
diff --git a/cmds/statsd/tools/loadtest/run_loadtest.sh b/cmds/statsd/tools/loadtest/run_loadtest.sh
deleted file mode 100755
index 3c93a06..0000000
--- a/cmds/statsd/tools/loadtest/run_loadtest.sh
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/bin/sh
-#
-# Script that measures statsd's PSS under an increasing number of metrics.
-
-# Globals.
-pss=""
-pid=""
-
-# Starts the loadtest.
-start_loadtest() {
- echo "Starting loadtest"
- adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "start"
-}
-
-# Stops the loadtest.
-stop_loadtest() {
- echo "Stopping loadtest"
- adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "stop"
-}
-
-# Sets the metrics replication.
-# Arguments:
-# $1: The replication factor.
-set_replication() {
- adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "set_replication" --ei "replication" "${1}"
- echo "Replication set to ${1}"
-}
-
-# Reads statsd's pid and PSS.
-update_pid_and_pss() {
- # Command that reads the PSS for statsd. This also gives us its pid.
- get_mem=$(adb shell dumpsys meminfo |grep statsd)
- # Looks for statsd's pid.
- regex="([0-9,]+)K: statsd \(pid ([0-9]+)\).*"
- if [[ $get_mem =~ $regex ]]; then
- pss=$(echo "${BASH_REMATCH[1]}" | tr -d , | sed 's/\.//g')
- pid=$(echo "${BASH_REMATCH[2]}")
- else
- echo $cmd doesnt match $regex
- fi
-}
-
-# Kills statsd.
-# Assumes the pid has been set.
-kill_statsd() {
- echo "Killing statsd (pid ${pid})"
- adb shell kill -9 "${pid}"
-}
-
-# Main loop.
-main() {
- start_time=$(date +%s)
- values=()
- stop_loadtest
-
- echo ""
- echo "********************* NEW LOADTEST ************************"
- update_pid_and_pss
- for replication in 1 2 4 8 16 32 64 128 256 512 1024 2048 4096
- do
- echo "**** Starting test at replication ${replication} ****"
-
- # (1) Restart statsd. This will ensure its state is empty.
- kill_statsd
- sleep 3 # wait a bit for it to restart
- update_pid_and_pss
- echo "Before the test, statsd's PSS is ${pss}"
-
- # (2) Set the replication.
- set_replication "${replication}"
- sleep 1 # wait a bit
-
- # (3) Start the loadtest.
- start_loadtest
-
- # (4) Wait several seconds, then read the PSS.
- sleep 100 && update_pid_and_pss
- echo "During the test, statsd's PSS is ${pss}"
- values+=(${pss})
-
- echo "Values: ${values[@]}"
-
- # (5) Stop loadtest.
- stop_loadtest
- sleep 2
-
- echo ""
- done
-
- end_time=$(date +%s)
- echo "Completed loadtest in $((${end_time} - ${start_time})) seconds."
-
- values_as_str=$(IFS=$'\n'; echo "${values[*]}")
- echo "The PSS values are:"
- echo "${values_as_str}"
- echo ""
-}
-
-main
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
deleted file mode 100644
index bab0c1e..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.util.Log;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import java.text.ParseException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class BatteryDataRecorder extends PerfDataRecorder {
- private static final String TAG = "loadtest.BatteryDataRecorder";
- private static final String DUMP_FILENAME = TAG + "_dump.tmp";
-
- public BatteryDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs,
- int burst, boolean includeCountMetric, boolean includeDurationMetric,
- boolean includeEventMetric, boolean includeValueMetric, boolean includeGaugeMetric) {
- super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
- includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
- }
-
- @Override
- public void startRecording(Context context) {
- // Reset batterystats.
- runDumpsysStats(context, DUMP_FILENAME, "batterystats", "--reset");
- }
-
- @Override
- public void onAlarm(Context context) {
- // Nothing to do as for battery, the whole data is in the final dumpsys call.
- }
-
- @Override
- public void stopRecording(Context context) {
- StringBuilder sb = new StringBuilder();
- // Don't use --checkin.
- runDumpsysStats(context, DUMP_FILENAME, "batterystats");
- readDumpData(context, DUMP_FILENAME, new BatteryStatsParser(), sb);
- writeData(context, "battery_", "time,battery_level", sb);
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
deleted file mode 100644
index 203d97a..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.util.Log;
-import java.text.ParseException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class BatteryStatsParser implements PerfParser {
-
- private static final Pattern LINE_PATTERN =
- Pattern.compile("\\s*\\+*(\\S*)\\s\\(\\d+\\)\\s(\\d\\d\\d)\\s.*");
- private static final Pattern TIME_PATTERN =
- Pattern.compile("(\\d+)?(h)?(\\d+)?(m)?(\\d+)?(s)?(\\d+)?(ms)?");
- private static final String TAG = "loadtest.BatteryStatsParser";
-
- private boolean mHistoryStarted;
- private boolean mHistoryEnded;
-
- public BatteryStatsParser() {
- }
-
- @Override
- @Nullable
- public String parseLine(String line) {
- if (mHistoryEnded) {
- return null;
- }
- if (!mHistoryStarted) {
- if (line.contains("Battery History")) {
- mHistoryStarted = true;
- }
- return null;
- }
- if (line.isEmpty()) {
- mHistoryEnded = true;
- return null;
- }
- Matcher lineMatcher = LINE_PATTERN.matcher(line);
- if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) {
- if (lineMatcher.group(1).equals("0")) {
- return "0," + lineMatcher.group(2) + "\n";
- } else {
- Matcher timeMatcher = TIME_PATTERN.matcher(lineMatcher.group(1));
- if (timeMatcher.find()) {
- Long time = getTime(lineMatcher.group(1));
- if (time != null) {
- return time + "," + lineMatcher.group(2) + "\n";
- } else {
- return null; // bad time
- }
- } else {
- return null; // bad or no time
- }
- }
- }
- return null;
- }
-
- @Nullable
- private Long getTime(String group) {
- if ("0".equals(group)) {
- return 0L;
- }
- Matcher timeMatcher = TIME_PATTERN.matcher(group);
- if (!timeMatcher.find()) {
- return null;
- }
-
- // Get rid of "ms".
- String[] matches = group.split("ms", -1);
- if (matches.length > 1) {
- group = matches[0];
- }
-
- long time = 0L;
- matches = group.split("h");
- if (matches.length > 1) {
- time += Long.parseLong(matches[0]) * 60 * 60 * 1000; // hours
- group = matches[1];
- }
- matches = group.split("m");
- if (matches.length > 1) {
- time += Long.parseLong(matches[0]) * 60 * 1000; // minutes
- group = matches[1];
- }
- matches = group.split("s");
- if (matches.length > 1) {
- time += Long.parseLong(matches[0]) * 1000; // seconds
- group = matches[1];
- }
-
- if (!group.isEmpty()) {
- time += Long.parseLong(group); // milliseconds
- }
- return time;
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
deleted file mode 100644
index 2e0161b..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-import com.android.internal.os.StatsdConfigProto.Predicate;
-import com.android.internal.os.StatsdConfigProto.CountMetric;
-import com.android.internal.os.StatsdConfigProto.DurationMetric;
-import com.android.internal.os.StatsdConfigProto.MetricConditionLink;
-import com.android.internal.os.StatsdConfigProto.EventMetric;
-import com.android.internal.os.StatsdConfigProto.GaugeMetric;
-import com.android.internal.os.StatsdConfigProto.ValueMetric;
-import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
-import com.android.internal.os.StatsdConfigProto.AtomMatcher;
-import com.android.internal.os.StatsdConfigProto.SimplePredicate;
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-import java.io.InputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Creates StatsdConfig protos for loadtesting.
- */
-public class ConfigFactory {
- public static class ConfigMetadata {
- public final byte[] bytes;
- public final int numMetrics;
-
- public ConfigMetadata(byte[] bytes, int numMetrics) {
- this.bytes = bytes;
- this.numMetrics = numMetrics;
- }
- }
-
- public static final long CONFIG_ID = 123456789;
-
- private static final String TAG = "loadtest.ConfigFactory";
-
- private final StatsdConfig mTemplate;
-
- public ConfigFactory(Context context) {
- // Read the config template from the resoures.
- Resources res = context.getResources();
- byte[] template = null;
- StatsdConfig templateProto = null;
- try {
- InputStream inputStream = res.openRawResource(R.raw.loadtest_config);
- template = new byte[inputStream.available()];
- inputStream.read(template);
- templateProto = StatsdConfig.parseFrom(template);
- } catch (IOException e) {
- Log.e(TAG, "Unable to read or parse loadtest config template. Using an empty config.");
- }
- mTemplate = templateProto == null ? StatsdConfig.newBuilder().build() : templateProto;
-
- Log.d(TAG, "Loadtest template config: " + mTemplate);
- }
-
- /**
- * Generates a config.
- *
- * All configs are based on the same template.
- * That template is designed to make the most use of the set of atoms that {@code SequencePusher}
- * pushes, and to exercise as many of the metrics features as possible.
- * Furthermore, by passing a replication factor to this method, one can artificially inflate
- * the number of metrics in the config. One can also adjust the bucket size for aggregate
- * metrics.
- *
- * @param replication The number of times each metric is replicated in the config.
- * If the config template has n metrics, the generated config will have n * replication
- * ones
- * @param bucketMillis The bucket size, in milliseconds, for aggregate metrics
- * @param placebo If true, only return an empty config
- * @return The serialized config and the number of metrics.
- */
- public ConfigMetadata getConfig(int replication, TimeUnit bucket, boolean placebo,
- boolean includeCount, boolean includeDuration, boolean includeEvent,
- boolean includeValue, boolean includeGauge) {
- StatsdConfig.Builder config = StatsdConfig.newBuilder()
- .setId(CONFIG_ID);
- if (placebo) {
- replication = 0; // Config will be empty, aside from a name.
- }
- int numMetrics = 0;
- for (int i = 0; i < replication; i++) {
- // metrics
- if (includeEvent) {
- for (EventMetric metric : mTemplate.getEventMetricList()) {
- addEventMetric(metric, i, config);
- numMetrics++;
- }
- }
- if (includeCount) {
- for (CountMetric metric : mTemplate.getCountMetricList()) {
- addCountMetric(metric, i, bucket, config);
- numMetrics++;
- }
- }
- if (includeDuration) {
- for (DurationMetric metric : mTemplate.getDurationMetricList()) {
- addDurationMetric(metric, i, bucket, config);
- numMetrics++;
- }
- }
- if (includeGauge) {
- for (GaugeMetric metric : mTemplate.getGaugeMetricList()) {
- addGaugeMetric(metric, i, bucket, config);
- numMetrics++;
- }
- }
- if (includeValue) {
- for (ValueMetric metric : mTemplate.getValueMetricList()) {
- addValueMetric(metric, i, bucket, config);
- numMetrics++;
- }
- }
- // predicates
- for (Predicate predicate : mTemplate.getPredicateList()) {
- addPredicate(predicate, i, config);
- }
- // matchers
- for (AtomMatcher matcher : mTemplate.getAtomMatcherList()) {
- addMatcher(matcher, i, config);
- }
- }
-
- Log.d(TAG, "Loadtest config is : " + config.build());
- Log.d(TAG, "Generated config has " + numMetrics + " metrics");
-
- return new ConfigMetadata(config.build().toByteArray(), numMetrics);
- }
-
- /**
- * Creates {@link MetricConditionLink}s that are identical to the one passed to this method,
- * except that the names are appended with the provided suffix.
- */
- private List<MetricConditionLink> getLinks(
- List<MetricConditionLink> links, int suffix) {
- List<MetricConditionLink> newLinks = new ArrayList();
- for (MetricConditionLink link : links) {
- newLinks.add(link.toBuilder()
- .setCondition(link.getCondition() + suffix)
- .build());
- }
- return newLinks;
- }
-
- /**
- * Creates an {@link EventMetric} based on the template. Makes sure that all names are appended
- * with the provided suffix. Then adds that metric to the config.
- */
- private void addEventMetric(EventMetric template, int suffix, StatsdConfig.Builder config) {
- EventMetric.Builder metric = template.toBuilder()
- .setId(template.getId() + suffix)
- .setWhat(template.getWhat() + suffix);
- if (template.hasCondition()) {
- metric.setCondition(template.getCondition() + suffix);
- }
- if (template.getLinksCount() > 0) {
- List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
- metric.clearLinks();
- metric.addAllLinks(links);
- }
- config.addEventMetric(metric);
- }
-
- /**
- * Creates a {@link CountMetric} based on the template. Makes sure that all names are appended
- * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
- */
- private void addCountMetric(CountMetric template, int suffix, TimeUnit bucket,
- StatsdConfig.Builder config) {
- CountMetric.Builder metric = template.toBuilder()
- .setId(template.getId() + suffix)
- .setWhat(template.getWhat() + suffix);
- if (template.hasCondition()) {
- metric.setCondition(template.getCondition() + suffix);
- }
- if (template.getLinksCount() > 0) {
- List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
- metric.clearLinks();
- metric.addAllLinks(links);
- }
- metric.setBucket(bucket);
- config.addCountMetric(metric);
- }
-
- /**
- * Creates a {@link DurationMetric} based on the template. Makes sure that all names are appended
- * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
- */
- private void addDurationMetric(DurationMetric template, int suffix, TimeUnit bucket,
- StatsdConfig.Builder config) {
- DurationMetric.Builder metric = template.toBuilder()
- .setId(template.getId() + suffix)
- .setWhat(template.getWhat() + suffix);
- if (template.hasCondition()) {
- metric.setCondition(template.getCondition() + suffix);
- }
- if (template.getLinksCount() > 0) {
- List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
- metric.clearLinks();
- metric.addAllLinks(links);
- }
- metric.setBucket(bucket);
- config.addDurationMetric(metric);
- }
-
- /**
- * Creates a {@link GaugeMetric} based on the template. Makes sure that all names are appended
- * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
- */
- private void addGaugeMetric(GaugeMetric template, int suffix, TimeUnit bucket,
- StatsdConfig.Builder config) {
- GaugeMetric.Builder metric = template.toBuilder()
- .setId(template.getId() + suffix)
- .setWhat(template.getWhat() + suffix);
- if (template.hasCondition()) {
- metric.setCondition(template.getCondition() + suffix);
- }
- if (template.getLinksCount() > 0) {
- List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
- metric.clearLinks();
- metric.addAllLinks(links);
- }
- metric.setBucket(bucket);
- config.addGaugeMetric(metric);
- }
-
- /**
- * Creates a {@link ValueMetric} based on the template. Makes sure that all names are appended
- * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
- */
- private void addValueMetric(ValueMetric template, int suffix, TimeUnit bucket,
- StatsdConfig.Builder config) {
- ValueMetric.Builder metric = template.toBuilder()
- .setId(template.getId() + suffix)
- .setWhat(template.getWhat() + suffix);
- if (template.hasCondition()) {
- metric.setCondition(template.getCondition() + suffix);
- }
- if (template.getLinksCount() > 0) {
- List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
- metric.clearLinks();
- metric.addAllLinks(links);
- }
- metric.setBucket(bucket);
- config.addValueMetric(metric);
- }
-
- /**
- * Creates a {@link Predicate} based on the template. Makes sure that all names
- * are appended with the provided suffix. Then adds that predicate to the config.
- */
- private void addPredicate(Predicate template, int suffix, StatsdConfig.Builder config) {
- Predicate.Builder predicate = template.toBuilder()
- .setId(template.getId() + suffix);
- if (template.hasCombination()) {
- Predicate.Combination.Builder cb = template.getCombination().toBuilder()
- .clearPredicate();
- for (long child : template.getCombination().getPredicateList()) {
- cb.addPredicate(child + suffix);
- }
- predicate.setCombination(cb.build());
- }
- if (template.hasSimplePredicate()) {
- SimplePredicate.Builder sc = template.getSimplePredicate().toBuilder()
- .setStart(template.getSimplePredicate().getStart() + suffix)
- .setStop(template.getSimplePredicate().getStop() + suffix);
- if (template.getSimplePredicate().hasStopAll()) {
- sc.setStopAll(template.getSimplePredicate().getStopAll() + suffix);
- }
- predicate.setSimplePredicate(sc.build());
- }
- config.addPredicate(predicate);
- }
-
- /**
- * Creates a {@link AtomMatcher} based on the template. Makes sure that all names
- * are appended with the provided suffix. Then adds that matcher to the config.
- */
- private void addMatcher(AtomMatcher template, int suffix, StatsdConfig.Builder config) {
- AtomMatcher.Builder matcher = template.toBuilder()
- .setId(template.getId() + suffix);
- if (template.hasCombination()) {
- AtomMatcher.Combination.Builder cb = template.getCombination().toBuilder()
- .clearMatcher();
- for (long child : template.getCombination().getMatcherList()) {
- cb.addMatcher(child + suffix);
- }
- matcher.setCombination(cb);
- }
- config.addAtomMatcher(matcher);
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
deleted file mode 100644
index d55f3f3..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.text.format.DateFormat;
-
-import com.android.os.StatsLog;
-
-import java.util.List;
-
-public class DisplayProtoUtils {
- private static final int MAX_NUM_METRICS_TO_DISPLAY = 10;
-
- public static void displayLogReport(StringBuilder sb, StatsLog.ConfigMetricsReportList reports) {
- sb.append("******************** Report ********************\n");
- if (reports.hasConfigKey()) {
- sb.append("ConfigKey: ");
- com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey();
- sb.append("\tuid: ").append(key.getUid()).append(" id: ").append(key.getId())
- .append("\n");
- }
-
- int numMetrics = 0;
- for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
- sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
- sb.append("Last report time:").append(getDateStr(report.getLastReportElapsedNanos())).
- append("\n");
- sb.append("Current report time:").append(getDateStr(report.getCurrentReportElapsedNanos())).
- append("\n");
- for (StatsLog.StatsLogReport log : report.getMetricsList()) {
- numMetrics++;
- if (numMetrics > MAX_NUM_METRICS_TO_DISPLAY) {
- sb.append("... output truncated\n");
- sb.append("************************************************");
- return;
- }
- sb.append("\n");
- sb.append("metric id: ").append(log.getMetricId()).append("\n");
-
- switch (log.getDataCase()) {
- case DURATION_METRICS:
- sb.append("Duration metric data\n");
- displayDurationMetricData(sb, log);
- break;
- case EVENT_METRICS:
- sb.append("Event metric data\n");
- displayEventMetricData(sb, log);
- break;
- case COUNT_METRICS:
- sb.append("Count metric data\n");
- displayCountMetricData(sb, log);
- break;
- case GAUGE_METRICS:
- sb.append("Gauge metric data\n");
- displayGaugeMetricData(sb, log);
- break;
- case VALUE_METRICS:
- sb.append("Value metric data\n");
- displayValueMetricData(sb, log);
- break;
- case DATA_NOT_SET:
- sb.append("No metric data\n");
- break;
- }
- }
- }
- sb.append("************************************************");
- }
-
- public static String getDateStr(long nanoSec) {
- return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString();
- }
-
- private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) {
- sb.append(dimensionValue.getField()).append(":");
- if (dimensionValue.hasValueBool()) {
- sb.append(dimensionValue.getValueBool());
- } else if (dimensionValue.hasValueFloat()) {
- sb.append(dimensionValue.getValueFloat());
- } else if (dimensionValue.hasValueInt()) {
- sb.append(dimensionValue.getValueInt());
- } else if (dimensionValue.hasValueStr()) {
- sb.append(dimensionValue.getValueStr());
- } else if (dimensionValue.hasValueTuple()) {
- sb.append("{");
- for (StatsLog.DimensionsValue child :
- dimensionValue.getValueTuple().getDimensionsValueList()) {
- displayDimension(sb, child);
- }
- sb.append("}");
- }
- sb.append(" ");
- }
-
- public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- StatsLog.StatsLogReport.DurationMetricDataWrapper durationMetricDataWrapper
- = log.getDurationMetrics();
- sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
- for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
- sb.append("dimension_in_what: ");
- displayDimension(sb, duration.getDimensionsInWhat());
- sb.append("\n");
- if (duration.hasDimensionsInCondition()) {
- sb.append("dimension_in_condition: ");
- displayDimension(sb, duration.getDimensionsInCondition());
- sb.append("\n");
- }
-
- for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) {
- sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-")
- .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ")
- .append(info.getDurationNanos()).append(" ns\n");
- }
- }
- }
-
- public static void displayEventMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- sb.append("Contains ").append(log.getEventMetrics().getDataCount()).append(" events\n");
- StatsLog.StatsLogReport.EventMetricDataWrapper eventMetricDataWrapper =
- log.getEventMetrics();
- for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) {
- sb.append(getDateStr(event.getElapsedTimestampNanos())).append(": ");
- sb.append(event.getAtom().getPushedCase().toString()).append("\n");
- }
- }
-
- public static void displayCountMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- StatsLog.StatsLogReport.CountMetricDataWrapper countMetricDataWrapper
- = log.getCountMetrics();
- sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
- for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
- sb.append("dimension_in_what: ");
- displayDimension(sb, count.getDimensionsInWhat());
- sb.append("\n");
- if (count.hasDimensionsInCondition()) {
- sb.append("dimension_in_condition: ");
- displayDimension(sb, count.getDimensionsInCondition());
- sb.append("\n");
- }
-
- for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) {
- sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-")
- .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ")
- .append(info.getCount()).append("\n");
- }
- }
- }
-
- public static void displayGaugeMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- sb.append("Display me!");
- }
-
- public static void displayValueMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
- sb.append("Display me!");
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
deleted file mode 100644
index 769f78c..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.StatsManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IStatsManager;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.util.StatsLog;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.view.MotionEvent;
-import android.view.View.OnFocusChangeListener;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.Spinner;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.os.StatsLog.ConfigMetricsReport;
-import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.os.StatsLog.StatsdStatsReport;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Runs a load test for statsd.
- * How it works:
- * <ul>
- * <li> Sets up and pushes a custom config with metrics that exercise a large swath of code paths.
- * <li> Periodically logs certain atoms into logd.
- * <li> Impact on battery can be printed to logcat, or a bug report can be filed and analyzed
- * in battery Historian.
- * </ul>
- * The load depends on how demanding the config is, as well as how frequently atoms are pushsed
- * to logd. Those are all controlled by 4 adjustable parameters:
- * <ul>
- * <li> The 'replication' parameter artificially multiplies the number of metrics in the config.
- * <li> The bucket size controls the time-bucketing the aggregate metrics.
- * <li> The period parameter controls how frequently atoms are pushed to logd.
- * <li> The 'burst' parameter controls how many atoms are pushed at the same time (per period).
- * </ul>
- */
-public class LoadtestActivity extends Activity implements AdapterView.OnItemSelectedListener {
-
- private static final String TAG = "loadtest.LoadtestActivity";
- public static final String TYPE = "type";
- private static final String PUSH_ALARM = "push_alarm";
- public static final String PERF_ALARM = "perf_alarm";
- private static final String SET_REPLICATION = "set_replication";
- private static final String REPLICATION = "replication";
- private static final String START = "start";
- private static final String STOP = "stop";
- private static final Map<String, TimeUnit> TIME_UNIT_MAP = initializeTimeUnitMap();
- private static final List<String> TIME_UNIT_LABELS = initializeTimeUnitLabels();
-
- public final static class PusherAlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Intent activityIntent = new Intent(context, LoadtestActivity.class);
- activityIntent.putExtra(TYPE, PUSH_ALARM);
- context.startActivity(activityIntent);
- }
- }
-
- public final static class StopperAlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Intent activityIntent = new Intent(context, LoadtestActivity.class);
- activityIntent.putExtra(TYPE, STOP);
- context.startActivity(activityIntent);
- }
- }
-
- private static Map<String, TimeUnit> initializeTimeUnitMap() {
- Map<String, TimeUnit> labels = new HashMap();
- labels.put("1m", TimeUnit.ONE_MINUTE);
- labels.put("5m", TimeUnit.FIVE_MINUTES);
- labels.put("10m", TimeUnit.TEN_MINUTES);
- labels.put("30m", TimeUnit.THIRTY_MINUTES);
- labels.put("1h", TimeUnit.ONE_HOUR);
- labels.put("3h", TimeUnit.THREE_HOURS);
- labels.put("6h", TimeUnit.SIX_HOURS);
- labels.put("12h", TimeUnit.TWELVE_HOURS);
- labels.put("1d", TimeUnit.ONE_DAY);
- labels.put("1s", TimeUnit.CTS);
- return labels;
- }
-
- private static List<String> initializeTimeUnitLabels() {
- List<String> labels = new ArrayList();
- labels.add("1s");
- labels.add("1m");
- labels.add("5m");
- labels.add("10m");
- labels.add("30m");
- labels.add("1h");
- labels.add("3h");
- labels.add("6h");
- labels.add("12h");
- labels.add("1d");
- return labels;
- }
-
- private AlarmManager mAlarmMgr;
-
- /**
- * Used to periodically log atoms to logd.
- */
- private PendingIntent mPushPendingIntent;
-
- /**
- * Used to end the loadtest.
- */
- private PendingIntent mStopPendingIntent;
-
- private Button mStartStop;
- private EditText mReplicationText;
- private Spinner mBucketSpinner;
- private EditText mPeriodText;
- private EditText mBurstText;
- private EditText mDurationText;
- private TextView mReportText;
- private CheckBox mPlaceboCheckBox;
- private CheckBox mCountMetricCheckBox;
- private CheckBox mDurationMetricCheckBox;
- private CheckBox mEventMetricCheckBox;
- private CheckBox mValueMetricCheckBox;
- private CheckBox mGaugeMetricCheckBox;
-
- /**
- * When the load test started.
- */
- private long mStartedTimeMillis;
-
- /**
- * For measuring perf data.
- */
- private PerfData mPerfData;
-
- /**
- * For communicating with statsd.
- */
- private StatsManager mStatsManager;
-
- private PowerManager mPowerManager;
- private WakeLock mWakeLock;
-
- /**
- * If true, we only measure the effect of the loadtest infrastructure. No atom are pushed and
- * the configuration is empty.
- */
- private boolean mPlacebo;
-
- /**
- * Whether to include CountMetric in the config.
- */
- private boolean mIncludeCountMetric;
-
- /**
- * Whether to include DurationMetric in the config.
- */
- private boolean mIncludeDurationMetric;
-
- /**
- * Whether to include EventMetric in the config.
- */
- private boolean mIncludeEventMetric;
-
- /**
- * Whether to include ValueMetric in the config.
- */
- private boolean mIncludeValueMetric;
-
- /**
- * Whether to include GaugeMetric in the config.
- */
- private boolean mIncludeGaugeMetric;
-
- /**
- * The burst size.
- */
- private int mBurst;
-
- /**
- * The metrics replication.
- */
- private int mReplication;
-
- /**
- * The period, in seconds, at which batches of atoms are pushed.
- */
- private long mPeriodSecs;
-
- /**
- * The bucket size, in minutes, for aggregate metrics.
- */
- private TimeUnit mBucket;
-
- /**
- * The duration, in minutes, of the loadtest.
- */
- private long mDurationMins;
-
- /**
- * Whether the loadtest has started.
- */
- private boolean mStarted = false;
-
- /**
- * Orchestrates the logging of pushed events into logd.
- */
- private SequencePusher mPusher;
-
- /**
- * Generates statsd configs.
- */
- private ConfigFactory mFactory;
-
- /**
- * For intra-minute periods.
- */
- private final Handler mHandler = new Handler();
-
- /**
- * Number of metrics in the current config.
- */
- private int mNumMetrics;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Log.d(TAG, "Starting loadtest Activity");
-
- setContentView(R.layout.activity_loadtest);
- mReportText = (TextView) findViewById(R.id.report_text);
- initBurst();
- initReplication();
- initBucket();
- initPeriod();
- initDuration();
- initPlacebo();
- initMetricWhitelist();
-
- // Hide the keyboard outside edit texts.
- findViewById(R.id.outside).setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- InputMethodManager imm =
- (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- if (getCurrentFocus() != null) {
- imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
- }
- return true;
- }
- });
-
- mStartStop = findViewById(R.id.start_stop);
- mStartStop.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mStarted) {
- stopLoadtest();
- } else {
- startLoadtest();
- }
- }
- });
-
- mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- mStatsManager = (StatsManager) getSystemService("stats");
- mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
- mFactory = new ConfigFactory(this);
- stopLoadtest();
- mReportText.setText("");
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- String type = intent.getStringExtra(TYPE);
- if (type == null) {
- return;
- }
- switch (type) {
- case PERF_ALARM:
- onPerfAlarm();
- break;
- case PUSH_ALARM:
- onAlarm();
- break;
- case SET_REPLICATION:
- if (intent.hasExtra(REPLICATION)) {
- setReplication(intent.getIntExtra(REPLICATION, 0));
- }
- break;
- case START:
- startLoadtest();
- break;
- case STOP:
- stopLoadtest();
- break;
- default:
- throw new IllegalArgumentException("Unknown type: " + type);
- }
- }
-
- @Override
- public void onDestroy() {
- Log.d(TAG, "Destroying");
- mPerfData.onDestroy();
- stopLoadtest();
- clearConfigs();
- super.onDestroy();
- }
-
- @Nullable
- public StatsdStatsReport getMetadata() {
- if (!statsdRunning()) {
- return null;
- }
- if (mStatsManager != null) {
- byte[] data;
- try {
- data = mStatsManager.getStatsMetadata();
- } catch (StatsManager.StatsUnavailableException e) {
- Log.e(TAG, "Failed to get data from statsd", e);
- return null;
- }
- if (data != null) {
- StatsdStatsReport report = null;
- boolean good = false;
- try {
- return StatsdStatsReport.parseFrom(data);
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- Log.d(TAG, "Bad StatsdStatsReport");
- }
- }
- }
- return null;
- }
-
- @Nullable
- public List<ConfigMetricsReport> getData() {
- if (!statsdRunning()) {
- return null;
- }
- if (mStatsManager != null) {
- byte[] data;
- try {
- data = mStatsManager.getReports(ConfigFactory.CONFIG_ID);
- } catch (StatsManager.StatsUnavailableException e) {
- Log.e(TAG, "Failed to get data from statsd", e);
- return null;
- }
- if (data != null) {
- ConfigMetricsReportList reports = null;
- try {
- reports = ConfigMetricsReportList.parseFrom(data);
- Log.d(TAG, "Num reports: " + reports.getReportsCount());
- StringBuilder sb = new StringBuilder();
- DisplayProtoUtils.displayLogReport(sb, reports);
- Log.d(TAG, sb.toString());
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- Log.d(TAG, "Invalid data");
- }
- if (reports != null) {
- return reports.getReportsList();
- }
- }
- }
- return null;
- }
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- String item = parent.getItemAtPosition(position).toString();
-
- mBucket = TIME_UNIT_MAP.get(item);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // Another interface callback
- }
-
- private void onPerfAlarm() {
- if (mPerfData != null) {
- mPerfData.onAlarm(this);
- }
- // Piggy-back on that alarm to show the elapsed time.
- long elapsedTimeMins = (long) Math.floor(
- (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
- mReportText.setText("Loadtest in progress.\n"
- + "num metrics =" + mNumMetrics
- + "\nElapsed time = " + elapsedTimeMins + " min(s)");
- }
-
- private void onAlarm() {
- Log.d(TAG, "ON ALARM");
-
- // Set the next task.
- scheduleNext();
-
- // Do the work.
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StatsdLoadTest");
- mWakeLock.acquire();
- if (mPusher != null) {
- mPusher.next();
- }
- mWakeLock.release();
- mWakeLock = null;
- }
-
- /**
- * Schedules the next cycle of pushing atoms into logd.
- */
- private void scheduleNext() {
- Intent intent = new Intent(this, PusherAlarmReceiver.class);
- intent.putExtra(TYPE, PUSH_ALARM);
- mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
- long nextTime = SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
- mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent);
- }
-
- private synchronized void startLoadtest() {
- if (mStarted) {
- return;
- }
-
- // Clean up the state.
- stopLoadtest();
-
- // Prepare to push a sequence of atoms to logd.
- mPusher = new SequencePusher(mBurst, mPlacebo);
-
- // Create a config and push it to statsd.
- if (!setConfig(mFactory.getConfig(mReplication, mBucket, mPlacebo,
- mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric,
- mIncludeValueMetric, mIncludeGaugeMetric))) {
- return;
- }
-
- // Remember to stop in the future.
- Intent intent = new Intent(this, StopperAlarmReceiver.class);
- intent.putExtra(TYPE, STOP);
- mStopPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
- long nextTime = SystemClock.elapsedRealtime() + mDurationMins * 60 * 1000;
- mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mStopPendingIntent);
-
- // Log atoms.
- scheduleNext();
-
- // Start tracking performance.
- mPerfData = new PerfData(this, mPlacebo, mReplication, mBucket, mPeriodSecs, mBurst,
- mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, mIncludeValueMetric,
- mIncludeGaugeMetric);
- mPerfData.startRecording(this);
-
- mReportText.setText("Loadtest in progress.\nnum metrics =" + mNumMetrics);
- mStartedTimeMillis = SystemClock.elapsedRealtime();
-
- updateStarted(true);
- }
-
- private synchronized void stopLoadtest() {
- if (mPushPendingIntent != null) {
- Log.d(TAG, "Canceling pre-existing push alarm");
- mAlarmMgr.cancel(mPushPendingIntent);
- mPushPendingIntent = null;
- }
- if (mStopPendingIntent != null) {
- Log.d(TAG, "Canceling pre-existing stop alarm");
- mAlarmMgr.cancel(mStopPendingIntent);
- mStopPendingIntent = null;
- }
- if (mWakeLock != null) {
- mWakeLock.release();
- mWakeLock = null;
- }
- if (mPerfData != null) {
- mPerfData.stopRecording(this);
- mPerfData.onDestroy();
- mPerfData = null;
- }
-
- // Obtain the latest data and display it.
- getData();
-
- long elapsedTimeMins = (long) Math.floor(
- (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
- mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)");
- clearConfigs();
- updateStarted(false);
- }
-
- private synchronized void updateStarted(boolean started) {
- mStarted = started;
- mStartStop.setBackgroundColor(started ?
- Color.parseColor("#FFFF0000") : Color.parseColor("#FF00FF00"));
- mStartStop.setText(started ? getString(R.string.stop) : getString(R.string.start));
- updateControlsEnabled();
- }
-
- private void updateControlsEnabled() {
- mBurstText.setEnabled(!mPlacebo && !mStarted);
- mReplicationText.setEnabled(!mPlacebo && !mStarted);
- mPeriodText.setEnabled(!mStarted);
- mBucketSpinner.setEnabled(!mPlacebo && !mStarted);
- mDurationText.setEnabled(!mStarted);
- mPlaceboCheckBox.setEnabled(!mStarted);
-
- boolean enabled = !mStarted && !mPlaceboCheckBox.isChecked();
- mCountMetricCheckBox.setEnabled(enabled);
- mDurationMetricCheckBox.setEnabled(enabled);
- mEventMetricCheckBox.setEnabled(enabled);
- mValueMetricCheckBox.setEnabled(enabled);
- mGaugeMetricCheckBox.setEnabled(enabled);
- }
-
- private boolean statsdRunning() {
- if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) {
- Log.d(TAG, "Statsd not running");
- Toast.makeText(LoadtestActivity.this, "Statsd NOT running!", Toast.LENGTH_LONG).show();
- return false;
- }
- return true;
- }
-
- private int sanitizeInt(int val, int min, int max) {
- if (val > max) {
- val = max;
- } else if (val < min) {
- val = min;
- }
- return val;
- }
-
- private void clearConfigs() {
- // TODO: Clear all configs instead of specific ones.
- if (mStatsManager != null) {
- if (mStarted) {
- try {
- mStatsManager.removeConfig(ConfigFactory.CONFIG_ID);
- Log.d(TAG, "Removed loadtest statsd configs.");
- } catch (StatsManager.StatsUnavailableException e) {
- Log.e(TAG, "Failed to remove loadtest configs.", e);
- }
- }
- }
- }
-
- private boolean setConfig(ConfigFactory.ConfigMetadata configData) {
- if (mStatsManager != null) {
- try {
- mStatsManager.addConfig(ConfigFactory.CONFIG_ID, configData.bytes);
- mNumMetrics = configData.numMetrics;
- Log.d(TAG, "Config pushed to statsd");
- return true;
- } catch (StatsManager.StatsUnavailableException | IllegalArgumentException e) {
- Log.e(TAG, "Failed to push config to statsd", e);
- }
- }
- return false;
- }
-
- private synchronized void setReplication(int replication) {
- if (mStarted) {
- return;
- }
- mReplicationText.setText("" + replication);
- }
-
- private synchronized void setPeriodSecs(long periodSecs) {
- mPeriodSecs = periodSecs;
- }
-
- private synchronized void setBurst(int burst) {
- mBurst = burst;
- }
-
- private synchronized void setDurationMins(long durationMins) {
- mDurationMins = durationMins;
- }
-
-
- private void handleFocus(EditText editText) {
- /*
- editText.setOnFocusChangeListener(new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (!hasFocus && editText.getText().toString().isEmpty()) {
- editText.setText("-1", TextView.BufferType.EDITABLE);
- }
- }
- });
- */
- }
-
- private void initBurst() {
- mBurst = getResources().getInteger(R.integer.burst_default);
- mBurstText = (EditText) findViewById(R.id.burst);
- mBurstText.addTextChangedListener(new NumericalWatcher(mBurstText, 0, 1000) {
- @Override
- public void onNewValue(int newValue) {
- setBurst(newValue);
- }
- });
- handleFocus(mBurstText);
- }
-
- private void initReplication() {
- mReplication = getResources().getInteger(R.integer.replication_default);
- mReplicationText = (EditText) findViewById(R.id.replication);
- mReplicationText.addTextChangedListener(new NumericalWatcher(mReplicationText, 1, 4096) {
- @Override
- public void onNewValue(int newValue) {
- mReplication = newValue;
- }
- });
- handleFocus(mReplicationText);
- }
-
- private void initBucket() {
- String defaultValue = getResources().getString(R.string.bucket_default);
- mBucket = TimeUnit.valueOf(defaultValue);
- mBucketSpinner = (Spinner) findViewById(R.id.bucket_spinner);
-
- ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(
- this, R.layout.spinner_item, TIME_UNIT_LABELS);
-
- mBucketSpinner.setAdapter(dataAdapter);
- mBucketSpinner.setOnItemSelectedListener(this);
-
- for (String label : TIME_UNIT_MAP.keySet()) {
- if (defaultValue.equals(TIME_UNIT_MAP.get(label).toString())) {
- mBucketSpinner.setSelection(dataAdapter.getPosition(label));
- }
- }
- }
-
- private void initPeriod() {
- mPeriodSecs = getResources().getInteger(R.integer.period_default);
- mPeriodText = (EditText) findViewById(R.id.period);
- mPeriodText.addTextChangedListener(new NumericalWatcher(mPeriodText, 1, 60) {
- @Override
- public void onNewValue(int newValue) {
- setPeriodSecs(newValue);
- }
- });
- handleFocus(mPeriodText);
- }
-
- private void initDuration() {
- mDurationMins = getResources().getInteger(R.integer.duration_default);
- mDurationText = (EditText) findViewById(R.id.duration);
- mDurationText.addTextChangedListener(new NumericalWatcher(mDurationText, 1, 24 * 60) {
- @Override
- public void onNewValue(int newValue) {
- setDurationMins(newValue);
- }
- });
- handleFocus(mDurationText);
- }
-
- private void initPlacebo() {
- mPlaceboCheckBox = findViewById(R.id.placebo);
- mPlacebo = false;
- mPlaceboCheckBox.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mPlacebo = mPlaceboCheckBox.isChecked();
- updateControlsEnabled();
- }
- });
- }
-
- private void initMetricWhitelist() {
- mCountMetricCheckBox = findViewById(R.id.include_count);
- mCountMetricCheckBox.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mIncludeCountMetric = mCountMetricCheckBox.isChecked();
- }
- });
- mDurationMetricCheckBox = findViewById(R.id.include_duration);
- mDurationMetricCheckBox.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mIncludeDurationMetric = mDurationMetricCheckBox.isChecked();
- }
- });
- mEventMetricCheckBox = findViewById(R.id.include_event);
- mEventMetricCheckBox.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mIncludeEventMetric = mEventMetricCheckBox.isChecked();
- }
- });
- mValueMetricCheckBox = findViewById(R.id.include_value);
- mValueMetricCheckBox.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mIncludeValueMetric = mValueMetricCheckBox.isChecked();
- }
- });
- mGaugeMetricCheckBox = findViewById(R.id.include_gauge);
- mGaugeMetricCheckBox.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mIncludeGaugeMetric = mGaugeMetricCheckBox.isChecked();
- }
- });
-
- mIncludeCountMetric = mCountMetricCheckBox.isChecked();
- mIncludeDurationMetric = mDurationMetricCheckBox.isChecked();
- mIncludeEventMetric = mEventMetricCheckBox.isChecked();
- mIncludeValueMetric = mValueMetricCheckBox.isChecked();
- mIncludeGaugeMetric = mGaugeMetricCheckBox.isChecked();
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java
deleted file mode 100644
index 01eebf2..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.os.SystemClock;
-import android.util.Log;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/** Parses PSS info from dumpsys meminfo */
-public class MemInfoParser implements PerfParser {
-
- private static final Pattern LINE_PATTERN =
- Pattern.compile("\\s*(\\d*,*\\d*)K:\\s(\\S*)\\s\\.*");
- private static final String PSS_BY_PROCESS = "Total PSS by process:";
- private static final String TAG = "loadtest.MemInfoParser";
-
- private boolean mPssStarted;
- private boolean mPssEnded;
- private final long mStartTimeMillis;
-
- public MemInfoParser(long startTimeMillis) {
- mStartTimeMillis = startTimeMillis;
- }
-
- @Override
- @Nullable
- public String parseLine(String line) {
- if (mPssEnded) {
- return null;
- }
- if (!mPssStarted) {
- if (line.contains(PSS_BY_PROCESS)) {
- mPssStarted = true;
- }
- return null;
- }
- if (line.isEmpty()) {
- mPssEnded = true;
- return null;
- }
- Matcher lineMatcher = LINE_PATTERN.matcher(line);
- if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) {
- if (lineMatcher.group(2).equals("statsd")) {
- long timeDeltaMillis = SystemClock.elapsedRealtime() - mStartTimeMillis;
- return timeDeltaMillis + "," + convertToPss(lineMatcher.group(1));
- }
- }
- return null;
- }
-
- private String convertToPss(String input) {
- return input.replace(",", "");
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
deleted file mode 100644
index af7bd4d..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.content.Context;
-import android.os.SystemClock;
-import android.util.Log;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-public class MemoryDataRecorder extends PerfDataRecorder {
- private static final String TAG = "loadtest.MemoryDataDataRecorder";
- private static final String DUMP_FILENAME = TAG + "_dump.tmp";
-
- private long mStartTimeMillis;
- private StringBuilder mSb;
-
- public MemoryDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs,
- int burst, boolean includeCountMetric, boolean includeDurationMetric,
- boolean includeEventMetric, boolean includeValueMetric, boolean includeGaugeMetric) {
- super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
- includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
- }
-
- @Override
- public void startRecording(Context context) {
- mStartTimeMillis = SystemClock.elapsedRealtime();
- mSb = new StringBuilder();
- }
-
- @Override
- public void onAlarm(Context context) {
- runDumpsysStats(context, DUMP_FILENAME, "meminfo");
- readDumpData(context, DUMP_FILENAME, new MemInfoParser(mStartTimeMillis), mSb);
- }
-
- @Override
- public void stopRecording(Context context) {
- writeData(context, "meminfo_", "time,pss", mSb);
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
deleted file mode 100644
index 555e6dd..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.widget.TextView;
-
-public abstract class NumericalWatcher implements TextWatcher {
-
- private static final String TAG = "loadtest.NumericalWatcher";
-
- private final TextView mTextView;
- private final int mMin;
- private final int mMax;
- private int currentValue = -1;
-
- public NumericalWatcher(TextView textView, int min, int max) {
- mTextView = textView;
- mMin = min;
- mMax = max;
- }
-
- public abstract void onNewValue(int newValue);
-
- @Override
- final public void afterTextChanged(Editable editable) {
- String s = mTextView.getText().toString();
- if (s.isEmpty()) {
- return;
- }
- int unsanitized = Integer.parseInt(s);
- int newValue = sanitize(unsanitized);
- if (currentValue != newValue || unsanitized != newValue) {
- currentValue = newValue;
- editable.clear();
- editable.append(newValue + "");
- }
- onNewValue(newValue);
- }
-
- @Override
- final public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- final public void onTextChanged(CharSequence s, int start, int before, int count) {}
-
- private int sanitize(int val) {
- if (val > mMax) {
- val = mMax;
- } else if (val < mMin) {
- val = mMin;
- }
- return val;
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
deleted file mode 100644
index 7a01ade..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/** Prints some information about the device via Dumpsys in order to evaluate health metrics. */
-public class PerfData extends PerfDataRecorder {
-
- private static final String TAG = "loadtest.PerfData";
-
- /** Polling period for performance snapshots like memory. */
- private static final long POLLING_PERIOD_MILLIS = 1 * 60 * 1000;
-
- public final static class PerfAlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Intent activityIntent = new Intent(context, LoadtestActivity.class);
- activityIntent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
- context.startActivity(activityIntent);
- }
- }
-
- private AlarmManager mAlarmMgr;
-
- /** Used to periodically poll some dumpsys data. */
- private PendingIntent mPendingIntent;
-
- private final Set<PerfDataRecorder> mRecorders;
-
- public PerfData(LoadtestActivity loadtestActivity, boolean placebo, int replication,
- TimeUnit bucket, long periodSecs, int burst, boolean includeCountMetric,
- boolean includeDurationMetric, boolean includeEventMetric, boolean includeValueMetric,
- boolean includeGaugeMetric) {
- super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
- includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
- mRecorders = new HashSet();
- mRecorders.add(new BatteryDataRecorder(placebo, replication, bucket, periodSecs, burst,
- includeCountMetric, includeDurationMetric, includeEventMetric, includeValueMetric,
- includeGaugeMetric));
- mRecorders.add(new MemoryDataRecorder(placebo, replication, bucket, periodSecs, burst,
- includeCountMetric, includeDurationMetric, includeEventMetric, includeValueMetric,
- includeGaugeMetric));
- mRecorders.add(new StatsdStatsRecorder(loadtestActivity, placebo, replication, bucket,
- periodSecs, burst, includeCountMetric, includeDurationMetric, includeEventMetric,
- includeValueMetric, includeGaugeMetric));
- mRecorders.add(new ValidationRecorder(loadtestActivity, placebo, replication, bucket,
- periodSecs, burst, includeCountMetric, includeDurationMetric, includeEventMetric,
- includeValueMetric, includeGaugeMetric));
- mAlarmMgr = (AlarmManager) loadtestActivity.getSystemService(Context.ALARM_SERVICE);
- }
-
- public void onDestroy() {
- if (mPendingIntent != null) {
- mAlarmMgr.cancel(mPendingIntent);
- mPendingIntent = null;
- }
- }
-
- @Override
- public void startRecording(Context context) {
- Intent intent = new Intent(context, PerfAlarmReceiver.class);
- intent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
- mPendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
- mAlarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, -1 /* now */,
- POLLING_PERIOD_MILLIS, mPendingIntent);
-
- for (PerfDataRecorder recorder : mRecorders) {
- recorder.startRecording(context);
- }
- }
-
- @Override
- public void onAlarm(Context context) {
- for (PerfDataRecorder recorder : mRecorders) {
- recorder.onAlarm(context);
- }
- }
-
- @Override
- public void stopRecording(Context context) {
- if (mPendingIntent != null) {
- mAlarmMgr.cancel(mPendingIntent);
- mPendingIntent = null;
- }
-
- for (PerfDataRecorder recorder : mRecorders) {
- recorder.stopRecording(context);
- }
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
deleted file mode 100644
index 8613ac1..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.os.Environment;
-import android.util.Log;
-import android.os.Debug;
-
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import java.io.BufferedReader;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-public abstract class PerfDataRecorder {
- private static final String TAG = "loadtest.PerfDataRecorder";
-
- protected final String mTimeAsString;
- protected final String mColumnSuffix;
-
- protected PerfDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs,
- int burst, boolean includeCountMetric, boolean includeDurationMetric,
- boolean includeEventMetric, boolean includeValueMetric, boolean includeGaugeMetric) {
- mTimeAsString = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
- mColumnSuffix = getColumnSuffix(placebo, replication, bucket, periodSecs, burst,
- includeCountMetric, includeDurationMetric, includeEventMetric, includeValueMetric,
- includeGaugeMetric);
- }
-
- /** Starts recording performance data. */
- public abstract void startRecording(Context context);
-
- /** Called periodically. For the recorder to sample data, if needed. */
- public abstract void onAlarm(Context context);
-
- /** Stops recording performance data, and writes it to disk. */
- public abstract void stopRecording(Context context);
-
- /** Runs the dumpsys command. */
- protected void runDumpsysStats(Context context, String dumpFilename, String cmd,
- String... args) {
- boolean success = false;
- // Call dumpsys Dump statistics to a file.
- FileOutputStream fo = null;
- try {
- fo = context.openFileOutput(dumpFilename, Context.MODE_PRIVATE);
- if (!Debug.dumpService(cmd, fo.getFD(), args)) {
- Log.w(TAG, "Dumpsys failed.");
- }
- success = true;
- } catch (IOException | SecurityException | NullPointerException e) {
- // SecurityException may occur when trying to dump multi-user info.
- // NPE can occur during dumpService (root cause unknown).
- throw new RuntimeException(e);
- } finally {
- closeQuietly(fo);
- }
- }
-
- /**
- * Reads a text file and parses each line, one by one. The result of the parsing is stored
- * in the passed {@link StringBuffer}.
- */
- protected void readDumpData(Context context, String dumpFilename, PerfParser parser,
- StringBuilder sb) {
- FileInputStream fi = null;
- BufferedReader br = null;
- try {
- fi = context.openFileInput(dumpFilename);
- br = new BufferedReader(new InputStreamReader(fi));
- String line = br.readLine();
- while (line != null) {
- String recordLine = parser.parseLine(line);
- if (recordLine != null) {
- sb.append(recordLine).append('\n');
- }
- line = br.readLine();
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- closeQuietly(br);
- }
- }
-
- /** Writes CSV data to a file. */
- protected void writeData(Context context, String filePrefix, String columnPrefix,
- StringBuilder sb) {
- File dataFile = new File(getStorageDir(), filePrefix + mTimeAsString + ".csv");
-
- FileWriter writer = null;
- try {
- writer = new FileWriter(dataFile);
- writer.append(columnPrefix + mColumnSuffix + "\n");
- writer.append(sb.toString());
- writer.flush();
- Log.d(TAG, "Finished writing data at " + dataFile.getAbsolutePath());
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- closeQuietly(writer);
- }
- }
-
- /** Gets the suffix to use in the column name for perf data. */
- private String getColumnSuffix(boolean placebo, int replication, TimeUnit bucket,
- long periodSecs, int burst, boolean includeCountMetric, boolean includeDurationMetric,
- boolean includeEventMetric, boolean includeValueMetric, boolean includeGaugeMetric) {
- if (placebo) {
- return "_placebo_p=" + periodSecs;
- }
- StringBuilder sb = new StringBuilder()
- .append("_r=" + replication)
- .append("_bkt=" + bucket)
- .append("_p=" + periodSecs)
- .append("_bst=" + burst)
- .append("_m=");
- if (includeCountMetric) {
- sb.append("c");
- }
- if (includeEventMetric) {
- sb.append("e");
- }
- if (includeDurationMetric) {
- sb.append("d");
- }
- if (includeGaugeMetric) {
- sb.append("g");
- }
- if (includeValueMetric) {
- sb.append("v");
- }
- return sb.toString();
- }
-
- private File getStorageDir() {
- File file = new File(Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DOCUMENTS), "loadtest/" + mTimeAsString);
- if (!file.mkdirs()) {
- Log.e(TAG, "Directory not created");
- }
- return file;
- }
-
- private void closeQuietly(@Nullable Closeable c) {
- if (c != null) {
- try {
- c.close();
- } catch (IOException ignore) {
- }
- }
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
deleted file mode 100644
index e000918..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.annotation.Nullable;
-
-public interface PerfParser {
-
- /**
- * Parses one line of the dumpsys output, and returns a string to write to the data file,
- * or null if no string should be written.
- */
- @Nullable String parseLine(String line);
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java
deleted file mode 100644
index 5dcce9a..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.util.Log;
-import android.util.StatsLog;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Manages the pushing of atoms into logd for loadtesting.
- * We rely on small number of pushed atoms, and a config with metrics based on those atoms.
- * The atoms are:
- * <ul>
- * <li> BatteryLevelChanged - For EventMetric, CountMetric and GaugeMetric (no dimensions).
- * <li> BleScanResultReceived - For CountMetric and ValueMetric, sliced by uid.
- * <li> ChargingStateChanged - For DurationMetric (no dimension).
- * <li> GpsScanStateChanged - For DurationMetric, sliced by uid.
- * <li> ScreenStateChanged - For Conditions with no dimensions.
- * <li> AudioStateChanged - For Conditions with dimensions (uid).
- * </ul>
- * The sequence is played over and over at a given frequency.
- */
-public class SequencePusher {
- private static final String TAG = "SequencePusher";
-
- /** Some atoms are pushed in burst of {@code mBurst} events. */
- private final int mBurst;
-
- /** If this is true, we don't log anything in logd. */
- private final boolean mPlacebo;
-
- /** Current state in the automaton. */
- private int mCursor = 0;
-
- public SequencePusher(int burst, boolean placebo) {
- mBurst = burst;
- mPlacebo = placebo;
- }
-
- /**
- * Pushes the next atom to logd.
- * This follows a small automaton which makes the right events and conditions overlap:
- * (0) Push a burst of BatteryLevelChanged atoms.
- * (1) Push a burst of BleScanResultReceived atoms.
- * (2) Push ChargingStateChanged with BATTERY_STATUS_CHARGING once.
- * (3) Push a burst of GpsScanStateChanged atoms with ON, with a different uid each time.
- * (4) Push ChargingStateChanged with BATTERY_STATUS_NOT_CHARGING once.
- * (5) Push a burst GpsScanStateChanged atoms with OFF, with a different uid each time.
- * (6) Push ScreenStateChanged with STATE_ON once.
- * (7) Push a burst of AudioStateChanged with ON, with a different uid each time.
- * (8) Repeat steps (0)-(5).
- * (9) Push ScreenStateChanged with STATE_OFF once.
- * (10) Push a burst of AudioStateChanged with OFF, with a different uid each time.
- * and repeat.
- */
- public void next() {
- Log.d(TAG, "Next step: " + mCursor);
- if (mPlacebo) {
- return;
- }
- switch (mCursor) {
- case 0:
- case 8:
- for (int i = 0; i < mBurst; i++) {
- StatsLog.write(StatsLog.BATTERY_LEVEL_CHANGED, 50 + i /* battery_level */);
- }
- break;
- case 1:
- case 9:
- for (int i = 0; i < mBurst; i++) {
- StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, i /* uid */,
- 100 /* num_of_results */);
- }
- break;
- case 2:
- case 10:
- StatsLog.write(StatsLog.CHARGING_STATE_CHANGED,
- StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_CHARGING
- /* charging_state */);
- break;
- case 3:
- case 11:
- for (int i = 0; i < mBurst; i++) {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */,
- StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON /* state */);
- }
- break;
- case 4:
- case 12:
- StatsLog.write(StatsLog.CHARGING_STATE_CHANGED,
- StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_NOT_CHARGING
- /* charging_state */);
- break;
- case 5:
- case 13:
- for (int i = 0; i < mBurst; i++) {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */,
- StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF /* state */);
- }
- break;
- case 6:
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
- StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON /* display_state */);
- break;
- case 7:
- for (int i = 0; i < mBurst; i++) {
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */,
- StatsLog.AUDIO_STATE_CHANGED__STATE__ON /* state */);
- }
- break;
- case 14:
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
- StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF /* display_state */);
- break;
- case 15:
- for (int i = 0; i < mBurst; i++) {
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */,
- StatsLog.AUDIO_STATE_CHANGED__STATE__OFF /* state */);
- }
- break;
- default:
- }
- mCursor++;
- if (mCursor > 15) {
- mCursor = 0;
- }
- }
-
- /**
- * Properly finishes in order to be close all conditions and durations.
- */
- public void finish() {
- // Screen goes back to off. This will ensure that conditions get back to false.
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
- StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF /* display_state */);
- for (int i = 0; i < mBurst; i++) {
- StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */,
- StatsLog.AUDIO_STATE_CHANGED__STATE__OFF /* state */);
- }
- // Stop charging, to ensure the corresponding durations are closed.
- StatsLog.write(StatsLog.CHARGING_STATE_CHANGED,
- StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_NOT_CHARGING
- /* charging_state */);
- // Stop scanning GPS, to ensure the corresponding conditions get back to false.
- for (int i = 0; i < mBurst; i++) {
- StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */,
- StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF /* state */);
- }
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
deleted file mode 100644
index 3939e7e..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.content.Context;
-import com.android.os.StatsLog.StatsdStatsReport;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-
-public class StatsdStatsRecorder extends PerfDataRecorder {
- private static final String TAG = "loadtest.StatsdStatsRecorder";
-
- private final LoadtestActivity mLoadtestActivity;
-
- public StatsdStatsRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication,
- TimeUnit bucket, long periodSecs, int burst, boolean includeCountMetric,
- boolean includeDurationMetric, boolean includeEventMetric, boolean includeValueMetric,
- boolean includeGaugeMetric) {
- super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
- includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
- mLoadtestActivity = loadtestActivity;
- }
-
- @Override
- public void startRecording(Context context) {
- // Nothing to do.
- }
-
- @Override
- public void onAlarm(Context context) {
- // Nothing to do.
- }
-
- @Override
- public void stopRecording(Context context) {
- StatsdStatsReport metadata = mLoadtestActivity.getMetadata();
- if (metadata != null) {
- int numConfigs = metadata.getConfigStatsCount();
- StringBuilder sb = new StringBuilder();
- StatsdStatsReport.ConfigStats configStats = metadata.getConfigStats(numConfigs - 1);
- sb.append("metric_count,")
- .append(configStats.getMetricCount() + "\n")
- .append("condition_count,")
- .append(configStats.getConditionCount() + "\n")
- .append("matcher_count,")
- .append(configStats.getMatcherCount() + "\n");
- writeData(context, "statsdstats_", "stat,value", sb);
- }
- }
-}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java
deleted file mode 100644
index d9f0ca9..0000000
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-package com.android.statsd.loadtest;
-
-import android.content.Context;
-import android.util.Log;
-import com.android.os.StatsLog.ConfigMetricsReport;
-import com.android.os.StatsLog.EventMetricData;
-import com.android.os.StatsLog.StatsLogReport;
-import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Checks the correctness of the stats.
- */
-public class ValidationRecorder extends PerfDataRecorder {
- private static final String TAG = "loadtest.ValidationRecorder";
-
- private final LoadtestActivity mLoadtestActivity;
-
- public ValidationRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication,
- TimeUnit bucket, long periodSecs, int burst, boolean includeCountMetric,
- boolean includeDurationMetric, boolean includeEventMetric, boolean includeValueMetric,
- boolean includeGaugeMetric) {
- super(placebo, replication, bucket, periodSecs, burst, includeCountMetric,
- includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric);
- mLoadtestActivity = loadtestActivity;
- }
-
- @Override
- public void startRecording(Context context) {
- // Nothing to do.
- }
-
- @Override
- public void onAlarm(Context context) {
- validateData();
- }
-
- @Override
- public void stopRecording(Context context) {
- validateData();
- }
-
- private void validateData() {
- // The code below is commented out because it calls getData, which has the side-effect
- // of clearing statsd's data buffer.
- /*
- List<ConfigMetricsReport> reports = mLoadtestActivity.getData();
- if (reports != null) {
- Log.d(TAG, "GOT DATA");
- for (ConfigMetricsReport report : reports) {
- for (StatsLogReport logReport : report.getMetricsList()) {
- if (!logReport.hasMetricId()) {
- Log.e(TAG, "Metric missing name.");
- }
- }
- }
- }
- */
- }
-
- private void validateEventBatteryLevelChanges(StatsLogReport logReport) {
- Log.d(TAG, "Validating " + logReport.getMetricId());
- if (logReport.hasEventMetrics()) {
- Log.d(TAG, "Num events captured: " + logReport.getEventMetrics().getDataCount());
- for (EventMetricData data : logReport.getEventMetrics().getDataList()) {
- Log.d(TAG, " Event : " + data.getAtom());
- }
- } else {
- Log.d(TAG, "Metric is invalid");
- }
- }
-
- private void validateEventBatteryLevelChangesWhileScreenIsOn(StatsLogReport logReport) {
- Log.d(TAG, "Validating " + logReport.getMetricId());
- }
-}
diff --git a/cmds/statsd/tools/localtools/Android.bp b/cmds/statsd/tools/localtools/Android.bp
index 75a57a3..69a43a8 100644
--- a/cmds/statsd/tools/localtools/Android.bp
+++ b/cmds/statsd/tools/localtools/Android.bp
@@ -11,9 +11,8 @@
],
}
-java_binary_host {
- name: "statsd_testdrive",
- manifest: "testdrive_manifest.txt",
+java_library_host {
+ name: "statsd_testdrive_lib",
srcs: [
"src/com/android/statsd/shelltools/testdrive/*.java",
"src/com/android/statsd/shelltools/Utils.java",
@@ -22,4 +21,26 @@
"platformprotos",
"guava",
],
-}
\ No newline at end of file
+}
+
+
+java_binary_host {
+ name: "statsd_testdrive",
+ manifest: "testdrive_manifest.txt",
+ static_libs: [
+ "statsd_testdrive_lib",
+ ],
+}
+
+java_test_host {
+ name: "statsd_testdrive_test",
+ test_suites: ["general-tests"],
+ srcs: ["test/com/android/statsd/shelltools/testdrive/*.java"],
+ static_libs: [
+ "statsd_testdrive_lib",
+ "junit",
+ "platformprotos",
+ "guava",
+ ],
+}
+
diff --git a/cmds/statsd/tools/localtools/TEST_MAPPING b/cmds/statsd/tools/localtools/TEST_MAPPING
new file mode 100644
index 0000000..7c8a3db
--- /dev/null
+++ b/cmds/statsd/tools/localtools/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "statsd_testdrive_test",
+ "host": true
+ }
+ ]
+}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index a381f9c..6a74480 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -17,16 +17,23 @@
import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.google.common.io.Files;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Utilities for local use of statsd.
@@ -80,7 +87,8 @@
* @throws InterruptedException
*/
public static ConfigMetricsReportList getReportList(long configId, boolean clearData,
- boolean useShellUid, Logger logger) throws IOException, InterruptedException {
+ boolean useShellUid, Logger logger, String deviceSerial)
+ throws IOException, InterruptedException {
try {
File outputFile = File.createTempFile("statsdret", ".bin");
outputFile.deleteOnExit();
@@ -88,6 +96,8 @@
outputFile,
logger,
"adb",
+ "-s",
+ deviceSerial,
"shell",
CMD_DUMP_REPORT,
useShellUid ? SHELL_UID : "",
@@ -117,12 +127,14 @@
* @throws IOException
* @throws InterruptedException
*/
- public static void logAppBreadcrumb(int label, int state, Logger logger)
+ public static void logAppBreadcrumb(int label, int state, Logger logger, String deviceSerial)
throws IOException, InterruptedException {
runCommand(
null,
logger,
"adb",
+ "-s",
+ deviceSerial,
"shell",
CMD_LOG_APP_BREADCRUMB,
String.valueOf(label),
@@ -145,13 +157,14 @@
* Algorithm: true if (sdk >= minSdk) || (sdk == minSdk-1 && codeName.startsWith(minCodeName))
* If all else fails, assume it will work (letting future commands deal with any errors).
*/
- public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename) {
+ public static boolean isAcceptableStatsd(Logger logger, int minSdk, String minCodename,
+ String deviceSerial) {
BufferedReader in = null;
try {
File outFileSdk = File.createTempFile("shelltools_sdk", "tmp");
outFileSdk.deleteOnExit();
runCommand(outFileSdk, logger,
- "adb", "shell", "getprop", "ro.build.version.sdk");
+ "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.sdk");
in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileSdk)));
// If NullPointerException/NumberFormatException/etc., just catch and return true.
int sdk = Integer.parseInt(in.readLine().trim());
@@ -162,7 +175,7 @@
File outFileCode = File.createTempFile("shelltools_codename", "tmp");
outFileCode.deleteOnExit();
runCommand(outFileCode, logger,
- "adb", "shell", "getprop", "ro.build.version.codename");
+ "adb", "-s", deviceSerial, "shell", "getprop", "ro.build.version.codename");
in = new BufferedReader(new InputStreamReader(new FileInputStream(outFileCode)));
return in.readLine().startsWith(minCodename);
} else {
@@ -190,4 +203,82 @@
return record.getMessage() + "\n";
}
}
+
+ /**
+ * Parse the result of "adb devices" to return the list of connected devices.
+ * @param logger Logger to log error messages
+ * @return List of the serial numbers of the connected devices.
+ */
+ public static List<String> getDeviceSerials(Logger logger) {
+ try {
+ ArrayList<String> devices = new ArrayList<>();
+ File outFile = File.createTempFile("device_serial", "tmp");
+ outFile.deleteOnExit();
+ Utils.runCommand(outFile, logger, "adb", "devices");
+ List<String> outputLines = Files.readLines(outFile, Charset.defaultCharset());
+ Pattern regex = Pattern.compile("^(.*)\tdevice$");
+ for (String line : outputLines) {
+ Matcher m = regex.matcher(line);
+ if (m.find()) {
+ devices.add(m.group(1));
+ }
+ }
+ return devices;
+ } catch (Exception ex) {
+ logger.log(Level.SEVERE, "Failed to list connected devices: " + ex.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * Returns ANDROID_SERIAL environment variable, or null if that is undefined or unavailable.
+ * @param logger Destination of error messages.
+ * @return String value of ANDROID_SERIAL environment variable, or null.
+ */
+ public static String getDefaultDevice(Logger logger) {
+ try {
+ return System.getenv("ANDROID_SERIAL");
+ } catch (Exception ex) {
+ logger.log(Level.SEVERE, "Failed to check ANDROID_SERIAL environment variable.",
+ ex);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the device to use if one can be deduced, or null.
+ * @param device Command-line specified device, or null.
+ * @param connectedDevices List of all connected devices.
+ * @param defaultDevice Environment-variable specified device, or null.
+ * @param logger Destination of error messages.
+ * @return Device to use, or null.
+ */
+ public static String chooseDevice(String device, List<String> connectedDevices,
+ String defaultDevice, Logger logger) {
+ if (connectedDevices == null || connectedDevices.isEmpty()) {
+ logger.severe("No connected device.");
+ return null;
+ }
+ if (device != null) {
+ if (connectedDevices.contains(device)) {
+ return device;
+ }
+ logger.severe("Device not connected: " + device);
+ return null;
+ }
+ if (connectedDevices.size() == 1) {
+ return connectedDevices.get(0);
+ }
+ if (defaultDevice != null) {
+ if (connectedDevices.contains(defaultDevice)) {
+ return defaultDevice;
+ } else {
+ logger.severe("ANDROID_SERIAL device is not connected: " + defaultDevice);
+ return null;
+ }
+ }
+ logger.severe("More than one device is connected. Choose one"
+ + " with -s DEVICE_SERIAL or environment variable ANDROID_SERIAL.");
+ return null;
+ }
}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
index 2eb4660..ec3c7df 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -26,6 +26,7 @@
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
+import java.util.List;
import java.util.logging.Logger;
/**
@@ -49,7 +50,7 @@
public static final String HELP_STRING =
"Usage:\n\n" +
- "statsd_localdrive upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] upload CONFIG_FILE [CONFIG_ID] [--binary]\n" +
" Uploads the given statsd config file (in binary or human-readable-text format).\n" +
" If a config with this id already exists, removes it first.\n" +
" CONFIG_FILE Location of config file on host.\n" +
@@ -59,12 +60,12 @@
// Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
"\n" +
- "statsd_localdrive update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] update CONFIG_FILE [CONFIG_ID] [--binary]\n" +
" Same as upload, but does not remove the old config first (if it already exists).\n" +
// Similar to: adb shell cmd stats config update SHELL_UID CONFIG_ID
"\n" +
- "statsd_localdrive get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]\n" +
" Prints the output statslog data (in binary or human-readable-text format).\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
" --binary Output should be in binary, instead of default human-readable text.\n" +
@@ -75,13 +76,13 @@
// --include_current_bucket --proto
"\n" +
- "statsd_localdrive remove [CONFIG_ID]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] remove [CONFIG_ID]\n" +
" Removes the config.\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
// Equivalent to: adb shell cmd stats config remove SHELL_UID CONFIG_ID
"\n" +
- "statsd_localdrive clear [CONFIG_ID]\n" +
+ "statsd_localdrive [-s DEVICE_SERIAL] clear [CONFIG_ID]\n" +
" Clears the data associated with the config.\n" +
" CONFIG_ID Long ID of the config. If absent, uses " + DEFAULT_CONFIG_ID + ".\n" +
// Similar to: adb shell cmd stats dump-report SHELL_UID CONFIG_ID
@@ -94,29 +95,51 @@
/** Usage: make statsd_localdrive && statsd_localdrive */
public static void main(String[] args) {
Utils.setUpLogger(sLogger, DEBUG);
+ if (args.length == 0) {
+ printHelp();
+ return;
+ }
- if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME)) {
+ int remainingArgsLength = args.length;
+ String deviceSerial = null;
+ if (args[0].equals("-s")) {
+ if (args.length == 1) {
+ printHelp();
+ }
+ deviceSerial = args[1];
+ remainingArgsLength -= 2;
+ }
+
+ List<String> connectedDevices = Utils.getDeviceSerials(sLogger);
+ deviceSerial = Utils.chooseDevice(deviceSerial, connectedDevices,
+ Utils.getDefaultDevice(sLogger), sLogger);
+ if (deviceSerial == null) {
+ return;
+ }
+
+ if (!Utils.isAcceptableStatsd(sLogger, MIN_SDK, MIN_CODENAME, deviceSerial)) {
sLogger.severe("LocalDrive only works with statsd versions for Android "
+ MIN_CODENAME + " or higher.");
return;
}
- if (args.length > 0) {
- switch (args[0]) {
+ int idx = args.length - remainingArgsLength;
+ if (remainingArgsLength > 0) {
+ switch (args[idx]) {
case "clear":
- cmdClear(args);
+ cmdClear(args, idx, deviceSerial);
return;
case "get-data":
- cmdGetData(args);
+ cmdGetData(args, idx, deviceSerial);
return;
case "remove":
- cmdRemove(args);
+ cmdRemove(args, idx);
return;
case "update":
- cmdUpdate(args);
+ cmdUpdate(args, idx, deviceSerial);
return;
case "upload":
- cmdUpload(args);
+ cmdUpload(args, idx, deviceSerial);
return;
}
}
@@ -128,17 +151,18 @@
}
// upload CONFIG_FILE [CONFIG_ID] [--binary]
- private static boolean cmdUpload(String[] args) {
- return updateConfig(args, true);
+ private static boolean cmdUpload(String[] args, int idx, String deviceSerial) {
+ return updateConfig(args, idx, true, deviceSerial);
}
// update CONFIG_FILE [CONFIG_ID] [--binary]
- private static boolean cmdUpdate(String[] args) {
- return updateConfig(args, false);
+ private static boolean cmdUpdate(String[] args, int idx, String deviceSerial) {
+ return updateConfig(args, idx, false, deviceSerial);
}
- private static boolean updateConfig(String[] args, boolean removeOldConfig) {
- int argCount = args.length - 1; // Used up one for upload/update.
+ private static boolean updateConfig(String[] args, int idx, boolean removeOldConfig,
+ String deviceSerial) {
+ int argCount = args.length - 1 - idx; // Used up one for upload/update.
// Get CONFIG_FILE
if (argCount < 1) {
@@ -146,7 +170,7 @@
printHelp();
return false;
}
- final String origConfigLocation = args[1];
+ final String origConfigLocation = args[idx + 1];
if (!new File(origConfigLocation).exists()) {
sLogger.severe("Error - Cannot find the provided config file: " + origConfigLocation);
return false;
@@ -154,13 +178,13 @@
argCount--;
// Get --binary
- boolean binary = contains(args, 2, BINARY_FLAG);
+ boolean binary = contains(args, idx + 2, BINARY_FLAG);
if (binary) argCount --;
// Get CONFIG_ID
long configId;
try {
- configId = getConfigId(argCount < 1, args, 2);
+ configId = getConfigId(argCount < 1, args, idx + 2);
} catch (NumberFormatException e) {
sLogger.severe("Invalid config id provided.");
printHelp();
@@ -174,7 +198,8 @@
try {
Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
Utils.SHELL_UID, String.valueOf(configId));
- Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger);
+ Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger,
+ deviceSerial);
} catch (InterruptedException | IOException e) {
sLogger.severe("Failed to remove config: " + e.getMessage());
return false;
@@ -218,19 +243,19 @@
}
// get-data [CONFIG_ID] [--clear] [--binary] [--no-uid-map]
- private static boolean cmdGetData(String[] args) {
- boolean binary = contains(args, 1, BINARY_FLAG);
- boolean noUidMap = contains(args, 1, NO_UID_MAP_FLAG);
- boolean clearData = contains(args, 1, CLEAR_DATA);
+ private static boolean cmdGetData(String[] args, int idx, String deviceSerial) {
+ boolean binary = contains(args, idx + 1, BINARY_FLAG);
+ boolean noUidMap = contains(args, idx + 1, NO_UID_MAP_FLAG);
+ boolean clearData = contains(args, idx + 1, CLEAR_DATA);
// Get CONFIG_ID
- int argCount = args.length - 1; // Used up one for get-data.
+ int argCount = args.length - 1 - idx; // Used up one for get-data.
if (binary) argCount--;
if (noUidMap) argCount--;
if (clearData) argCount--;
long configId;
try {
- configId = getConfigId(argCount < 1, args, 1);
+ configId = getConfigId(argCount < 1, args, idx + 1);
} catch (NumberFormatException e) {
sLogger.severe("Invalid config id provided.");
printHelp();
@@ -243,7 +268,8 @@
// Even if the args request no modifications, we still parse it to make sure it's valid.
ConfigMetricsReportList reportList;
try {
- reportList = Utils.getReportList(configId, clearData, true /* SHELL_UID */, sLogger);
+ reportList = Utils.getReportList(configId, clearData, true /* SHELL_UID */, sLogger,
+ deviceSerial);
} catch (IOException | InterruptedException e) {
sLogger.severe("Failed to get report list: " + e.getMessage());
return false;
@@ -274,11 +300,11 @@
}
// clear [CONFIG_ID]
- private static boolean cmdClear(String[] args) {
+ private static boolean cmdClear(String[] args, int idx, String deviceSerial) {
// Get CONFIG_ID
long configId;
try {
- configId = getConfigId(false, args, 1);
+ configId = getConfigId(false, args, idx + 1);
} catch (NumberFormatException e) {
sLogger.severe("Invalid config id provided.");
printHelp();
@@ -287,7 +313,8 @@
sLogger.fine(String.format("cmdClear with %d", configId));
try {
- Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger);
+ Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger,
+ deviceSerial);
} catch (IOException | InterruptedException e) {
sLogger.severe("Failed to get report list: " + e.getMessage());
return false;
@@ -296,11 +323,11 @@
}
// remove [CONFIG_ID]
- private static boolean cmdRemove(String[] args) {
+ private static boolean cmdRemove(String[] args, int idx) {
// Get CONFIG_ID
long configId;
try {
- configId = getConfigId(false, args, 1);
+ configId = getConfigId(false, args, idx + 1);
} catch (NumberFormatException e) {
sLogger.severe("Invalid config id provided.");
printHelp();
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 c97f035..51bcad1 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
@@ -15,27 +15,34 @@
*/
package com.android.statsd.shelltools.testdrive;
+import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.EventMetric;
import com.android.internal.os.StatsdConfigProto.FieldFilter;
import com.android.internal.os.StatsdConfigProto.GaugeMetric;
+import com.android.internal.os.StatsdConfigProto.PullAtomPackages;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
import com.android.os.AtomsProto.Atom;
+import com.android.os.StatsLog;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.StatsLogReport;
import com.android.statsd.shelltools.Utils;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -49,190 +56,364 @@
private static final int VENDOR_PULLED_ATOM_START_TAG = 150000;
private static final long CONFIG_ID = 54321;
private static final String[] ALLOWED_LOG_SOURCES = {
- "AID_GRAPHICS",
- "AID_INCIDENTD",
- "AID_STATSD",
- "AID_RADIO",
- "com.android.systemui",
- "com.android.vending",
- "AID_SYSTEM",
- "AID_ROOT",
- "AID_BLUETOOTH",
- "AID_LMKD",
- "com.android.managedprovisioning",
- "AID_MEDIA",
- "AID_NETWORK_STACK"
+ "AID_GRAPHICS",
+ "AID_INCIDENTD",
+ "AID_STATSD",
+ "AID_RADIO",
+ "com.android.systemui",
+ "com.android.vending",
+ "AID_SYSTEM",
+ "AID_ROOT",
+ "AID_BLUETOOTH",
+ "AID_LMKD",
+ "com.android.managedprovisioning",
+ "AID_MEDIA",
+ "AID_NETWORK_STACK",
+ "com.google.android.providers.media.module",
+ };
+ private static final String[] DEFAULT_PULL_SOURCES = {
+ "AID_SYSTEM",
+ "AID_RADIO"
};
private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
- private String mAdditionalAllowedPackage;
- private final Set<Long> mTrackedMetrics = new HashSet<>();
+ @VisibleForTesting
+ String mDeviceSerial = null;
+ @VisibleForTesting
+ Dumper mDumper = new BasicDumper();
public static void main(String[] args) {
- TestDrive testDrive = new TestDrive();
- Set<Integer> trackedAtoms = new HashSet<>();
+ final Configuration configuration = new Configuration();
+ final TestDrive testDrive = new TestDrive();
Utils.setUpLogger(LOGGER, false);
- String remoteConfigPath = null;
- if (args.length < 1) {
- LOGGER.log(Level.SEVERE, "Usage: ./test_drive [-p additional_allowed_package] "
- + "<atomId1> <atomId2> ... <atomIdN>");
+ if (!testDrive.processArgs(configuration, args,
+ Utils.getDeviceSerials(LOGGER), Utils.getDefaultDevice(LOGGER))) {
return;
}
- if (args.length >= 3 && args[0].equals("-p")) {
- testDrive.mAdditionalAllowedPackage = args[1];
+ final ConfigMetricsReportList reports = testDrive.testDriveAndGetReports(
+ configuration.createConfig(), configuration.hasPulledAtoms(),
+ configuration.hasPushedAtoms());
+ if (reports != null) {
+ configuration.dumpMetrics(reports, testDrive.mDumper);
+ }
+ }
+
+ boolean processArgs(Configuration configuration, String[] args, List<String> connectedDevices,
+ String defaultDevice) {
+ if (args.length < 1) {
+ LOGGER.severe("Usage: ./test_drive [-one] "
+ + "[-p additional_allowed_package] "
+ + "[-s DEVICE_SERIAL_NUMBER] "
+ + "<atomId1> <atomId2> ... <atomIdN>");
+ return false;
}
- for (int i = testDrive.mAdditionalAllowedPackage == null ? 0 : 2; i < args.length; i++) {
+ int first_arg = 0;
+ // Consume all flags, which must precede all atoms
+ for (; first_arg < args.length; ++first_arg) {
+ String arg = args[first_arg];
+ int remaining_args = args.length - first_arg;
+ if (remaining_args >= 2 && arg.equals("-one")) {
+ LOGGER.info("Creating one event metric to catch all pushed atoms.");
+ configuration.mOnePushedAtomEvent = true;
+ } else if (remaining_args >= 2 && arg.equals("-terse")) {
+ LOGGER.info("Terse output format.");
+ mDumper = new TerseDumper();
+ } else if (remaining_args >= 3 && arg.equals("-p")) {
+ configuration.mAdditionalAllowedPackage = args[++first_arg];
+ } else if (remaining_args >= 3 && arg.equals("-s")) {
+ mDeviceSerial = args[++first_arg];
+ } else {
+ break; // Found the atom list
+ }
+ }
+
+ mDeviceSerial = Utils.chooseDevice(mDeviceSerial, connectedDevices, defaultDevice, LOGGER);
+ if (mDeviceSerial == null) {
+ return false;
+ }
+
+ for ( ; first_arg < args.length; ++first_arg) {
+ String atom = args[first_arg];
try {
- int atomId = Integer.valueOf(args[i]);
- if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
- LOGGER.log(Level.SEVERE, "No such atom found: " + args[i]);
- continue;
- }
- trackedAtoms.add(atomId);
+ configuration.addAtom(Integer.valueOf(atom));
} catch (NumberFormatException e) {
- LOGGER.log(Level.SEVERE, "Bad atom id provided: " + args[i]);
- continue;
+ LOGGER.severe("Bad atom id provided: " + atom);
}
}
+ return configuration.hasPulledAtoms() || configuration.hasPushedAtoms();
+ }
+
+ private ConfigMetricsReportList testDriveAndGetReports(StatsdConfig config,
+ boolean hasPulledAtoms, boolean hasPushedAtoms) {
+ if (config == null) {
+ LOGGER.severe("Failed to create valid config.");
+ return null;
+ }
+
+ String remoteConfigPath = null;
try {
- StatsdConfig config = testDrive.createConfig(trackedAtoms);
- if (config == null) {
- LOGGER.log(Level.SEVERE, "Failed to create valid config.");
- return;
- }
- remoteConfigPath = testDrive.pushConfig(config);
- LOGGER.info("Pushed the following config to statsd:");
+ remoteConfigPath = pushConfig(config, mDeviceSerial);
+ LOGGER.info("Pushed the following config to statsd on device '" + mDeviceSerial
+ + "':");
LOGGER.info(config.toString());
- if (!hasPulledAtom(trackedAtoms)) {
+ if (hasPushedAtoms) {
+ LOGGER.info("Now please play with the device to trigger the event.");
+ }
+ if (!hasPulledAtoms) {
LOGGER.info(
- "Now please play with the device to trigger the event. All events should "
- + "be dumped after 1 min ...");
+ "All events should be dumped after 1 min ...");
Thread.sleep(60_000);
} else {
- LOGGER.info("Now wait for 1.5 minutes ...");
+ LOGGER.info("All events should be dumped after 1.5 minutes ...");
Thread.sleep(15_000);
- Utils.logAppBreadcrumb(0, 0, LOGGER);
+ Utils.logAppBreadcrumb(0, 0, LOGGER, mDeviceSerial);
Thread.sleep(75_000);
}
- testDrive.dumpMetrics();
+ return Utils.getReportList(CONFIG_ID, true, false, LOGGER,
+ mDeviceSerial);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e);
} finally {
- testDrive.removeConfig();
+ removeConfig(mDeviceSerial);
if (remoteConfigPath != null) {
try {
- Utils.runCommand(null, LOGGER, "adb", "shell", "rm", remoteConfigPath);
+ Utils.runCommand(null, LOGGER,
+ "adb", "-s", mDeviceSerial, "shell", "rm",
+ remoteConfigPath);
} catch (Exception e) {
LOGGER.log(Level.WARNING,
"Unable to remove remote config file: " + remoteConfigPath, e);
}
}
}
+ return null;
}
- private void dumpMetrics() throws Exception {
- ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER);
- // We may get multiple reports. Take the last one.
- ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
- for (StatsLogReport statsLog : report.getMetricsList()) {
- if (mTrackedMetrics.contains(statsLog.getMetricId())) {
- LOGGER.info(statsLog.toString());
+ static class Configuration {
+ boolean mOnePushedAtomEvent = false;
+ @VisibleForTesting
+ Set<Integer> mPushedAtoms = new TreeSet<>();
+ @VisibleForTesting
+ Set<Integer> mPulledAtoms = new TreeSet<>();
+ @VisibleForTesting
+ String mAdditionalAllowedPackage = null;
+ private final Set<Long> mTrackedMetrics = new HashSet<>();
+
+ private void dumpMetrics(ConfigMetricsReportList reportList, Dumper dumper) {
+ // We may get multiple reports. Take the last one.
+ ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
+ for (StatsLogReport statsLog : report.getMetricsList()) {
+ if (isTrackedMetric(statsLog.getMetricId())) {
+ dumper.dump(statsLog);
+ }
}
}
- }
- private StatsdConfig createConfig(Set<Integer> atomIds) {
- long metricId = METRIC_ID_BASE;
- long atomMatcherId = ATOM_MATCHER_ID_BASE;
-
- ArrayList<String> allowedSources = new ArrayList<>();
- Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES);
- if (mAdditionalAllowedPackage != null) {
- allowedSources.add(mAdditionalAllowedPackage);
+ boolean isTrackedMetric(long metricId) {
+ return mTrackedMetrics.contains(metricId);
}
- StatsdConfig.Builder builder = StatsdConfig.newBuilder();
- builder
- .addAllAllowedLogSource(allowedSources)
- .setHashStringsInMetricReport(false);
-
- if (hasPulledAtom(atomIds)) {
- builder.addAtomMatcher(
- createAtomMatcher(
- Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, APP_BREADCRUMB_MATCHER_ID));
+ static boolean isPulledAtom(int atomId) {
+ return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG
+ || atomId >= VENDOR_PULLED_ATOM_START_TAG;
}
- for (int atomId : atomIds) {
- if (isPulledAtom(atomId)) {
+ void addAtom(Integer atom) {
+ if (Atom.getDescriptor().findFieldByNumber(atom) == null) {
+ LOGGER.severe("No such atom found: " + atom);
+ return;
+ }
+ if (isPulledAtom(atom)) {
+ mPulledAtoms.add(atom);
+ } else {
+ mPushedAtoms.add(atom);
+ }
+ }
+
+ private boolean hasPulledAtoms() {
+ return !mPulledAtoms.isEmpty();
+ }
+
+ private boolean hasPushedAtoms() {
+ return !mPushedAtoms.isEmpty();
+ }
+
+ StatsdConfig createConfig() {
+ long metricId = METRIC_ID_BASE;
+ long atomMatcherId = ATOM_MATCHER_ID_BASE;
+
+ StatsdConfig.Builder builder = baseBuilder();
+
+ if (hasPulledAtoms()) {
+ builder.addAtomMatcher(
+ createAtomMatcher(
+ Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
+ APP_BREADCRUMB_MATCHER_ID));
+ }
+
+ for (int atomId : mPulledAtoms) {
builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder();
gaugeMetricBuilder
- .setId(metricId)
- .setWhat(atomMatcherId)
- .setTriggerEvent(APP_BREADCRUMB_MATCHER_ID)
- .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
- .setBucket(TimeUnit.ONE_MINUTE)
- .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
- .setMaxNumGaugeAtomsPerBucket(100);
+ .setId(metricId)
+ .setWhat(atomMatcherId)
+ .setTriggerEvent(APP_BREADCRUMB_MATCHER_ID)
+ .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
+ .setBucket(TimeUnit.ONE_MINUTE)
+ .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
+ .setMaxNumGaugeAtomsPerBucket(100);
builder.addGaugeMetric(gaugeMetricBuilder.build());
- } else {
- EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
- eventMetricBuilder
- .setId(metricId)
- .setWhat(atomMatcherId);
- builder.addEventMetric(eventMetricBuilder.build());
- builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
+ atomMatcherId++;
+ mTrackedMetrics.add(metricId++);
}
- atomMatcherId++;
- mTrackedMetrics.add(metricId++);
+
+ // A simple atom matcher for each pushed atom.
+ List<AtomMatcher> simpleAtomMatchers = new ArrayList<>();
+ for (int atomId : mPushedAtoms) {
+ final AtomMatcher atomMatcher = createAtomMatcher(atomId, atomMatcherId++);
+ simpleAtomMatchers.add(atomMatcher);
+ builder.addAtomMatcher(atomMatcher);
+ }
+
+ if (mOnePushedAtomEvent) {
+ // Create a union event metric, using an matcher that matches all pulled atoms.
+ AtomMatcher unionAtomMatcher = createUnionMatcher(simpleAtomMatchers,
+ atomMatcherId);
+ builder.addAtomMatcher(unionAtomMatcher);
+ EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
+ eventMetricBuilder.setId(metricId).setWhat(unionAtomMatcher.getId());
+ builder.addEventMetric(eventMetricBuilder.build());
+ mTrackedMetrics.add(metricId++);
+ } else {
+ // Create multiple event metrics, one per pulled atom.
+ for (AtomMatcher atomMatcher : simpleAtomMatchers) {
+ EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
+ eventMetricBuilder
+ .setId(metricId)
+ .setWhat(atomMatcher.getId());
+ builder.addEventMetric(eventMetricBuilder.build());
+ mTrackedMetrics.add(metricId++);
+ }
+ }
+
+ return builder.build();
}
- return builder.build();
+
+ private static AtomMatcher createAtomMatcher(int atomId, long matcherId) {
+ AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
+ atomMatcherBuilder
+ .setId(matcherId)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
+ return atomMatcherBuilder.build();
+ }
+
+ private AtomMatcher createUnionMatcher(List<AtomMatcher> simpleAtomMatchers,
+ long atomMatcherId) {
+ AtomMatcher.Combination.Builder combinationBuilder =
+ AtomMatcher.Combination.newBuilder();
+ combinationBuilder.setOperation(StatsdConfigProto.LogicalOperation.OR);
+ for (AtomMatcher matcher : simpleAtomMatchers) {
+ combinationBuilder.addMatcher(matcher.getId());
+ }
+ AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
+ atomMatcherBuilder.setId(atomMatcherId).setCombination(combinationBuilder.build());
+ return atomMatcherBuilder.build();
+ }
+
+ private StatsdConfig.Builder baseBuilder() {
+ ArrayList<String> allowedSources = new ArrayList<>();
+ Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES);
+ if (mAdditionalAllowedPackage != null) {
+ allowedSources.add(mAdditionalAllowedPackage);
+ }
+ return StatsdConfig.newBuilder()
+ .addAllAllowedLogSource(allowedSources)
+ .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES))
+ .addPullAtomPackages(PullAtomPackages.newBuilder()
+ .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER)
+ .addPackages("AID_GPU_SERVICE"))
+ .addPullAtomPackages(PullAtomPackages.newBuilder()
+ .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER)
+ .addPackages("AID_GPU_SERVICE"))
+ .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);
+ }
}
- private static AtomMatcher createAtomMatcher(int atomId, long matcherId) {
- AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
- atomMatcherBuilder
- .setId(matcherId)
- .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
- return atomMatcherBuilder.build();
+ interface Dumper {
+ void dump(StatsLogReport report);
}
- private static String pushConfig(StatsdConfig config) throws IOException, InterruptedException {
+ static class BasicDumper implements Dumper {
+ @Override
+ public void dump(StatsLogReport report) {
+ System.out.println(report.toString());
+ }
+ }
+
+ static class TerseDumper extends BasicDumper {
+ @Override
+ public void dump(StatsLogReport report) {
+ if (report.hasGaugeMetrics()) {
+ dumpGaugeMetrics(report);
+ }
+ if (report.hasEventMetrics()) {
+ dumpEventMetrics(report);
+ }
+ }
+ void dumpEventMetrics(StatsLogReport report) {
+ final List<StatsLog.EventMetricData> data = report.getEventMetrics().getDataList();
+ if (data.isEmpty()) {
+ return;
+ }
+ long firstTimestampNanos = data.get(0).getElapsedTimestampNanos();
+ for (StatsLog.EventMetricData event : data) {
+ final double deltaSec = (event.getElapsedTimestampNanos() - firstTimestampNanos)
+ / 1e9;
+ System.out.println(
+ String.format("+%.3fs: %s", deltaSec, event.getAtom().toString()));
+ }
+ }
+ void dumpGaugeMetrics(StatsLogReport report) {
+ final List<StatsLog.GaugeMetricData> data = report.getGaugeMetrics().getDataList();
+ if (data.isEmpty()) {
+ return;
+ }
+ for (StatsLog.GaugeMetricData gauge : data) {
+ System.out.println(gauge.toString());
+ }
+ }
+ }
+
+ private static String pushConfig(StatsdConfig config, String deviceSerial)
+ throws IOException, InterruptedException {
File configFile = File.createTempFile("statsdconfig", ".config");
configFile.deleteOnExit();
Files.write(config.toByteArray(), configFile);
String remotePath = "/data/local/tmp/" + configFile.getName();
- Utils.runCommand(null, LOGGER, "adb", "push", configFile.getAbsolutePath(), remotePath);
- Utils.runCommand(null, LOGGER,
- "adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
+ Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
+ "push", configFile.getAbsolutePath(), remotePath);
+ Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
+ "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
String.valueOf(CONFIG_ID));
return remotePath;
}
- private static void removeConfig() {
+ private static void removeConfig(String deviceSerial) {
try {
- Utils.runCommand(null, LOGGER,
- "adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
+ Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
+ "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
} catch (Exception e) {
- LOGGER.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
+ LOGGER.severe("Failed to remove config: " + e.getMessage());
}
}
-
- private static boolean isPulledAtom(int atomId) {
- return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG
- || atomId >= VENDOR_PULLED_ATOM_START_TAG;
- }
-
- private static boolean hasPulledAtom(Set<Integer> atoms) {
- for (Integer i : atoms) {
- if (isPulledAtom(i)) {
- return true;
- }
- }
- return false;
- }
}
diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java
new file mode 100644
index 0000000..b1cc60f
--- /dev/null
+++ b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java
@@ -0,0 +1,326 @@
+/*
+ * 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.
+ */
+
+package com.android.statsd.shelltools.testdrive;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.internal.os.StatsdConfigProto;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link TestDrive}
+ */
+public class ConfigurationTest {
+
+ private StatsdConfigProto.AtomMatcher findAndRemoveAtomMatcherById(
+ List<StatsdConfigProto.AtomMatcher> atomMatchers, long id) {
+ int numMatches = 0;
+ StatsdConfigProto.AtomMatcher match = null;
+ for (StatsdConfigProto.AtomMatcher atomMatcher : atomMatchers) {
+ if (id == atomMatcher.getId()) {
+ ++numMatches;
+ match = atomMatcher;
+ }
+ }
+ if (numMatches == 1) {
+ atomMatchers.remove(match);
+ return match;
+ }
+ return null; // Too many, or not found
+ }
+
+ private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration();
+
+ @Test
+ public void testOnePushed() {
+ final int atom = 90;
+ assertFalse(TestDrive.Configuration.isPulledAtom(atom));
+ mConfiguration.addAtom(atom);
+ StatsdConfig config = mConfiguration.createConfig();
+
+ //event_metric {
+ // id: 1111
+ // what: 1234567
+ //}
+ //atom_matcher {
+ // id: 1234567
+ // simple_atom_matcher {
+ // atom_id: 90
+ // }
+ //}
+
+ assertEquals(1, config.getEventMetricCount());
+ assertEquals(0, config.getGaugeMetricCount());
+
+ assertTrue(mConfiguration.isTrackedMetric(config.getEventMetric(0).getId()));
+
+ final List<StatsdConfigProto.AtomMatcher> atomMatchers =
+ new ArrayList<>(config.getAtomMatcherList());
+ assertEquals(atom,
+ findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(0, atomMatchers.size());
+ }
+
+ @Test
+ public void testOnePulled() {
+ final int atom = 10022;
+ assertTrue(TestDrive.Configuration.isPulledAtom(atom));
+ mConfiguration.addAtom(atom);
+ StatsdConfig config = mConfiguration.createConfig();
+
+ //gauge_metric {
+ // id: 1111
+ // what: 1234567
+ // gauge_fields_filter {
+ // include_all: true
+ // }
+ // bucket: ONE_MINUTE
+ // sampling_type: FIRST_N_SAMPLES
+ // max_num_gauge_atoms_per_bucket: 100
+ // trigger_event: 1111111
+ //}
+ //atom_matcher {
+ // id: 1111111
+ // simple_atom_matcher {
+ // atom_id: 47
+ // }
+ //}
+ //atom_matcher {
+ // id: 1234567
+ // simple_atom_matcher {
+ // atom_id: 10022
+ // }
+ //}
+
+ assertEquals(0, config.getEventMetricCount());
+ assertEquals(1, config.getGaugeMetricCount());
+
+ assertTrue(mConfiguration.isTrackedMetric(config.getGaugeMetric(0).getId()));
+
+ final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0);
+ assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll());
+
+ final List<StatsdConfigProto.AtomMatcher> atomMatchers =
+ new ArrayList<>(config.getAtomMatcherList());
+ assertEquals(atom,
+ findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
+ findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(0, atomMatchers.size());
+ }
+
+ @Test
+ public void testOnePulledTwoPushed() {
+ final int pulledAtom = 10022;
+ assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom));
+ mConfiguration.addAtom(pulledAtom);
+
+ Integer[] pushedAtoms = new Integer[]{244, 245};
+ for (int atom : pushedAtoms) {
+ assertFalse(TestDrive.Configuration.isPulledAtom(atom));
+ mConfiguration.addAtom(atom);
+ }
+ StatsdConfig config = mConfiguration.createConfig();
+
+ // event_metric {
+ // id: 1111
+ // what: 1234567
+ // }
+ // event_metric {
+ // id: 1112
+ // what: 1234568
+ // }
+ // gauge_metric {
+ // id: 1114
+ // what: 1234570
+ // gauge_fields_filter {
+ // include_all: true
+ // }
+ // bucket: ONE_MINUTE
+ // sampling_type: FIRST_N_SAMPLES
+ // max_num_gauge_atoms_per_bucket: 100
+ // trigger_event: 1111111
+ // }
+ // atom_matcher {
+ // id: 1111111
+ // simple_atom_matcher {
+ // atom_id: 47
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234567
+ // simple_atom_matcher {
+ // atom_id: 244
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234568
+ // simple_atom_matcher {
+ // atom_id: 245
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234570
+ // simple_atom_matcher {
+ // atom_id: 10022
+ // }
+ // }
+
+ assertEquals(2, config.getEventMetricCount());
+ assertEquals(1, config.getGaugeMetricCount());
+
+ final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0);
+ assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId()));
+ assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll());
+ for (StatsdConfigProto.EventMetric eventMetric : config.getEventMetricList()) {
+ assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId()));
+ }
+
+ final List<StatsdConfigProto.AtomMatcher> atomMatchers =
+ new ArrayList<>(config.getAtomMatcherList());
+
+ assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
+ findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent())
+ .getSimpleAtomMatcher().getAtomId());
+
+ Integer[] actualAtoms = new Integer[]{
+ findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat())
+ .getSimpleAtomMatcher().getAtomId(),
+ findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(1).getWhat())
+ .getSimpleAtomMatcher().getAtomId()};
+ Arrays.sort(actualAtoms);
+ assertArrayEquals(pushedAtoms, actualAtoms);
+
+ assertEquals(0, atomMatchers.size());
+ }
+
+ @Test
+ public void testOnePulledTwoPushedTogether() {
+ mConfiguration.mOnePushedAtomEvent = true; // Use one event grabbing all pushed atoms
+
+ final int pulledAtom = 10022;
+ assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom));
+ mConfiguration.addAtom(pulledAtom);
+
+ Integer[] pushedAtoms = new Integer[]{244, 245};
+ for (int atom : pushedAtoms) {
+ assertFalse(TestDrive.Configuration.isPulledAtom(atom));
+ mConfiguration.addAtom(atom);
+ }
+ StatsdConfig config = mConfiguration.createConfig();
+
+ // event_metric {
+ // id: 1112
+ // what: 1234570
+ // }
+ // gauge_metric {
+ // id: 1111
+ // what: 1234567
+ // gauge_fields_filter {
+ // include_all: true
+ // }
+ // bucket: ONE_MINUTE
+ // sampling_type: FIRST_N_SAMPLES
+ // max_num_gauge_atoms_per_bucket: 100
+ // trigger_event: 1111111
+ // }
+ // atom_matcher {
+ // id: 1111111
+ // simple_atom_matcher {
+ // atom_id: 47
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234567
+ // simple_atom_matcher {
+ // atom_id: 10022
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234568
+ // simple_atom_matcher {
+ // atom_id: 244
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234569
+ // simple_atom_matcher {
+ // atom_id: 245
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234570
+ // combination {
+ // operation: OR
+ // matcher: 1234568
+ // matcher: 1234569
+ // }
+ // }
+
+ assertEquals(1, config.getEventMetricCount());
+ assertEquals(1, config.getGaugeMetricCount());
+
+ final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0);
+ assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId()));
+ assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll());
+
+ StatsdConfigProto.EventMetric eventMetric = config.getEventMetric(0);
+ assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId()));
+
+ final List<StatsdConfigProto.AtomMatcher> atomMatchers =
+ new ArrayList<>(config.getAtomMatcherList());
+
+ assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
+ findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent())
+ .getSimpleAtomMatcher().getAtomId());
+
+ StatsdConfigProto.AtomMatcher unionMatcher = findAndRemoveAtomMatcherById(atomMatchers,
+ eventMetric.getWhat());
+ assertNotNull(unionMatcher.getCombination());
+ assertEquals(2, unionMatcher.getCombination().getMatcherCount());
+
+ Integer[] actualAtoms = new Integer[]{
+ findAndRemoveAtomMatcherById(atomMatchers,
+ unionMatcher.getCombination().getMatcher(0))
+ .getSimpleAtomMatcher().getAtomId(),
+ findAndRemoveAtomMatcherById(atomMatchers,
+ unionMatcher.getCombination().getMatcher(1))
+ .getSimpleAtomMatcher().getAtomId()};
+ Arrays.sort(actualAtoms);
+ assertArrayEquals(pushedAtoms, actualAtoms);
+
+ assertEquals(0, atomMatchers.size());
+ }
+}
diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java
new file mode 100644
index 0000000..363fac0
--- /dev/null
+++ b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+package com.android.statsd.shelltools.testdrive;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for {@link TestDrive}
+ */
+@RunWith(Parameterized.class)
+public class TestDriveTest {
+ /**
+ * Expected results of a single iteration of the paramerized test.
+ */
+ static class Expect {
+ public boolean success;
+ public Integer[] atoms;
+ public boolean onePushedAtomEvent = false;
+ public String extraPackage = null;
+ public String target;
+ public boolean terse = false;
+
+ static Expect success(Integer... atoms) {
+ return new Expect(true, atoms,
+ TARGET);
+ }
+ Expect(boolean success, Integer[] atoms, String target) {
+ this.success = success;
+ this.atoms = atoms;
+ this.target = target;
+ }
+ static final Expect FAILURE = new Expect(false, null, null);
+ Expect onePushedAtomEvent() {
+ this.onePushedAtomEvent = true;
+ return this;
+ }
+ Expect extraPackage() {
+ this.extraPackage = TestDriveTest.PACKAGE;
+ return this;
+ }
+ Expect terse() {
+ this.terse = true;
+ return this;
+ }
+ }
+
+ @Parameterized.Parameter(0)
+ public String[] mArgs;
+
+ @Parameterized.Parameter(1)
+ public List<String> mConnectedDevices;
+
+ @Parameterized.Parameter(2)
+ public String mDefaultDevice;
+
+ @Parameterized.Parameter(3)
+ public Expect mExpect;
+
+ private static final String TARGET = "target";
+ private static final List<String> TARGET_AND_OTHER = Arrays.asList("otherDevice",
+ TARGET);
+ private static final List<String> TWO_OTHER_DEVICES = Arrays.asList(
+ "other1", "other2");
+ private static final List<String> TARGET_ONLY = Collections.singletonList(TARGET);
+ private static final List<String> NOT_TARGET = Collections.singletonList("other");
+ private static final List<String> NO_DEVICES = Collections.emptyList();
+ private static final String PACKAGE = "extraPackage";
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> data() {
+ return Arrays.asList(
+ new Object[]{new String[]{}, null, null,
+ Expect.FAILURE}, // Usage explanation
+ new Object[]{new String[]{"244", "245"}, null, null,
+ Expect.FAILURE}, // Failure looking up connected devices
+ new Object[]{new String[]{"244", "245"}, NO_DEVICES, null,
+ Expect.FAILURE}, // No connected devices
+ new Object[]{new String[]{"-s", TARGET, "244", "245"}, NOT_TARGET, null,
+ Expect.FAILURE}, // Wrong device connected
+ new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, null,
+ Expect.FAILURE}, // Wrong devices connected
+ new Object[]{new String[]{"244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245)}, // If only one device connected, guess that one
+ new Object[]{new String[]{"244", "not_an_atom"}, TARGET_ONLY, null,
+ Expect.success(244)}, // Ignore non-atoms
+ new Object[]{new String[]{"not_an_atom"}, TARGET_ONLY, null,
+ Expect.FAILURE}, // Require at least one atom
+ new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, TARGET,
+ Expect.FAILURE}, // ANDROID_SERIAL specifies non-connected target
+ new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, TARGET,
+ Expect.success(244, 245)}, // ANDROID_SERIAL specifies a valid target
+ new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, null,
+ Expect.FAILURE}, // Two connected devices, no indication of which to use
+ new Object[]{new String[]{"-one", "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).onePushedAtomEvent()},
+ new Object[]{new String[]{"-terse", "-one", "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).onePushedAtomEvent().terse()},
+ new Object[]{new String[]{"-one", "-terse", "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).onePushedAtomEvent().terse()},
+ new Object[]{new String[]{"-p", PACKAGE, "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).extraPackage()},
+ new Object[]{new String[]{"-p", PACKAGE, "-one", "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
+ new Object[]{new String[]{"-one", "-p", PACKAGE, "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
+ new Object[]{new String[]{"-s", TARGET, "-one", "-p", PACKAGE, "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
+ new Object[]{new String[]{"-one", "-s", TARGET, "-p", PACKAGE, "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
+ new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
+ new Object[]{new String[]{"-terse", "-one", "-p", PACKAGE, "-s", TARGET,
+ "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()},
+ new Object[]{new String[]{"-one", "-terse", "-p", PACKAGE, "-s", TARGET,
+ "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()},
+ new Object[]{new String[]{"-one", "-p", PACKAGE, "-terse", "-s", TARGET,
+ "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()},
+ new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "-terse",
+ "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent().terse()}
+ );
+ }
+
+ private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration();
+ private final TestDrive mTestDrive = new TestDrive();
+
+ private static Integer[] collectAtoms(TestDrive.Configuration configuration) {
+ Integer[] result = new Integer[configuration.mPulledAtoms.size()
+ + configuration.mPushedAtoms.size()];
+ int result_index = 0;
+ for (Integer atom : configuration.mPushedAtoms) {
+ result[result_index++] = atom;
+ }
+ for (Integer atom : configuration.mPulledAtoms) {
+ result[result_index++] = atom;
+ }
+ Arrays.sort(result);
+ return result;
+ }
+
+ @Test
+ public void testProcessArgs() {
+ boolean result = mTestDrive.processArgs(mConfiguration, mArgs, mConnectedDevices,
+ mDefaultDevice);
+ if (mExpect.success) {
+ assertTrue(result);
+ assertArrayEquals(mExpect.atoms, collectAtoms(mConfiguration));
+ assertEquals(mExpect.onePushedAtomEvent, mConfiguration.mOnePushedAtomEvent);
+ assertEquals(mExpect.target, mTestDrive.mDeviceSerial);
+ if (mExpect.terse) {
+ assertEquals(TestDrive.TerseDumper.class, mTestDrive.mDumper.getClass());
+ } else {
+ assertEquals(TestDrive.BasicDumper.class, mTestDrive.mDumper.getClass());
+ }
+ } else {
+ assertFalse(result);
+ }
+ }
+}
diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java
index de6e885..2ed2678 100644
--- a/cmds/svc/src/com/android/commands/svc/Svc.java
+++ b/cmds/svc/src/com/android/commands/svc/Svc.java
@@ -93,7 +93,7 @@
public static final Command[] COMMANDS = new Command[] {
COMMAND_HELP,
new PowerCommand(),
- new WifiCommand(),
+ // `svc wifi` has been migrated to WifiShellCommand
new UsbCommand(),
new NfcCommand(),
new BluetoothCommand(),
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index 3893be4..cd751f4 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -42,7 +42,9 @@
+ " Sets the functions which, if the device was charging, become current on"
+ "screen unlock. If function is blank, turn off this feature.\n"
+ " svc usb getFunctions\n"
- + " Gets the list of currently enabled functions\n\n"
+ + " Gets the list of currently enabled functions\n"
+ + " svc usb resetUsbGadget\n"
+ + " Reset usb gadget\n\n"
+ "possible values of [function] are any of 'mtp', 'ptp', 'rndis', 'midi'\n";
}
@@ -75,6 +77,13 @@
System.err.println("Error communicating with UsbManager: " + e);
}
return;
+ } else if ("resetUsbGadget".equals(args[1])) {
+ try {
+ usbMgr.resetUsbGadget();
+ } catch (RemoteException e) {
+ System.err.println("Error communicating with UsbManager: " + e);
+ }
+ return;
}
}
System.err.println(longHelp());
diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
deleted file mode 100644
index e31cb53..0000000
--- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-package com.android.commands.svc;
-
-import android.os.ServiceManager;
-import android.os.RemoteException;
-import android.net.wifi.IWifiManager;
-import android.content.Context;
-
-public class WifiCommand extends Svc.Command {
- public WifiCommand() {
- super("wifi");
- }
-
- public String shortHelp() {
- return "Control the Wi-Fi manager";
- }
-
- public String longHelp() {
- return shortHelp() + "\n"
- + "\n"
- + "usage: svc wifi [enable|disable]\n"
- + " Turn Wi-Fi on or off.\n\n";
- }
-
- public void run(String[] args) {
- boolean validCommand = false;
- if (args.length >= 2) {
- boolean flag = false;
- if ("enable".equals(args[1])) {
- flag = true;
- validCommand = true;
- } else if ("disable".equals(args[1])) {
- flag = false;
- validCommand = true;
- }
- if (validCommand) {
- IWifiManager wifiMgr
- = IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
- if (wifiMgr == null) {
- System.err.println("Wi-Fi service is not ready");
- return;
- }
- try {
- wifiMgr.setWifiEnabled("com.android.shell", flag);
- }
- catch (RemoteException e) {
- System.err.println("Wi-Fi operation failed: " + e);
- }
- return;
- }
- }
- System.err.println(longHelp());
- }
-}
diff --git a/cmds/svc/svc b/cmds/svc/svc
index 7431aea..95265e8 100755
--- a/cmds/svc/svc
+++ b/cmds/svc/svc
@@ -1,5 +1,24 @@
#!/system/bin/sh
+# `svc wifi` has been migrated to WifiShellCommand,
+# simply perform translation to `cmd wifi set-wifi-enabled` here.
+if [ "x$1" == "xwifi" ]; then
+ # `cmd wifi` by convention uses enabled/disabled
+ # instead of enable/disable
+ if [ "x$2" == "xenable" ]; then
+ exec cmd wifi set-wifi-enabled enabled
+ elif [ "x$2" == "xdisable" ]; then
+ exec cmd wifi set-wifi-enabled disabled
+ else
+ echo "Control the Wi-Fi manager"
+ echo ""
+ echo "usage: svc wifi [enable|disable]"
+ echo " Turn Wi-Fi on or off."
+ echo ""
+ fi
+ exit 1
+fi
+
if [ "x$1" == "xdata" ]; then
if [ "x$2" == "xenable" ]; then
exec cmd phone data enable
@@ -16,3 +35,4 @@
export CLASSPATH=/system/framework/svc.jar
exec app_process /system/bin com.android.commands.svc.Svc "$@"
+
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 24d65fb..5f13a5c 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -46,6 +46,10 @@
* @param args The command-line arguments
*/
public static void main(String[] args) {
+ // Initialize the telephony module.
+ // TODO: Do it in zygote and RuntimeInit. b/148897549
+ ActivityThread.initializeMainlineModules();
+
(new Telecom()).run(args);
}
diff --git a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
index c35f7fc..3b14be7 100644
--- a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
+++ b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java
@@ -16,6 +16,7 @@
package com.android.commands.uiautomator;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.UiAutomation;
import android.graphics.Point;
import android.hardware.display.DisplayManagerGlobal;
@@ -61,11 +62,14 @@
public void run(String[] args) {
File dumpFile = DEFAULT_DUMP_FILE;
boolean verboseMode = true;
+ boolean allWindows = false;
for (String arg : args) {
if (arg.equals("--compressed"))
verboseMode = false;
- else if (!arg.startsWith("-")) {
+ else if (arg.equals("--windows")) {
+ allWindows = true;
+ } else if (!arg.startsWith("-")) {
dumpFile = new File(arg);
}
}
@@ -85,18 +89,28 @@
try {
UiAutomation uiAutomation = automationWrapper.getUiAutomation();
uiAutomation.waitForIdle(1000, 1000 * 10);
- AccessibilityNodeInfo info = uiAutomation.getRootInActiveWindow();
- if (info == null) {
- System.err.println("ERROR: null root node returned by UiTestAutomationBridge.");
- return;
- }
+ if (allWindows) {
+ AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+ info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+ uiAutomation.setServiceInfo(info);
+ AccessibilityNodeInfoDumper.dumpWindowsToFile(
+ uiAutomation.getWindowsOnAllDisplays(), dumpFile,
+ DisplayManagerGlobal.getInstance());
+ } else {
+ AccessibilityNodeInfo info = uiAutomation.getRootInActiveWindow();
+ if (info == null) {
+ System.err.println("ERROR: null root node returned by UiTestAutomationBridge.");
+ return;
+ }
- Display display =
- DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
- int rotation = display.getRotation();
- Point size = new Point();
- display.getSize(size);
- AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x, size.y);
+ Display display =
+ DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
+ int rotation = display.getRotation();
+ Point size = new Point();
+ display.getSize(size);
+ AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x,
+ size.y);
+ }
} catch (TimeoutException re) {
System.err.println("ERROR: could not get idle state.");
return;
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index 63c51e8..ab198b3 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -16,11 +16,17 @@
package com.android.uiautomator.core;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
import android.os.Environment;
import android.os.SystemClock;
import android.util.Log;
+import android.util.SparseArray;
import android.util.Xml;
+import android.view.Display;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
import org.xmlpull.v1.XmlSerializer;
@@ -28,6 +34,7 @@
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
+import java.util.List;
/**
*
@@ -98,6 +105,95 @@
Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms");
}
+ /**
+ * Using {@link AccessibilityWindowInfo} this method will dump some window information and
+ * then walk the layout hierarchy of it's
+ * and generates an xml dump to the location specified by <code>dumpFile</code>
+ * @param allWindows All windows indexed by display-id.
+ * @param dumpFile The file to dump to.
+ */
+ public static void dumpWindowsToFile(SparseArray<List<AccessibilityWindowInfo>> allWindows,
+ File dumpFile, DisplayManagerGlobal displayManager) {
+ if (allWindows.size() == 0) {
+ return;
+ }
+ final long startTime = SystemClock.uptimeMillis();
+ try {
+ FileWriter writer = new FileWriter(dumpFile);
+ XmlSerializer serializer = Xml.newSerializer();
+ StringWriter stringWriter = new StringWriter();
+ serializer.setOutput(stringWriter);
+ serializer.startDocument("UTF-8", true);
+ serializer.startTag("", "displays");
+ for (int d = 0, nd = allWindows.size(); d < nd; ++d) {
+ int displayId = allWindows.keyAt(d);
+ Display display = displayManager.getRealDisplay(displayId);
+ if (display == null) {
+ continue;
+ }
+ final List<AccessibilityWindowInfo> windows = allWindows.valueAt(d);
+ if (windows.isEmpty()) {
+ continue;
+ }
+ serializer.startTag("", "display");
+ serializer.attribute("", "id", Integer.toString(displayId));
+ int rotation = display.getRotation();
+ Point size = new Point();
+ display.getSize(size);
+ for (int i = 0, n = windows.size(); i < n; ++i) {
+ dumpWindowRec(windows.get(i), serializer, i, size.x, size.y, rotation);
+ }
+ serializer.endTag("", "display");
+ }
+ serializer.endTag("", "displays");
+ serializer.endDocument();
+ writer.write(stringWriter.toString());
+ writer.close();
+ } catch (IOException e) {
+ Log.e(LOGTAG, "failed to dump window to file", e);
+ }
+ final long endTime = SystemClock.uptimeMillis();
+ Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms");
+ }
+
+ private static void dumpWindowRec(AccessibilityWindowInfo winfo, XmlSerializer serializer,
+ int index, int width, int height, int rotation) throws IOException {
+ serializer.startTag("", "window");
+ serializer.attribute("", "index", Integer.toString(index));
+ final CharSequence title = winfo.getTitle();
+ serializer.attribute("", "title", title != null ? title.toString() : "");
+ final Rect tmpBounds = new Rect();
+ winfo.getBoundsInScreen(tmpBounds);
+ serializer.attribute("", "bounds", tmpBounds.toShortString());
+ serializer.attribute("", "active", Boolean.toString(winfo.isActive()));
+ serializer.attribute("", "focused", Boolean.toString(winfo.isFocused()));
+ serializer.attribute("", "accessibility-focused",
+ Boolean.toString(winfo.isAccessibilityFocused()));
+ serializer.attribute("", "id", Integer.toString(winfo.getId()));
+ serializer.attribute("", "layer", Integer.toString(winfo.getLayer()));
+ serializer.attribute("", "type", AccessibilityWindowInfo.typeToString(winfo.getType()));
+ int count = winfo.getChildCount();
+ for (int i = 0; i < count; ++i) {
+ AccessibilityWindowInfo child = winfo.getChild(i);
+ if (child == null) {
+ Log.i(LOGTAG, String.format("Null window child %d/%d, parent: %s", i, count,
+ winfo.getTitle()));
+ continue;
+ }
+ dumpWindowRec(child, serializer, i, width, height, rotation);
+ child.recycle();
+ }
+ AccessibilityNodeInfo root = winfo.getRoot();
+ if (root != null) {
+ serializer.startTag("", "hierarchy");
+ serializer.attribute("", "rotation", Integer.toString(rotation));
+ dumpNodeRec(root, serializer, 0, width, height);
+ root.recycle();
+ serializer.endTag("", "hierarchy");
+ }
+ serializer.endTag("", "window");
+ }
+
private static void dumpNodeRec(AccessibilityNodeInfo node, XmlSerializer serializer,int index,
int width, int height) throws IOException {
serializer.startTag("", "node");
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index 455e4bb..b23bf5d 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -67,7 +67,7 @@
throw new IllegalStateException("Could not find provider: " + providerName);
}
provider = holder.provider;
- cursor = provider.query(null, Settings.Secure.CONTENT_URI,
+ cursor = provider.query(null, null, Settings.Secure.CONTENT_URI,
new String[] {
Settings.Secure.VALUE
},