diff options
Diffstat (limited to 'cmds')
223 files changed, 11389 insertions, 12232 deletions
diff --git a/cmds/bmgr/TEST_MAPPING b/cmds/bmgr/TEST_MAPPING new file mode 100644 index 000000000000..7c0e79e7692d --- /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 680ccfc57ddf..ed717c491467 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 @@ public class Bmgr { 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 @@ public class Bmgr { return; } + if ("autorestore".equals(op)) { + doAutoRestore(userId); + return; + } + if ("enabled".equals(op)) { doEnabled(userId); return; @@ -190,21 +199,45 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { showUsage(); return; } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -259,8 +289,7 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { } } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -569,8 +592,7 @@ public class Bmgr { } }); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); return; } @@ -598,8 +620,7 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { mRestore.endRestoreSession(); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -686,8 +705,7 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { } } } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -886,8 +902,7 @@ public class Bmgr { + " for user " + userId); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -928,6 +943,7 @@ public class Bmgr { 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 @@ public class Bmgr { 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/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index db384baff4d7..8fac31a05c49 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -288,6 +288,48 @@ status_t BootAnimation::readyToRun() { dinfo.w, dinfo.h, 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 boot.animation.displays 19260422155234049,19261083906282754 + Vector<uint64_t> physicalDisplayIds; + char displayValue[PROPERTY_VALUE_MAX] = ""; + property_get("boot.animation.displays", 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: boot.animation.displays"); + } + 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(); @@ -1071,7 +1113,7 @@ void BootAnimation::handleViewport(nsecs_t timestep) { 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; @@ -1115,10 +1157,10 @@ BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn) parseAnimationDesc(*animation); if (!preloadZip(*animation)) { + releaseAnimation(animation); return nullptr; } - mLoadedFiles.remove(fn); return animation; } diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java index 55dbc17dba5d..59544a971e5f 100644 --- a/cmds/content/src/com/android/commands/content/Content.java +++ b/cmds/content/src/com/android/commands/content/Content.java @@ -508,7 +508,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - provider.insert(resolveCallingPackage(), mUri, mContentValues); + provider.insert(resolveCallingPackage(), null, mUri, mContentValues, null); } } @@ -522,7 +522,8 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - provider.delete(resolveCallingPackage(), mUri, mWhere, null); + provider.delete(resolveCallingPackage(), null, mUri, + ContentResolver.createSqlQueryBundle(mWhere, null)); } } @@ -557,7 +558,7 @@ public class Content { @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 +585,7 @@ public class Content { @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,7 +598,7 @@ public class Content { @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()); } } @@ -616,7 +617,7 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection, + Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection, ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null); if (cursor == null) { System.out.println("No result found."); @@ -679,7 +680,8 @@ public class Content { @Override public void onExecute(IContentProvider provider) throws Exception { - provider.update(resolveCallingPackage(), mUri, mContentValues, mWhere, null); + provider.update(resolveCallingPackage(), null, mUri, mContentValues, + ContentResolver.createSqlQueryBundle(mWhere, null)); } } diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java index 6c6797a328c9..d0c2a24d5314 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 @@ public final class Dpm extends BaseCommand { 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 @@ public final class Dpm extends BaseCommand { "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 @@ public final class Dpm extends BaseCommand { 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 @@ public final class Dpm extends BaseCommand { } - 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/jni/Android.bp b/cmds/hid/jni/Android.bp index 095cfc6ceb53..2c07de04b6a7 100644 --- a/cmds/hid/jni/Android.bp +++ b/cmds/hid/jni/Android.bp @@ -5,6 +5,7 @@ cc_library_shared { 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 d4fdf85491d3..f56dd6e4968e 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 const char* UHID_PATH = "/dev/uhid"; static struct { jmethodID onDeviceOpen; jmethodID onDeviceGetReport; + jmethodID onDeviceOutput; jmethodID onDeviceError; } gDeviceCallbackClassInfo; @@ -61,6 +66,26 @@ static void checkAndClearException(JNIEnv* env, const char* methodName) { } } +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 @@ void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) { 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, + 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,8 +146,7 @@ Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid, 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])); + 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.vendor = vid; @@ -126,7 +157,6 @@ Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid, 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 +164,21 @@ Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid, // 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 +192,14 @@ Device::~Device() { 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 +212,7 @@ void Device::sendReport(const std::vector<uint8_t>& report) const { 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 +223,7 @@ void Device::sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& 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 +240,37 @@ int Device::handleEvents(int events) { 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; @@ -250,9 +304,10 @@ static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint i 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, desc, + std::move(cb)); + return reinterpret_cast<jlong>(d.release()); } static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray rawReport) { @@ -304,6 +359,8 @@ int register_com_android_commands_hid_Device(JNIEnv* env) { 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 892c7cd12953..93ea881cfe28 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 @@ public: 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 @@ private: 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, + 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 @@ public: 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 616d411ef7bb..874604ceb5e4 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.Handler; 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 @@ public class Device { 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(); @@ -55,12 +59,13 @@ public class Device { private static native void nativeCloseDevice(long ptr); public Device(int id, String name, int vid, int pid, byte[] descriptor, - byte[] report, SparseArray<byte[]> featureReports) { + 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; @@ -160,6 +165,11 @@ public class Device { } 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 +186,29 @@ public class Device { 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 746e37289076..62587a70f10d 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.JsonToken; 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"; @@ -41,6 +44,7 @@ public class Event { private int mPid; private byte[] mReport; private SparseArray<byte[]> mFeatureReports; + private Map<ByteBuffer, byte[]> mOutputs; private int mDuration; public int getId() { @@ -75,6 +79,10 @@ public class Event { return mFeatureReports; } + public Map<ByteBuffer, byte[]> getOutputs() { + return mOutputs; + } + public int getDuration() { return mDuration; } @@ -88,6 +96,7 @@ public class Event { + ", pid=" + mPid + ", report=" + Arrays.toString(mReport) + ", feature_reports=" + mFeatureReports.toString() + + ", outputs=" + mOutputs.toString() + ", duration=" + mDuration + "}"; } @@ -123,6 +132,10 @@ public class Event { mEvent.mFeatureReports = reports; } + public void setOutputs(Map<ByteBuffer, byte[]> outputs) { + mEvent.mOutputs = outputs; + } + public void setVid(int vid) { mEvent.mVid = vid; } @@ -199,6 +212,9 @@ public class Event { case "feature_reports": eb.setFeatureReports(readFeatureReports()); break; + case "outputs": + eb.setOutputs(readOutputs()); + break; case "duration": eb.setDuration(readInt()); break; @@ -250,7 +266,7 @@ public class Event { 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 +292,60 @@ public class Event { } } 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); + } + 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); - } finally { - return featureReports; } + return outputs; } private void consumeRemainingElements() throws IOException { @@ -296,10 +355,6 @@ public class Event { } } - 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 54ac1b0733ff..0ee2cc45932f 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"; @@ -119,7 +114,7 @@ public class Hid { } int id = e.getId(); Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), - e.getDescriptor(), e.getReport(), e.getFeatureReports()); + 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 ae5d74a47000..000000000000 --- 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 f415f8f5dd75..000000000000 --- 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 8f86ed8f7d32..000000000000 --- 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 5962108c9f7e..000000000000 --- 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 20005e2766d8..000000000000 --- 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 847dda3df91f..000000000000 --- 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 4c77ba402595..41a17064c3ba 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -43,9 +43,10 @@ cc_library { "libidmap2/Policies.cpp", "libidmap2/PrettyPrintVisitor.cpp", "libidmap2/RawPrintVisitor.cpp", + "libidmap2/ResourceMapping.cpp", "libidmap2/ResourceUtils.cpp", "libidmap2/Result.cpp", - "libidmap2/Xml.cpp", + "libidmap2/XmlParser.cpp", "libidmap2/ZipFile.cpp", ], export_include_dirs: ["include"], @@ -97,9 +98,10 @@ cc_test { "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: [ diff --git a/cmds/idmap2/CPPLINT.cfg b/cmds/idmap2/CPPLINT.cfg index 9dc6b4a77380..20ed43c2a76a 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/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index bb8d92737563..3ff6d3514331 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -50,7 +50,7 @@ Result<Unit> Create(const std::vector<std::string>& args) { std::string overlay_apk_path; std::string idmap_path; std::vector<std::string> policies; - bool ignore_overlayable; + bool ignore_overlayable = false; const CommandLineOptions opts = CommandLineOptions("idmap2 create") @@ -99,8 +99,8 @@ Result<Unit> Create(const std::vector<std::string>& args) { 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/Dump.cpp b/cmds/idmap2/idmap2/Dump.cpp index 8716bf313ed0..47f442aab235 100644 --- a/cmds/idmap2/idmap2/Dump.cpp +++ b/cmds/idmap2/idmap2/Dump.cpp @@ -39,7 +39,7 @@ using android::idmap2::Unit; Result<Unit> Dump(const std::vector<std::string>& args) { SYSTRACE << "Dump " << args; std::string idmap_path; - bool verbose; + bool verbose = false; const CommandLineOptions opts = CommandLineOptions("idmap2 dump") diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index b7ae9d090cee..c5cf9807b689 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::IdmapHeader; 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 { @@ -132,29 +132,6 @@ Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId res 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 +179,12 @@ Result<Unit> Lookup(const std::vector<std::string>& args) { } 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(), diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index da8c06e5f74f..b4fdd0b8a94d 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -33,7 +33,7 @@ #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -#include "idmap2/Xml.h" +#include "idmap2/XmlParser.h" #include "idmap2/ZipFile.h" using android::idmap2::CommandLineOptions; @@ -98,6 +98,7 @@ Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector } 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 +107,7 @@ std::vector<std::string> PoliciesForPath(const std::string& apk_path) { {"/system_ext/", kPolicySystem}, {"/vendor/", kPolicyVendor}, }; + // clang-format on std::vector<std::string> fulfilled_policies = {kPolicyPublic}; for (auto const& pair : values) { @@ -178,11 +180,11 @@ Result<Unit> Scan(const std::vector<std::string>& args) { // 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; } } diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index 8ee79f61520a..4aabf8399a25 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -137,8 +137,8 @@ Status Idmap2Service::createIdmap(const std::string& target_apk_path, 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()); } diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index 1aab0598449b..94d2af49260c 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -34,18 +34,19 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { } 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); + 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::unique_ptr<std::string>* _aidl_return); + std::unique_ptr<std::string>* _aidl_return) override; }; } // namespace android::os diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h index 2c3e9d321881..ff45b1407dea 100644 --- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h +++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h @@ -29,16 +29,19 @@ class BinaryStreamVisitor : public Visitor { 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 ebbb5ffc989d..d4a0c3221c20 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_ @@ -56,20 +74,14 @@ #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" #include "idmap2/Policies.h" +#include "idmap2/ResourceMapping.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 @@ -110,6 +122,10 @@ class IdmapHeader { 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. @@ -127,11 +143,11 @@ class IdmapHeader { uint32_t overlay_crc_; 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 +158,72 @@ class IdmapData { return target_package_id_; } - inline uint16_t GetTypeCount() const { - return type_count_; + inline PackageId GetOverlayPackageId() const { + return overlay_package_id_; } - void accept(Visitor* v) const; - - private: - Header() { + inline uint32_t GetTargetEntryCount() const { + return target_entry_count; } - PackageId target_package_id_; - uint16_t type_count_; - - friend Idmap; - DISALLOW_COPY_AND_ASSIGN(Header); - }; - - class TypeEntry { - public: - static std::unique_ptr<const TypeEntry> FromBinaryStream(std::istream& stream); - - inline TypeId GetTargetTypeId() const { - return target_type_id_; + inline uint32_t GetOverlayEntryCount() const { + return overlay_entry_count; } - inline TypeId GetOverlayTypeId() const { - return overlay_type_id_; + inline uint32_t GetStringPoolIndexOffset() const { + return string_pool_index_offset; } - 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; + inline uint32_t GetStringPoolLength() const { + return string_pool_len; } void accept(Visitor* v) const; private: - TypeEntry() { - } - - TypeId target_type_id_; - TypeId overlay_type_id_; - uint16_t entry_offset_; - std::vector<EntryId> entries_; + PackageId target_package_id_; + 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; - DISALLOW_COPY_AND_ASSIGN(TypeEntry); + friend IdmapData; + DISALLOW_COPY_AND_ASSIGN(Header); + }; + + struct TargetEntry { + ResourceId target_id; + TargetValue::DataType data_type; + TargetValue::DataValue data_value; + }; + + 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 +233,9 @@ class IdmapData { } 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 +252,7 @@ class Idmap { // 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 +285,6 @@ class Visitor { 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 000000000000..a6237e6f6ba9 --- /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/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h index 5111bb2eaab2..5dcf217e2aa3 100644 --- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h @@ -33,17 +33,16 @@ class PrettyPrintVisitor : public Visitor { 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 2e543d4fabdd..92c186453611 100644 --- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h @@ -34,22 +34,25 @@ class RawPrintVisitor : public Visitor { 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 000000000000..86dfab20e448 --- /dev/null +++ b/cmds/idmap2/include/idmap2/ResourceMapping.h @@ -0,0 +1,134 @@ +/* + * 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; + +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 9a0c2abced5a..de1dbc90eb2d 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -21,19 +21,32 @@ #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 { + +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 +54,8 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, 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 dd89dee0f64f..000000000000 --- 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 000000000000..972a6d7e3427 --- /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 dee2d219cbe1..362dcb36007a 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 @@ void BinaryStreamVisitor::Write32(uint32_t value) { 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,33 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) { Write32(header.GetVersion()); Write32(header.GetTargetCrc()); Write32(header.GetOverlayCrc()); - WriteString(header.GetTargetPath()); - WriteString(header.GetOverlayPath()); + 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); + } -void BinaryStreamVisitor::visit(const IdmapData::Header& header) { - Write16(header.GetTargetPackageId()); - Write16(header.GetTypeCount()); -} + for (const auto& overlay_entry : data.GetOverlayEntries()) { + Write32(overlay_entry.overlay_id); + Write32(overlay_entry.target_id); + } -void BinaryStreamVisitor::visit(const IdmapData::TypeEntry& type_entry) { - const uint16_t entryCount = type_entry.GetEntryCount(); + Write(data.GetStringPoolData(), data.GetHeader()->GetStringPoolLength()); +} - 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); - } +void BinaryStreamVisitor::visit(const IdmapData::Header& header) { + 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 4649675965db..7f2cd9596c95 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 android::idmap2 { 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 @@ bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) { 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,26 +83,21 @@ bool WARN_UNUSED ReadString(std::istream& stream, char out[kIdmapStringLength]) 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"); + } + if (size == 0) { + return std::string(""); } - int id = packages[0]->GetPackageId(); - return loaded_arsc.GetPackageById(id); + 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) { @@ -134,11 +115,17 @@ std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& s 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_)) { + !ReadString256(stream, idmap_header->target_path_) || + !ReadString256(stream, idmap_header->overlay_path_)) { return nullptr; } + auto debug_str = ReadString(stream); + if (!debug_str) { + return nullptr; + } + idmap_header->debug_info_ = std::move(*debug_str); + return std::move(idmap_header); } @@ -187,51 +174,48 @@ Result<Unit> IdmapHeader::IsUpToDate() const { 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->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->type_entries_.push_back(std::move(type)); + 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 +250,45 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromBinaryStream(std::istream& strea 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; -} - -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"); + 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}); } - 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"); + for (const auto& mappings : resource_mapping.GetOverlayToTargetMap()) { + data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mappings.first, mappings.second}); } - 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()); - } + 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(); - // 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()); - } + 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); - return Result<Unit>({}); + data->header_ = std::move(data_header); + return {std::move(data)}; } -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,11 +300,6 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar 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; @@ -395,78 +324,34 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar 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()); - std::unique_ptr<Idmap> idmap(new Idmap()); - 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); + auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path); + if (!overlay_info) { + return overlay_info.GetError(); } - if (matching_resources.Map().empty()) { - return Error("overlay \"%s\" does not successfully overlay any resource", - overlay_apk_path.c_str()); + 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(); } - // 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)); + auto idmap_data = IdmapData::FromResourceMapping(*resource_mapping); + if (!idmap_data) { + return idmap_data.GetError(); } - 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)); + std::unique_ptr<Idmap> idmap(new Idmap()); + header->debug_info_ = log_info.GetString(); + idmap->header_ = std::move(header); + idmap->data_.push_back(std::move(*idmap_data)); return {std::move(idmap)}; } @@ -481,25 +366,16 @@ void IdmapData::Header::accept(Visitor* v) const { 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/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp index fbf2c777be9a..63ee8a648352 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 @@ namespace android::idmap2 { #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 dd14fd47aea8..751c60c4add4 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -16,6 +16,7 @@ #include "idmap2/RawPrintVisitor.h" +#include <algorithm> #include <cstdarg> #include <string> @@ -27,6 +28,15 @@ using android::ApkAssets; +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 @@ -40,53 +50,102 @@ void RawPrintVisitor::visit(const IdmapHeader& header) { 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.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(); -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(); -} + 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"); + } -void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) { - const bool target_package_loaded = !target_am_.GetApkAssets().empty(); + print(target_entry.data_type, "type: %s", + utils::DataTypeToString(target_entry.data_type).data()); - 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"); + 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); + } - for (uint16_t i = 0; i < type_entry.GetEntryCount(); i++) { - const EntryId entry = type_entry.GetEntry(i); - if (entry == kNoEntry) { - print(kPadding, "no entry"); + if (overlay_name) { + print(overlay_entry.overlay_id, "overlay id: %s", overlay_name->c_str()); } 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); - } + 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(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"); +} + +// 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); + + stream_ << base::StringPrintf("%08zx: %02x", offset_, value) << " " << comment + << std::endl; + + offset_ += sizeof(uint8_t); } // NOLINTNEXTLINE(cert-dcl50-cpp) @@ -116,17 +175,30 @@ void RawPrintVisitor::print(uint32_t value, const char* fmt, ...) { } // 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 000000000000..407478945151 --- /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 "idmap2/ResourceUtils.h" + +using android::base::StringPrintf; +using android::idmap2::utils::ResToTypeEntryName; + +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::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(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 = + (overlay_resource->dataType == Res_value::TYPE_REFERENCE || + overlay_resource->dataType == Res_value::TYPE_DYNAMIC_REFERENCE) + ? 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) { + if (enforce_overlayable) { + log_info.Info(LogMessage() << "fulfilled_policies=" + << ConcatPolicies(BitmaskToPolicies(fulfilled_policies)) + << " enforce_overlayable=" + << (enforce_overlayable ? "true" : "false")); + } + + AssetManager2 target_asset_manager; + if (!target_asset_manager.SetApkAssets({&target_apk_assets}, true /* invalidate_caches */, + false /* filter_incompatible_configs*/)) { + 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) { + // Load the overlay resource mappings from the file specified using android:resourcesMap. + auto asset = OpenNonAssetFromResource(overlay_info.resource_mapping, 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 && + (data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) { + 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 (value.data_type != Res_value::TYPE_REFERENCE && + value.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) { + 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 dce83e35978d..a5df746ca733 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -22,18 +22,52 @@ #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) { +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 +99,72 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, 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()); + } + + if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) { + info.target_name = *result_str; } - iter = tag->find("targetName"); - if (iter != tag->end()) { - info.target_name = iter->second; + if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) { + if ((*result_value).dataType == Res_value::TYPE_REFERENCE) { + 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("isStatic"); - if (iter != tag->end()) { - info.is_static = std::stoul(iter->second) != 0U; + 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("priority"); - if (iter != tag->end()) { - info.priority = std::stoi(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("requiredSystemPropertyName"); - if (iter != tag->end()) { - info.requiredSystemPropertyName = iter->second; + if (auto result_str = overlay_it->GetAttributeStringValue("requiredSystemPropertyName")) { + info.requiredSystemPropertyName = *result_str; } - iter = tag->find("requiredSystemPropertyValue"); - if (iter != tag->end()) { - info.requiredSystemPropertyValue = iter->second; + 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 264586829c47..000000000000 --- 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 000000000000..526a560907aa --- /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 4f5e3a45f183..1e1a218163f0 100644 --- a/cmds/idmap2/libidmap2/ZipFile.cpp +++ b/cmds/idmap2/libidmap2/ZipFile.cpp @@ -34,6 +34,7 @@ std::unique_ptr<const ZipFile> ZipFile::Open(const std::string& path) { ::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/static-checks.sh b/cmds/idmap2/static-checks.sh index 41d3c69540b2..a372abdaa146 100755 --- a/cmds/idmap2/static-checks.sh +++ b/cmds/idmap2/static-checks.sh @@ -27,10 +27,11 @@ function _eval() 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 9348ab721493..db4778c8ee09 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" @@ -52,113 +53,39 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) { 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]; - - 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)); -} - -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 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()); - - const auto idmap = - Idmap::FromApkAssets(target_apk_path, *target_apk, overlay_apk_path, *overlay_apk, - PolicyFlags::POLICY_PUBLIC, /* enforce_overlayable */ true); - ASSERT_TRUE(idmap); - - 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 IdmapEntry_header* header = loaded_idmap->GetEntryMapForType(0x01); - ASSERT_THAT(header, NotNull()); - - 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); + 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()); - success = LoadedIdmap::Lookup(header, 0x000c, &entry); // string/str1 - ASSERT_TRUE(success); - ASSERT_EQ(entry, 0x0000); + 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()); - success = LoadedIdmap::Lookup(header, 0x000d, &entry); // string/str2 - ASSERT_FALSE(success); + 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); - success = LoadedIdmap::Lookup(header, 0x000e, &entry); // string/str3 - ASSERT_TRUE(success); - ASSERT_EQ(entry, 0x0001); + ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id); + ASSERT_EQ(target_entries1[1].data_value, target_entries2[1].data_value); - success = LoadedIdmap::Lookup(header, 0x000f, &entry); // string/str4 - ASSERT_TRUE(success); - ASSERT_EQ(entry, 0x0002); + ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id); + ASSERT_EQ(target_entries1[2].data_value, target_entries2[2].data_value); - success = LoadedIdmap::Lookup(header, 0x0010, &entry); // string/x - ASSERT_FALSE(success); + 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); - success = LoadedIdmap::Lookup(header, 0x0011, &entry); // string/y - ASSERT_FALSE(success); + ASSERT_EQ(overlay_entries1[1].overlay_id, overlay_entries2[1].overlay_id); + ASSERT_EQ(overlay_entries1[1].target_id, overlay_entries2[1].target_id); - 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/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index 8a48f4b8e6d5..b535f30de1f5 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -131,7 +131,6 @@ TEST_F(Idmap2BinaryTests, Dump) { 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_EQ(result->stdout.find("00000210: 007f target package id"), std::string::npos); // clang-format off result = ExecuteBinary({"idmap2", @@ -142,7 +141,6 @@ TEST_F(Idmap2BinaryTests, Dump) { 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", diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 0f47f1e77d7b..4bc625565144 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -30,12 +30,23 @@ #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; 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,11 +58,12 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream); ASSERT_THAT(header, NotNull()); ASSERT_EQ(header->GetMagic(), 0x504d4449U); - ASSERT_EQ(header->GetVersion(), 0x01U); + ASSERT_EQ(header->GetVersion(), 0x03U); 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->GetTargetPath().to_string(), "targetX.apk"); + ASSERT_EQ(header->GetOverlayPath().to_string(), "overlayX.apk"); + ASSERT_EQ(header->GetDebugInfo(), "debug"); } TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) { @@ -66,58 +78,40 @@ TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) { } TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { - const size_t offset = 0x210; + const size_t offset = 0x21c; 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 = 0x21c; 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); - - 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& 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); + + 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 +124,29 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x03U); 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()->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_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); - - 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); + ASSERT_THAT(data, NotNull()); + + 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); + + 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 +158,141 @@ TEST(IdmapTests, GracefullyFailToCreateIdmapFromCorruptBinaryStream) { 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()); +TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) { + std::string target_apk_path = GetTestDataPath() + "/target/target.apk"; + 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.to_string()); + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); 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; - 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); + auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_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()->GetVersion(), 0x03U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x76a20829); - ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed); + ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xc054fb26); 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; - 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); - ASSERT_THAT(idmap, NotNull()); - - const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); - ASSERT_EQ(dataBlocks.size(), 1U); - - const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; - - ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); - ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); - - const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); - ASSERT_EQ(types.size(), 1U); - - ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); - ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 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 -} - -TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) { - std::unique_ptr<const Idmap> idmap; - 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); - ASSERT_THAT(idmap, NotNull()); - - const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); - ASSERT_EQ(dataBlocks.size(), 1U); - - const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; - - ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); - ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); - - const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); - ASSERT_EQ(types.size(), 1U); - - ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U); - ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); - ASSERT_EQ(types[0]->GetEntryCount(), 1U); - ASSERT_EQ(types[0]->GetEntryOffset(), 9U); - ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_signature -} - -// 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()); - - const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); - ASSERT_EQ(dataBlocks.size(), 1U); +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::unique_ptr<const IdmapData>& data = dataBlocks[0]; + 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()); + } - ASSERT_EQ(data->GetHeader()->GetTargetPackageId(), 0x7fU); - ASSERT_EQ(data->GetHeader()->GetTypeCount(), 1U); + LogInfo log_info; + auto mapping = ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, + fulfilled_policies, enforce_overlayable, log_info); - const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); - ASSERT_EQ(types.size(), 1U); + if (!mapping) { + return mapping.GetError(); + } - 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 + return IdmapData::FromResourceMapping(*mapping); } -// Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled. -TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) { - std::unique_ptr<const Idmap> idmap; +TEST(IdmapTests, CreateIdmapDataFromApkAssets) { 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()); - - const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData(); - ASSERT_EQ(dataBlocks.size(), 1U); + std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay.apk"; - const std::unique_ptr<const IdmapData>& data = dataBlocks[0]; + std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); + ASSERT_THAT(target_apk, NotNull()); - 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 -} + std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); + ASSERT_THAT(overlay_apk, NotNull()); -// 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); + auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_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(), 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 + const auto& target_entries = data->GetTargetEntries(); + ASSERT_EQ(target_entries.size(), 4U); + ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f010000); + ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020000); + ASSERT_TARGET_ENTRY(target_entries[2], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001); + ASSERT_TARGET_ENTRY(target_entries[3], 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020002); + + const auto& overlay_entries = data->GetOverlayEntries(); + ASSERT_EQ(target_entries.size(), 4U); + ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f010000, 0x7f010000); + ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x7f020000, 0x7f02000c); + ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x7f020001, 0x7f02000e); + ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x7f020002, 0x7f02000f); } -// 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()); +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::POLICY_PUBLIC, + /* enforce_overlayable */ false); + + ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage(); + auto& data = *idmap_data; + + const auto& target_entries = data->GetTargetEntries(); + ASSERT_EQ(target_entries.size(), 2U); + ASSERT_TARGET_ENTRY(target_entries[0], 0x7f02000c, Res_value::TYPE_REFERENCE, + 0x0104000a); // string/str1 -> android:string/ok + ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, + 0x7f020001); // string/str3 -> string/str4 + + const auto& overlay_entries = data->GetOverlayEntries(); + ASSERT_EQ(overlay_entries.size(), 1U); + ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x7f020001, 0x7f02000e); // string/str3 <- string/str4 } -// 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(); +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::POLICY_PUBLIC, + /* enforce_overlayable */ false); + + ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage(); + auto& data = *idmap_data; + + 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], 0x7f010000, Res_value::TYPE_INT_DEC, + 73U); // integer/int1 -> 73 + ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_STRING, + overlay_string_pool_size + 0U); // string/str1 -> "Hello World" + + const auto& overlay_entries = data->GetOverlayEntries(); + ASSERT_EQ(overlay_entries.size(), 0U); } TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) { @@ -480,9 +309,8 @@ TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) { 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::POLICY_PUBLIC, + /* enforce_overlayable */ true); ASSERT_FALSE(result); } @@ -497,8 +325,7 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { 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::POLICY_PUBLIC, /* enforce_overlayable */ true); ASSERT_TRUE(result); const auto idmap = std::move(*result); @@ -605,10 +432,6 @@ class TestVisitor : public Visitor { 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 +448,10 @@ TEST(IdmapTests, TestVisitor) { (*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/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp index c41250457678..1d34e42e188d 100644 --- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp @@ -43,9 +43,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { 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::POLICY_PUBLIC, + /* enforce_overlayable */ true); ASSERT_TRUE(idmap); std::stringstream stream; diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index 26951763cd66..b22fdafb09bb 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -16,6 +16,7 @@ #include <cstdio> // fclose #include <memory> +#include <regex> #include <sstream> #include <string> @@ -29,7 +30,16 @@ using ::testing::NotNull; 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) + 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 +48,32 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { 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::POLICY_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); +#define ADDRESS "[0-9a-f]{8}: " + ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000003 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "76a20829 target crc\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "c054fb26 overlay crc\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()); +#undef ADDRESS } TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { @@ -69,10 +90,21 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { (*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("00000004: 00000003 version\n"), std::string::npos); ASSERT_NE(stream.str().find("00000008: 00001234 target crc\n"), std::string::npos); ASSERT_NE(stream.str().find("0000000c: 00005678 overlay crc\n"), std::string::npos); - ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f020000 -> 0x7f020000\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000021c: 7f target package id\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000021d: 7f overlay package id\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000021e: 00000003 target entry count\n"), std::string::npos); + ASSERT_NE(stream.str().find("00000222: 00000003 overlay entry count\n"), std::string::npos); + ASSERT_NE(stream.str().find("00000226: 00000000 string pool index offset\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000022a: 00000000 string pool byte length\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000022e: 7f020000 target id\n"), std::string::npos); + ASSERT_NE(stream.str().find("00000232: 01 type: reference\n"), std::string::npos); + ASSERT_NE(stream.str().find("00000233: 7f020000 value\n"), std::string::npos); + + ASSERT_NE(stream.str().find("00000249: 7f020000 overlay id\n"), std::string::npos); + ASSERT_NE(stream.str().find("0000024d: 7f020000 target id\n"), std::string::npos); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp new file mode 100644 index 000000000000..39c4937b0930 --- /dev/null +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -0,0 +1,325 @@ +/* + * 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 "TestHelpers.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; + +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::POLICY_PUBLIC, + /* enforce_overlayable */ false); + + ASSERT_TRUE(resources) << resources.GetErrorMessage(); + auto& res = *resources; + ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U); + ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_REFERENCE, 0x7f010000, + false /* rewrite */)); // integer/int1 + ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x7f020000, + false /* rewrite */)); // string/str1 + ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_REFERENCE, 0x7f020001, + false /* rewrite */)); // string/str3 + ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_REFERENCE, 0x7f020002, + false /* rewrite */)); // string/str4 +} + +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::POLICY_PUBLIC, + /* enforce_overlayable */ false); + + ASSERT_TRUE(resources) << resources.GetErrorMessage(); + auto& res = *resources; + ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); + ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020002, + true /* rewrite */)); // string/str1 -> string/str4 + ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020000, + true /* rewrite */)); // string/str3 -> string/str1 + ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001, + true /* rewrite */)); // string/str4 -> string/str3 +} + +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::POLICY_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, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x0104000a, + false /* rewrite */)); // string/str1 -> android:string/ok + ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x7f020001, + true /* rewrite */)); // string/str3 -> string/str4 +} + +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::POLICY_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, 0x7f02000c, Res_value::TYPE_STRING, + overlay_string_pool_size + 0U, + false /* rewrite */)); // string/str1 -> "Hello World" + ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_INT_DEC, 73U, + false /* rewrite */)); // string/str1 -> "Hello World" +} + +TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) { + auto resources = + TestGetResourceMapping("/target/target.apk", "/system-overlay/system-overlay.apk", + PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ true); + + ASSERT_TRUE(resources) << resources.GetErrorMessage(); + auto& res = *resources; + ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); + ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010000, + false /* rewrite */)); // string/policy_public + ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010001, + false /* rewrite */)); // string/policy_system + ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010002, + false /* rewrite */)); // string/policy_system_vendor +} + +// 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::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ true); + + ASSERT_TRUE(resources) << resources.GetErrorMessage(); + auto& res = *resources; + ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); + ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005, + false /* rewrite */)); // string/policy_public + ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007, + false /* rewrite */)); // string/policy_system + ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008, + false /* rewrite */)); // string/policy_system_vendor +} + +// 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::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_PUBLIC, + /* enforce_overlayable */ false); + + ASSERT_TRUE(resources) << resources.GetErrorMessage(); + auto& res = *resources; + ASSERT_EQ(res.GetTargetToOverlayMap().size(), 9U); + ASSERT_RESULT(MappingExists(res, 0x7f020003, Res_value::TYPE_REFERENCE, 0x7f010000, + false /* rewrite */)); // string/not_overlayable + ASSERT_RESULT(MappingExists(res, 0x7f020004, Res_value::TYPE_REFERENCE, 0x7f010001, + false /* rewrite */)); // string/other + ASSERT_RESULT(MappingExists(res, 0x7f020005, Res_value::TYPE_REFERENCE, 0x7f010002, + false /* rewrite */)); // string/policy_odm + ASSERT_RESULT(MappingExists(res, 0x7f020006, Res_value::TYPE_REFERENCE, 0x7f010003, + false /* rewrite */)); // string/policy_oem + ASSERT_RESULT(MappingExists(res, 0x7f020007, Res_value::TYPE_REFERENCE, 0x7f010004, + false /* rewrite */)); // string/policy_product + ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005, + false /* rewrite */)); // string/policy_public + ASSERT_RESULT(MappingExists(res, 0x7f020009, Res_value::TYPE_REFERENCE, 0x7f010006, + false /* rewrite */)); // string/policy_signature + ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007, + false /* rewrite */)); // string/policy_system + ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008, + false /* rewrite */)); // string/policy_system_vendor +} + +// 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::POLICY_PUBLIC, + /* enforce_overlayable */ false); + + ASSERT_TRUE(resources) << resources.GetErrorMessage(); + auto& res = *resources; + ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U); + ASSERT_RESULT(MappingExists(res, 0x7f010000, Res_value::TYPE_REFERENCE, 0x7f010000, + false /* rewrite */)); // integer/int1 + ASSERT_RESULT(MappingExists(res, 0x7f02000c, Res_value::TYPE_REFERENCE, 0x7f020000, + false /* rewrite */)); // string/str1 + ASSERT_RESULT(MappingExists(res, 0x7f02000e, Res_value::TYPE_REFERENCE, 0x7f020001, + false /* rewrite */)); // string/str3 + ASSERT_RESULT(MappingExists(res, 0x7f02000f, Res_value::TYPE_REFERENCE, 0x7f020002, + false /* rewrite */)); // string/str4 +} + +// 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::POLICY_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(), 9U); + ASSERT_RESULT(MappingExists(res, 0x7f020003, Res_value::TYPE_REFERENCE, 0x7f010000, + false /* rewrite */)); // string/not_overlayable + ASSERT_RESULT(MappingExists(res, 0x7f020004, Res_value::TYPE_REFERENCE, 0x7f010001, + false /* rewrite */)); // string/other + ASSERT_RESULT(MappingExists(res, 0x7f020005, Res_value::TYPE_REFERENCE, 0x7f010002, + false /* rewrite */)); // string/policy_odm + ASSERT_RESULT(MappingExists(res, 0x7f020006, Res_value::TYPE_REFERENCE, 0x7f010003, + false /* rewrite */)); // string/policy_oem + ASSERT_RESULT(MappingExists(res, 0x7f020007, Res_value::TYPE_REFERENCE, 0x7f010004, + false /* rewrite */)); // string/policy_product + ASSERT_RESULT(MappingExists(res, 0x7f020008, Res_value::TYPE_REFERENCE, 0x7f010005, + false /* rewrite */)); // string/policy_public + ASSERT_RESULT(MappingExists(res, 0x7f020009, Res_value::TYPE_REFERENCE, 0x7f010006, + false /* rewrite */)); // string/policy_signature + ASSERT_RESULT(MappingExists(res, 0x7f02000a, Res_value::TYPE_REFERENCE, 0x7f010007, + false /* rewrite */)); // string/policy_system + ASSERT_RESULT(MappingExists(res, 0x7f02000b, Res_value::TYPE_REFERENCE, 0x7f010008, + false /* rewrite */)); // string/policy_system_vendor + }; + + CheckEntries(PolicyFlags::POLICY_SIGNATURE); + CheckEntries(PolicyFlags::POLICY_PRODUCT_PARTITION); + CheckEntries(PolicyFlags::POLICY_SYSTEM_PARTITION); + CheckEntries(PolicyFlags::POLICY_VENDOR_PARTITION); + CheckEntries(PolicyFlags::POLICY_ODM_PARTITION); + CheckEntries(PolicyFlags::POLICY_OEM_PARTITION); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h index adea3293534d..e899589c7e61 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -30,7 +30,7 @@ const unsigned char idmap_raw_data[] = { 0x49, 0x44, 0x4d, 0x50, // 0x4: version - 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, // 0x8: target crc 0x34, 0x12, 0x00, 0x00, @@ -38,8 +38,8 @@ const unsigned char idmap_raw_data[] = { // 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: 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 +56,8 @@ const unsigned char idmap_raw_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x110: overlay path "overlay.apk" - 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x110: 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 +74,77 @@ const unsigned char idmap_raw_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // DATA HEADER - // 0x210: target package id - 0x7f, 0x00, + // 0x210: debug string + // string length, including terminating null + 0x08, 0x00, 0x00, 0x00, - // 0x212: types count - 0x02, 0x00, + // string contents "debug\0\0\0" (padded to word alignment) + 0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00, - // DATA BLOCK - // 0x214: target type - 0x02, 0x00, + // DATA HEADER + // 0x21c: target_package_id + 0x7f, - // 0x216: overlay type - 0x02, 0x00, + // 0x21d: overlay_package_id + 0x7f, - // 0x218: entry count - 0x01, 0x00, + // 0x21e: target_entry_count + 0x03, 0x00, 0x00, 0x00, - // 0x21a: entry offset - 0x00, 0x00, + // 0x222: overlay_entry_count + 0x03, 0x00, 0x00, 0x00, - // 0x21c: entries + // 0x226: string_pool_offset 0x00, 0x00, 0x00, 0x00, - // DATA BLOCK - // 0x220: target type - 0x03, 0x00, + // 0x22a: string_pool_byte_length + 0x00, 0x00, 0x00, 0x00, - // 0x222: overlay type - 0x03, 0x00, + // TARGET ENTRIES + // 0x22e: 0x7f020000 + 0x00, 0x00, 0x02, 0x7f, - // 0x224: entry count - 0x03, 0x00, + // 0x232: TYPE_REFERENCE + 0x01, - // 0x226: entry offset - 0x03, 0x00, + // 0x233: 0x7f020000 + 0x00, 0x00, 0x02, 0x7f, - // 0x228, 0x22c, 0x230: entries - 0x00, 0x00, 0x00, 0x00, + // 0x237: 0x7f030000 + 0x00, 0x00, 0x03, 0x7f, + + // 0x23b: TYPE_REFERENCE + 0x01, + + // 0x23c: 0x7f030000 + 0x00, 0x00, 0x03, 0x7f, + + // 0x240: 0x7f030002 + 0x02, 0x00, 0x03, 0x7f, + + // 0x244: TYPE_REFERENCE + 0x01, + + // 0x245: 0x7f030001 + 0x01, 0x00, 0x03, 0x7f, + + // OVERLAY ENTRIES + // 0x249: 0x7f020000 -> 0x7f020000 + 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f, - 0xff, 0xff, 0xff, 0xff, + // 0x251: 0x7f030000 -> 0x7f030000 + 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f, - 0x01, 0x00, 0x00, 0x00}; + // 0x259: 0x7f030001 -> 0x7f030002 + 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f}; -const unsigned int idmap_raw_data_len = 565; +const unsigned int idmap_raw_data_len = 0x261; 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 +157,7 @@ class Idmap2Tests : public testing::Test { 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 000000000000..1a7eaca4d67b --- /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 df63211a9209..000000000000 --- 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 619bb6ce0f25..cf3691c3b3cf 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 68b9f507a11d..b921b0d3d3ad 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 diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk Binary files differindex 18ee43dc57a4..7c25985e5a61 100644 --- a/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk +++ b/cmds/idmap2/tests/data/overlay/overlay-no-name-static.apk diff --git a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk Binary files differindex 642519008b15..c75f3e1dbddf 100644 --- a/cmds/idmap2/tests/data/overlay/overlay-no-name.apk +++ b/cmds/idmap2/tests/data/overlay/overlay-no-name.apk diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk Binary files differindex 642ab90d00ae..5b8a6e4a90ed 100644 --- a/cmds/idmap2/tests/data/overlay/overlay-static-1.apk +++ b/cmds/idmap2/tests/data/overlay/overlay-static-1.apk diff --git a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk Binary files differindex 2ec56020c4aa..698a1fd6e702 100644 --- a/cmds/idmap2/tests/data/overlay/overlay-static-2.apk +++ b/cmds/idmap2/tests/data/overlay/overlay-static-2.apk diff --git a/cmds/idmap2/tests/data/overlay/overlay.apk b/cmds/idmap2/tests/data/overlay/overlay.apk Binary files differindex 5842da4f432e..1db303ff05b5 100644 --- a/cmds/idmap2/tests/data/overlay/overlay.apk +++ b/cmds/idmap2/tests/data/overlay/overlay.apk 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 000000000000..edd33f7dc90d --- /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 000000000000..aa7fefaa305e --- /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 000000000000..e12b823ff50d --- /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 000000000000..5728e672d94a --- /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/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml index 0fe21c6b6d0a..56a3f7f0b13a 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 Binary files differindex 033305aaed4f..2eb7c477c3b4 100644 --- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk +++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk Binary files differindex 9bcd6dcabcde..251cf46f969d 100644 --- a/cmds/idmap2/tests/data/target/target.apk +++ b/cmds/idmap2/tests/data/target/target.apk diff --git a/cmds/idmap2/valgrind.sh b/cmds/idmap2/valgrind.sh new file mode 100755 index 000000000000..b4ebab0c7ffe --- /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/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index f476fcf91bd5..c9277a57bd07 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -476,10 +476,28 @@ status_t DumpsysSection::BlockingCall(int pipeWriteFd) const { // 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: @@ -512,9 +530,8 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const { // 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_RDONLY | ANDROID_LOG_NONBLOCK, 0, 0) - : android_logger_list_alloc_time(ANDROID_LOG_RDONLY | 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) { diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index c9b80563a609..fcf12f7336fd 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -146,8 +146,11 @@ class LogSection : public WorkerThreadSection { // global last log retrieved timestamp for each log_id_t. static map<log_id_t, log_time> gLastLogsRetrieved; + // log mode: read only & non blocking. + const static int logModeBase = ANDROID_LOG_RDONLY | 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; @@ -155,6 +158,7 @@ public: private: log_id_t mLogID; bool mBinary; + int mLogMode; }; /** diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index a0777459311b..08216d9b3f1d 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 @@ public class Input extends BaseCommand { + " (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 000000000000..56f5cc034f05 --- /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 7879c53684a7..000000000000 --- 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 e69de29bb2d1..000000000000 --- a/cmds/media/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/cmds/media/NOTICE b/cmds/media/NOTICE deleted file mode 100644 index c5b1efa7aac7..000000000000 --- 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 00c3915f2e65..000000000000 --- 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 1e915f8232d3..000000000000 --- 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 1629c6f178f0..000000000000 --- 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/statsd/Android.bp b/cmds/statsd/Android.bp index bc190fd293f1..1c6867c39790 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -52,11 +52,6 @@ cc_defaults { ":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 +59,65 @@ cc_defaults { "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/condition/StateConditionTracker.cpp", "src/config/ConfigKey.cpp", "src/config/ConfigListener.cpp", "src/config/ConfigManager.cpp", "src/external/CarStatsPuller.cpp", "src/external/GpuStatsPuller.cpp", "src/external/Perfetto.cpp", - "src/external/StatsPuller.cpp", + "src/external/PowerStatsPuller.cpp", + "src/external/PullResultReceiver.cpp", + "src/external/puller_util.cpp", + "src/external/ResourceHealthManagerPuller.cpp", "src/external/StatsCallbackPuller.cpp", + "src/external/StatsCallbackPullerDeprecated.cpp", "src/external/StatsCompanionServicePuller.cpp", + "src/external/StatsPuller.cpp", + "src/external/StatsPullerManager.cpp", "src/external/SubsystemSleepStatePuller.cpp", - "src/external/PowerStatsPuller.cpp", - "src/external/ResourceHealthManagerPuller.cpp", "src/external/TrainInfoPuller.cpp", - "src/external/StatsPullerManager.cpp", - "src/external/puller_util.cpp", + "src/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/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/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", + ], + + cflags: [ + // "-DNEW_ENCODING_SCHEME", ], local_include_dirs: [ @@ -117,32 +125,29 @@ cc_defaults { ], static_libs: [ + "android.frameworks.stats@1.0", + "android.hardware.power.stats@1.0", + "android.hardware.power@1.0", + "android.hardware.power@1.1", + "libbase", + "libcutils", "libhealthhalutils", + "liblog", "libplatformprotos", + "libprotoutil", + "libstatslog", + "libstatssocket", + "libsysutils", ], - shared_libs: [ - "libbase", + "android.hardware.health@2.0", "libbinder", "libgraphicsenv", + "libhidlbase", "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", + "libutils", ], } @@ -261,57 +266,57 @@ cc_test { "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/condition/StateConditionTracker_test.cpp", "tests/ConfigManager_test.cpp", - "tests/external/puller_util_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/GpuStatsPuller_test.cpp", "tests/external/IncidentReportArgs_test.cpp", + "tests/external/puller_util_test.cpp", + "tests/external/StatsCallbackPuller_test.cpp", "tests/external/StatsPuller_test.cpp", + "tests/FieldValue_test.cpp", + "tests/guardrail/StatsdStats_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/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/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/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/shell/ShellSubscriber_test.cpp", + "tests/UidMap_test.cpp", ], static_libs: [ @@ -324,7 +329,10 @@ cc_test { include_dirs: ["external/protobuf/src"], }, - shared_libs: ["libprotobuf-cpp-lite"], + shared_libs: [ + "libprotobuf-cpp-lite", + "libstatssocket" + ], } @@ -341,17 +349,17 @@ cc_benchmark { // not included in libprotobuf-cpp-lite, so compile it here. ":libprotobuf-internal-protos", - "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/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/duration_metric_benchmark.cpp", + "benchmark/stats_write_benchmark.cpp", + "src/atom_field_options.proto", + "src/atoms.proto", + "src/stats_log.proto", ], proto: { @@ -367,7 +375,7 @@ cc_benchmark { "-Wno-unused-function", // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374 - "-Wno-varargs" + "-Wno-varargs", ], static_libs: [ @@ -376,26 +384,27 @@ cc_benchmark { shared_libs: [ "libgtest_prod", - "libstatslog", "libprotobuf-cpp-lite", + "libstatslog", + "libstatssocket", ], } // ==== java proto device library (for test only) ============================== java_library { name: "statsdprotolite", - sdk_version: "core_platform", + sdk_version: "core_current", proto: { type: "lite", include_dirs: ["external/protobuf/src"], }, 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/OWNERS b/cmds/statsd/OWNERS index 380e499a5abf..04464ce02b4f 100644 --- a/cmds/statsd/OWNERS +++ b/cmds/statsd/OWNERS @@ -3,5 +3,6 @@ joeo@google.com jtnguyen@google.com muhammadq@google.com singhtejinder@google.com +tsaichristine@google.com yaochen@google.com yro@google.com diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp index 26034695906b..bdfdb2e00ac0 100644 --- a/cmds/statsd/benchmark/log_event_benchmark.cpp +++ b/cmds/statsd/benchmark/log_event_benchmark.cpp @@ -16,55 +16,30 @@ #include <vector> #include "benchmark/benchmark.h" #include "logd/LogEvent.h" +#include "stats_event.h" namespace android { namespace os { namespace statsd { -using std::vector; - -/* 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); +static size_t createAndParseStatsEvent(uint8_t* msg) { + struct stats_event* event = stats_event_obtain(); + stats_event_set_atom_id(event, 100); + stats_event_write_int32(event, 2); + stats_event_write_float(event, 2.0); + stats_event_build(event); + + size_t size; + uint8_t* buf = stats_event_get_buffer(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)); + benchmark::DoNotOptimize(LogEvent(msg, size, /*uid=*/ 1000)); } } BENCHMARK(BM_LogEventCreation); diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index 320a32ab4648..4385964f7f0e 100644 --- a/cmds/statsd/src/FieldValue.cpp +++ b/cmds/statsd/src/FieldValue.cpp @@ -116,28 +116,13 @@ void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* ou } 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; - } - } - + bool isUid = isAttributionUidField(value) || isUidField(value.mField, value.mValue); return isUid ? value.mValue.int_value : -1; } @@ -149,6 +134,18 @@ bool isAttributionUidField(const Field& field, const Value& value) { return false; } +bool isUidField(const Field& field, const Value& value) { + auto it = android::util::AtomsInfo::kAtomsWithUidField.find(field.getTag()); + + if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) { + int uidField = it->second; // uidField is the field number in proto + return field.getDepth() == 0 && field.getPosAtDepth(0) == uidField && + value.getType() == INT; + } + + return false; +} + Value::Value(const Value& from) { type = from.getType(); switch (type) { @@ -438,6 +435,25 @@ bool equalDimensions(const std::vector<Matcher>& dimension_a, 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 +480,4 @@ bool HasPositionALL(const FieldMatcher& matcher) { } // 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 6729e052b5ee..967fd323e5a0 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -261,6 +261,11 @@ inline Matcher getSimpleMatcher(int32_t tag, size_t field) { 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. * @@ -392,9 +397,14 @@ int getUidIfExists(const FieldValue& value); void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output); bool isAttributionUidField(const Field& field, const Value& value); +bool isUidField(const Field& field, const Value& value); 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 af8b3af6ea61..109785f649e4 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -59,6 +59,17 @@ android::hash_t hashDimension(const HashableDimensionKey& value) { 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; @@ -96,15 +107,34 @@ void getDimensionForCondition(const std::vector<FieldValue>& eventValues, 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()); } } @@ -122,6 +152,10 @@ bool LessThan(const vector<FieldValue>& s1, const vector<FieldValue>& s2) { 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; @@ -175,11 +209,11 @@ string HashableDimensionKey::toString() const { 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 { @@ -189,7 +223,7 @@ bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const { 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 6f4941f717ee..654e1358f2a1 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -34,6 +34,12 @@ struct Metric2Condition { 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) { @@ -65,6 +71,8 @@ public: std::string toString() const; + bool operator!=(const HashableDimensionKey& that) const; + bool operator==(const HashableDimensionKey& that) const; bool operator<(const HashableDimensionKey& that) const; @@ -76,17 +84,16 @@ private: }; 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 +103,37 @@ class MetricDimensionKey { 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 void setStateValuesKey(const HashableDimensionKey& key) { + mStateValuesKey = key; } - bool hasDimensionKeyInCondition() const { - return mDimensionKeyInCondition.getValues().size() > 0; + 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 @@ -145,6 +159,13 @@ void getDimensionForCondition(const std::vector<FieldValue>& eventValues, 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); + } // namespace statsd } // namespace os } // namespace android @@ -165,8 +186,8 @@ template <> 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 c9e026bf231c..34818145a922 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -16,25 +16,27 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" -#include "statslog.h" + +#include "StatsLogProcessor.h" #include <android-base/file.h> #include <dirent.h> #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> -#include "StatsLogProcessor.h" +#include <log/log_event_list.h> +#include <utils/Errors.h> +#include <utils/SystemClock.h> + #include "android-base/stringprintf.h" #include "atoms_info.h" #include "external/StatsPullerManager.h" #include "guardrail/StatsdStats.h" #include "metrics/CountMetricProducer.h" +#include "state/StateManager.h" #include "stats_log_util.h" #include "stats_util.h" +#include "statslog.h" #include "storage/StorageManager.h" -#include <log/log_event_list.h> -#include <utils/Errors.h> -#include <utils/SystemClock.h> - using namespace android; using android::base::StringPrintf; using android::util::FIELD_COUNT_REPEATED; @@ -199,6 +201,10 @@ void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs) { } void StatsLogProcessor::OnLogEvent(LogEvent* event) { + OnLogEvent(event, getElapsedRealtimeNs()); +} + +void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { std::lock_guard<std::mutex> lock(mMetricsMutex); #ifdef VERY_VERBOSE_PRINTING @@ -206,9 +212,9 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { ALOGI("%s", event->ToString().c_str()); } #endif - const int64_t currentTimestampNs = event->GetElapsedTimestampNs(); + const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs(); - resetIfConfigTtlExpiredLocked(currentTimestampNs); + resetIfConfigTtlExpiredLocked(eventElapsedTimeNs); StatsdStats::getInstance().noteAtomLogged( event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC); @@ -219,6 +225,8 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { onIsolatedUidChangedEventLocked(*event); } + StateManager::getInstance().onLogEvent(*event); + if (mMetricsManagers.empty()) { return; } @@ -261,15 +269,16 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { 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; @@ -279,13 +288,13 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { 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; } } } @@ -320,11 +329,6 @@ void StatsLogProcessor::OnConfigUpdatedLocked( mAnomalyAlarmMonitor, mPeriodicAlarmMonitor); if (newMetricsManager->isConfigValid()) { mUidMap->OnConfigUpdated(key); - if (newMetricsManager->shouldAddUidMapListener()) { - // We have to add listener after the MetricsManager is constructed because it's - // not safe to create wp or sp from this pointer inside its constructor. - mUidMap->addListener(newMetricsManager.get()); - } newMetricsManager->refreshTtl(timestampNs); mMetricsManagers[key] = newMetricsManager; VLOG("StatsdConfig valid"); @@ -547,22 +551,23 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { } } -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) || @@ -577,7 +582,8 @@ void StatsLogProcessor::flushIfNecessaryLocked( // 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; } @@ -585,7 +591,7 @@ void StatsLogProcessor::flushIfNecessaryLocked( 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); } } @@ -743,6 +749,32 @@ int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) { } } +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) { + it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version); + } +} + +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) { + 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) { + it.second->onUidMapReceived(eventTimeNs); + } +} + 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 313e16d19b62..c569bc1e33f7 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -32,7 +32,7 @@ namespace os { namespace statsd { -class StatsLogProcessor : public ConfigListener { +class StatsLogProcessor : public ConfigListener, public virtual PackageInfoListener { public: StatsLogProcessor(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, @@ -91,6 +91,16 @@ public: /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */ void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs); + /* Notify all MetricsManagers of app upgrades */ + void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, + const int64_t version) override; + + /* Notify all MetricsManagers of app removals */ + void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override; + + /* Notify all MetricsManagers of uid map snapshots received */ + void onUidMapReceived(const int64_t& eventTimeNs) override; + // Reset all configs. void resetConfigs(); @@ -147,6 +157,8 @@ private: sp<AlarmMonitor> mPeriodicAlarmMonitor; + void OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs); + void resetIfConfigTtlExpiredLocked(const int64_t timestampNs); void OnConfigUpdatedLocked( @@ -176,8 +188,7 @@ private: /* 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; @@ -248,19 +259,6 @@ private: 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, TestDurationMetric_SUM_single_bucket); @@ -275,12 +273,21 @@ private: FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); + 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(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 64b7aae01619..1ca19c3417c2 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -170,36 +170,36 @@ StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQ 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"); + sp<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) { + sp<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; } }); + mUidMap->setListener(mProcessor); mConfigManager->AddListener(mProcessor); init_system_properties(); @@ -271,7 +271,7 @@ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* rep } return NO_ERROR; } - default: { return BnStatsManager::onTransact(code, data, reply, flags); } + default: { return BnStatsd::onTransact(code, data, reply, flags); } } } @@ -573,18 +573,19 @@ status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) { 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)); + sp<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; } @@ -628,15 +629,15 @@ status_t StatsService::cmd_trigger_active_config_broadcast(int out, Vector<Strin } } } - auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid); - sp<IStatsCompanionService> sc = getStatsCompanionService(); - if (sc == nullptr) { - VLOG("Could not access statsCompanion"); - } else if (receiver == nullptr) { + sp<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; } @@ -861,13 +862,13 @@ status_t StatsService::cmd_log_binary_push(int out, const Vector<String8>& args) int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10); int options = 0; if (args[3] == "1") { - options = options | IStatsManager::FLAG_REQUIRE_STAGING; + options = options | IStatsd::FLAG_REQUIRE_STAGING; } if (args[4] == "1") { - options = options | IStatsManager::FLAG_ROLLBACK_ENABLED; + options = options | IStatsd::FLAG_ROLLBACK_ENABLED; } if (args[5] == "1") { - options = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR; + options = options | IStatsd::FLAG_REQUIRE_LOW_LATENCY_MONITOR; } int32_t state = atoi(args[6].c_str()); vector<int64_t> experimentIds; @@ -1110,7 +1111,6 @@ Status StatsService::statsCompanionReady() { mPullerManager->SetStatsCompanionService(statsCompanion); mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion); mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion); - SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion); return Status::ok(); } @@ -1135,12 +1135,11 @@ void StatsService::OnLogEvent(LogEvent* event) { } } -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*/, @@ -1148,22 +1147,18 @@ Status StatsService::getData(int64_t key, const String16& packageName, vector<ui 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"); @@ -1184,23 +1179,21 @@ bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<ui 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 sp<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()); @@ -1209,62 +1202,55 @@ Status StatsService::setDataFetchOperation(int64_t key, return Status::ok(); } -Status StatsService::setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender, - const String16& packageName, +Status StatsService::setActiveConfigsChangedOperation(const sp<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 sp<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(); @@ -1274,7 +1260,7 @@ 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, + (int32_t) IPCThreadState::self()->getCallingUid(), label, state); return Status::ok(); } @@ -1289,6 +1275,28 @@ Status StatsService::registerPullerCallback(int32_t atomTag, return Status::ok(); } +Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, + int64_t timeoutNs, const std::vector<int32_t>& additiveFields, + const sp<android::os::IPullAtomCallback>& pullerCallback) { + ENFORCE_UID(AID_SYSTEM); + + VLOG("StatsService::registerPullAtomCallback called."); + mPullerManager->RegisterPullAtomCallback(uid, atomTag, coolDownNs, timeoutNs, additiveFields, + pullerCallback); + return Status::ok(); +} + +Status StatsService::registerNativePullAtomCallback(int32_t atomTag, int64_t coolDownNs, + int64_t timeoutNs, const std::vector<int32_t>& additiveFields, + const sp<android::os::IPullAtomCallback>& pullerCallback) { + + VLOG("StatsService::registerNativePullAtomCallback called."); + int32_t uid = IPCThreadState::self()->getCallingUid(); + mPullerManager->RegisterPullAtomCallback(uid, atomTag, coolDownNs, timeoutNs, additiveFields, + pullerCallback); + return Status::ok(); +} + Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& packageName) { ENFORCE_DUMP_AND_USAGE_STATS(packageName); @@ -1297,6 +1305,13 @@ Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& p return Status::ok(); } +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::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn, const int64_t trainVersionCodeIn, const int options, @@ -1383,9 +1398,9 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra 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; + bool requiresStaging = options & IStatsd::FLAG_REQUIRE_STAGING; + bool rollbackEnabled = options & IStatsd::FLAG_ROLLBACK_ENABLED; + bool requiresLowLatencyMonitor = options & IStatsd::FLAG_REQUIRE_LOW_LATENCY_MONITOR; LogEvent event(trainNameUtf8, trainVersionCode, requiresStaging, rollbackEnabled, requiresLowLatencyMonitor, state, experimentIdsProtoBuffer, userId); mProcessor->OnLogEvent(&event); @@ -1455,17 +1470,7 @@ Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackType 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 // Read the latest train info @@ -1601,7 +1606,6 @@ void StatsService::binderDied(const wp <IBinder>& who) { } 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 5f1335efc2e0..c9a9072ecb92 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -29,9 +29,10 @@ #include <android/frameworks/stats/1.0/IStats.h> #include <android/frameworks/stats/1.0/types.h> -#include <android/os/BnStatsManager.h> +#include <android/os/BnStatsd.h> +#include <android/os/IPendingIntentRef.h> #include <android/os/IStatsCompanionService.h> -#include <android/os/IStatsManager.h> +#include <android/os/IStatsd.h> #include <binder/IResultReceiver.h> #include <binder/ParcelFileDescriptor.h> #include <utils/Looper.h> @@ -52,7 +53,7 @@ namespace statsd { using android::hardware::Return; -class StatsService : public BnStatsManager, +class StatsService : public BnStatsd, public IStats, public IBinder::DeathRecipient { public: @@ -98,15 +99,14 @@ public: * 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; /** @@ -115,53 +115,52 @@ public: */ 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 sp<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 sp<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 sp<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(); @@ -180,11 +179,30 @@ public: const String16& packageName) override; /** + * Binder call to register a callback function for a pulled atom. + */ + virtual Status registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, + int64_t timeoutNs, const std::vector<int32_t>& additiveFields, + const sp<android::os::IPullAtomCallback>& pullerCallback) override; + + /** + * Binder call to register a callback function for a pulled atom. + */ + virtual Status registerNativePullAtomCallback(int32_t atomTag, int64_t coolDownNs, + int64_t timeoutNs, const std::vector<int32_t>& additiveFields, + const sp<android::os::IPullAtomCallback>& pullerCallback) override; + + /** * Binder call to unregister any existing callback function for a vendor pulled atom. */ virtual Status unregisterPullerCallback(int32_t atomTag, const String16& packageName) override; /** + * Binder call to unregister any existing callback for the given uid and atom. + */ + virtual Status unregisterPullAtomCallback(int32_t uid, int32_t atomTag) override; + + /** * Binder call to log BinaryPushStateChanged atom. */ virtual Status sendBinaryPushStateChangedAtom( diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto index 16c936c41559..946c55087005 100644 --- a/cmds/statsd/src/atom_field_options.proto +++ b/cmds/statsd/src/atom_field_options.proto @@ -30,6 +30,8 @@ enum StateField { PRIMARY = 1; // The field that represents the state. It's an exclusive state. EXCLUSIVE = 2; + + PRIMARY_FIELD_FIRST_UID = 3; } // Used to annotate an atom that reprsents a state change. A state change atom must have exactly ONE @@ -85,5 +87,5 @@ extend google.protobuf.FieldOptions { optional bool allow_from_any_uid = 50003 [default = false]; - optional string log_from_module = 50004; -}
\ No newline at end of file + optional string module = 50004; +} diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 4eb38ccbbf0e..52a8a7c050a9 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -50,12 +50,15 @@ 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/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/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 master atom class. This message defines all of the available @@ -109,9 +112,9 @@ message Atom { 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; + 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; SettingChanged setting_changed = 41; ActivityForegroundStateChanged activity_foreground_state_changed = 42; @@ -123,10 +126,10 @@ message Atom { AppStartOccurred app_start_occurred = 48; AppStartCanceled app_start_canceled = 49; AppStartFullyDrawn app_start_fully_drawn = 50; - LmkKillOccurred lmk_kill_occurred = 51; + LmkKillOccurred lmk_kill_occurred = 51 [(module) = "lmkd"]; PictureInPictureStateChanged picture_in_picture_state_changed = 52; - WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53; - LmkStateChanged lmk_state_changed = 54; + WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(module) = "wifi"]; + LmkStateChanged lmk_state_changed = 54 [(module) = "lmkd"]; AppStartMemoryStateCaptured app_start_memory_state_captured = 55; ShutdownSequenceReported shutdown_sequence_reported = 56; BootSequenceReported boot_sequence_reported = 57; @@ -162,7 +165,7 @@ message Atom { BiometricAcquired biometric_acquired = 87; BiometricAuthenticated biometric_authenticated = 88; BiometricErrorOccurred biometric_error_occurred = 89; - // Atom number 90 is available for use. + UiEventReported ui_event_reported = 90; BatteryHealthSnapshot battery_health_snapshot = 91; SlowIo slow_io = 92; BatteryCausedShutdown battery_caused_shutdown = 93; @@ -179,39 +182,27 @@ message Atom { 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"]; + 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; WifiRunningStateChanged wifi_running_state_changed = 114; AppCompacted app_compacted = 115; - NetworkDnsEventReported network_dns_event_reported = 116 [(log_from_module) = "resolv"]; + 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"]; + 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; SignedConfigReported signed_config_reported = 123; GnssNiEventReported gnss_ni_event_reported = 124; @@ -261,7 +252,7 @@ message Atom { ScreenTimeoutExtensionReported screen_timeout_extension_reported = 168; ProcessStartTime process_start_time = 169; 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; @@ -270,21 +261,21 @@ message Atom { 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"]; + StyleUIChanged style_ui_changed = 179 [(module) = "style"]; PrivacyIndicatorsInteracted privacy_indicators_interacted = - 180 [(log_from_module) = "permissioncontroller"]; + 180 [(module) = "permissioncontroller"]; AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181; - NetworkStackReported network_stack_reported = 182 [(log_from_module) = "network_stack"]; + NetworkStackReported network_stack_reported = 182 [(module) = "network_stack"]; AppMovedStorageReported app_moved_storage_reported = 183; BiometricEnrolled biometric_enrolled = 184; SystemServerWatchdogOccurred system_server_watchdog_occurred = 185; TombStoneOccurred tomb_stone_occurred = 186; BluetoothClassOfDeviceReported bluetooth_class_of_device_reported = 187; IntelligenceEventReported intelligence_event_reported = - 188 [(log_from_module) = "intelligence"]; + 188 [(module) = "intelligence"]; ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189; 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; @@ -295,42 +286,58 @@ message Atom { MediametricsMediadrmReported mediametrics_mediadrm_reported = 198; MediametricsNuPlayerReported mediametrics_nuplayer_reported = 199; MediametricsRecorderReported mediametrics_recorder_reported = 200; + MediametricsDrmManagerReported mediametrics_drmmanager_reported = 201; CarPowerStateChanged car_power_state_changed = 203; GarageModeInfo garage_mode_info = 204; - TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"]; + TestAtomReported test_atom_reported = 205 [(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; 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"]; + 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; BackGesture back_gesture_reported_reported = 224; - + UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225; + UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226; + CameraActionEvent camera_action_event = 227; AppCompatibilityChangeReported app_compatibility_change_reported = 228 [(allow_from_any_uid) = true]; - PerfettoUploaded perfetto_uploaded = - 229 [(log_from_module) = "perfetto"]; + PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"]; VmsClientConnectionStateChanged vms_client_connection_state_changed = 230; + GpsLocationStatusReported gps_location_status_reported = 231; + GpsTimeToFirstFixReported gps_time_to_first_fix_reported = 232; + MediaProviderScanEvent media_provider_scan_event = 233 [(module) = "mediaprovider"]; + MediaProviderDeletionEvent media_provider_deletion_event = 234 [(module) = "mediaprovider"]; + MediaProviderPermissionEvent media_provider_permission_event = + 235 [(module) = "mediaprovider"]; + MediaProviderSchemaChange media_provider_schema_change = 236 [(module) = "mediaprovider"]; + MediaProviderIdleMaintenance media_provider_idle_maintenance = + 237 [(module) = "mediaprovider"]; + RebootEscrowRecoveryReported reboot_escrow_recovery_reported = 238; } // Pulled events will start at field 10000. - // Next: 10067 + // Next: 10069 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001; @@ -368,7 +375,6 @@ message Atom { 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; OnDevicePowerMeasurement on_device_power_measurement = 10038; DeviceCalculatedPowerUse device_calculated_power_use = 10039; @@ -394,13 +400,20 @@ message Atom { CoolingDevice cooling_device = 10059; AppOps app_ops = 10060; ProcessSystemIonHeapSize process_system_ion_heap_size = 10061; + SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062; + SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063; + ProcessMemorySnapshot process_memory_snapshot = 10064; VmsClientStats vms_client_stats = 10065; NotificationRemoteViews notification_remote_views = 10066; + DangerousPermissionStateSampled dangerous_permission_state_sampled = 10067; + GraphicsStats graphics_stats = 10068; } // 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; } /** @@ -718,6 +731,27 @@ message GpsSignalQualityChanged { optional android.server.location.GpsSignalQualityEnum level = 1; } +/** + * Gps location status report + * + * Logged from: + * /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java + */ +message GpsLocationStatusReported { + // Boolean stating if location was acquired + optional bool location_success = 1; +} + +/** + * Gps log time to first fix report + * + * Logged from: + * /frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java + */ +message GpsTimeToFirstFixReported { + // int32 reporting the time to first fix in milliseconds + optional int32 time_to_first_fix_millis = 1; +} /** * Logs when a sync manager sync state changes. @@ -874,14 +908,16 @@ message CameraStateChanged { * TODO */ message WakelockStateChanged { - repeated AttributionNode attribution_node = 1; + repeated AttributionNode attribution_node = 1 + [(state_field_option).option = PRIMARY_FIELD_FIRST_UID]; // 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).option = PRIMARY]; + ; // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). - optional string tag = 3; + optional string tag = 3 [(state_field_option).option = PRIMARY]; enum State { RELEASE = 0; @@ -889,7 +925,7 @@ message WakelockStateChanged { CHANGE_RELEASE = 2; CHANGE_ACQUIRE = 3; } - optional State state = 4; + optional State state = 4 [(state_field_option).option = EXCLUSIVE]; } /** @@ -2623,12 +2659,14 @@ message SettingsUIChanged { 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; @@ -3070,9 +3108,9 @@ message PictureInPictureStateChanged { * 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).option = PRIMARY, (is_uid) = true]; - optional string package_name = 2; + optional string package_name = 2 [(state_field_option).option = PRIMARY]; optional bool using_alert_window = 3; @@ -3080,7 +3118,7 @@ message OverlayStateChanged { ENTERED = 1; EXITED = 2; } - optional State state = 4; + optional State state = 4 [(state_field_option).option = EXCLUSIVE]; } /* @@ -3261,6 +3299,21 @@ message GenericAtom { } /** + * 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. + */ +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; +} + +/** * Logs when a biometric acquire event occurs. * * Logged from: @@ -3756,6 +3809,124 @@ message VmsClientConnectionStateChanged { optional State state = 2; } +/** + * Logs when MediaProvider has successfully finished scanning a storage volume. + * + * Logged from: + * packages/providers/MediaProvider/src/com/android/providers/media/scan/ModernMediaScanner.java + */ +message MediaProviderScanEvent { + 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 MediaProviderDeletionEvent { + // Volume type that this event pertains to + optional android.stats.mediaprovider.VolumeType volume_type = 1; + // Device timestamp when this deletion event occurred + optional int64 timestamp_millis = 2; + // App that requested deletion + optional string package_name = 3; + // Number of items that were deleted + optional int32 item_count = 4; +} + +/** + * 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 MediaProviderPermissionEvent { + 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; + // Device timestamp when this permission event occurred + optional int64 timestamp_millis = 2; + // App that requested permission + optional string package_name = 3; + // Number of items that were requested + optional int32 item_count = 4; + // Result of this request + optional Result result = 5; +} + +/** + * Logs when MediaProvider has finished upgrading or downgrading its database schema. + * + * Logged from: + * packages/providers/MediaProvider/src/com/android/providers/media/DatabaseHelper.java + */ +message MediaProviderSchemaChange { + // 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 MediaProviderIdleMaintenance { + // 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; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// @@ -4046,8 +4217,8 @@ message ProcessMemoryState { 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 @@ -4057,67 +4228,52 @@ message ProcessMemoryState { // 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; + // Deprecated: use ProcessMemorySnapshot atom instead. Always -1. + optional int32 anon_rss_and_swap_in_kilobytes = 11 [deprecated = true]; } /* - * Logs the memory stats for a native process (from procfs). + * Logs the memory high-water mark for a process. * - * Pulled from StatsCompanionService for selected native processes. + * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie) + * and for selected native processes. + * + * Pulling this atom resets high-water mark counters for all processes. */ -message NativeProcessMemoryState { +message ProcessMemoryHighWaterMark { // The uid if available. -1 means not available. optional int32 uid = 1 [(is_uid) = true]; // The process name. - // Value read from /proc/PID/cmdline. + // Usually package name or process cmdline. + // Provided by ActivityManagerService or 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 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]; - // 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; + // RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in + // /proc/PID/status. + optional int32 rss_high_water_mark_in_kilobytes = 4; } /* - * Logs the memory high-water mark for a process. + * Logs the memory stats for a process. * - * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie) + * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService) * and for selected native processes. - * - * Pulling this atom resets high-water mark counters for all processes. */ -message ProcessMemoryHighWaterMark { +message ProcessMemorySnapshot { // The uid if available. -1 means not available. optional int32 uid = 1 [(is_uid) = true]; @@ -4126,9 +4282,29 @@ message ProcessMemoryHighWaterMark { // Provided by ActivityManagerService or read from /proc/PID/cmdline. optional string process_name = 2; - // 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; + // 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; } /* @@ -4551,36 +4727,69 @@ message RoleHolder { } message AggStats { - optional int64 min = 1; - - optional int64 average = 2; - - optional int64 max = 3; -} - + // These are all in byte resolution. + optional int64 min = 1 [deprecated = true]; + optional int64 average = 2 [deprecated = true]; + optional int64 max = 3 [deprecated = true]; + + // 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; @@ -4605,7 +4814,7 @@ message ProcessStatsProto { // 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; @@ -5768,10 +5977,10 @@ message PermissionGrantRequestResultReported { 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; @@ -5799,6 +6008,18 @@ message PermissionGrantRequestResultReported { 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; } // The result of the permission grant optional Result result = 6; @@ -6259,8 +6480,44 @@ message MediametricsNuPlayerReported { } /** - * 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; +} + +/** + * State of a dangerous permission requested by a package + * Pulled from: StatsCompanionService +*/ message DangerousPermissionState { // Name of the permission optional string permission_name = 1; @@ -6295,7 +6552,8 @@ message DeviceIdentifierAccessDenied { optional bool is_preinstalled = 3; // True if the package is privileged. - optional bool is_priv_app = 4; + // Starting from Android 11, this boolean is not set and will always be false. + optional bool is_priv_app = 4 [deprecated = true]; } /** @@ -6467,6 +6725,12 @@ message GpuStatsAppInfo { // 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; } /* @@ -6570,8 +6834,14 @@ message CoolingDevice { * 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; } /** @@ -6640,6 +6910,9 @@ message AppOps { // 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; } /** @@ -6887,6 +7160,331 @@ message PermissionAppsFragmentViewed { } /** + * 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; +} + +/** + * 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; +} + +/** + * 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; +} + +/** + * 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; +} + +/** + * 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; +} + +/** + * 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]; +} + +/** + * 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; +} + +/** + * 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 = 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: @@ -6919,6 +7517,7 @@ message AppCompatibilityChangeReported { // Where it was logged from. optional Source source = 4; + } /** @@ -6983,3 +7582,88 @@ message VmsClientStats { optional int64 dropped_bytes = 9; optional int64 dropped_packets = 10; } + +/** + * 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; + + // Uid of the package + optional int32 uid = 2 [(is_uid) = true]; + + // If the permission is granted to the uid + optional bool is_granted = 3; + + // Permission flags as per android.content.pm.PermissionFlags + optional int32 permission_flags = 4; +} + +/** + * HWUI renders pipeline type: GL (0) or Vulkan (1). + */ +enum PipelineType { + GL = 0; + VULKAN = 1; +} + +/** + * HWUI stats for a given app. + */ +message GraphicsStats { + // The package name of the app + optional string package_name = 1; + + // The version code of the app + optional int64 version_code = 2; + + // The start & end timestamps in UTC as + // milliseconds since January 1, 1970 + // Compatible with java.util.Date#setTime() + optional int64 stats_start = 3; + + optional int64 stats_end = 4; + + // HWUI renders pipeline type: GL or Vulkan. + optional PipelineType pipeline = 5; + + // 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; +} diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 60a4b236df11..69aae3d1e31c 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -86,7 +86,7 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf 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()) { @@ -110,20 +110,14 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf 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] = @@ -178,25 +172,6 @@ void CombinationConditionTracker::evaluateCondition( } } -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 481cb200d8e6..e3d860127780 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -43,17 +43,8 @@ public: 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 1f4266b61cdf..e94ea6586f05 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -84,29 +84,14 @@ public: // 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 { diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp index 23a9d371145e..4f44a69ba980 100644 --- a/cmds/statsd/src/condition/ConditionWizard.cpp +++ b/cmds/statsd/src/condition/ConditionWizard.cpp @@ -25,27 +25,15 @@ using std::string; 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); @@ -82,4 +70,4 @@ bool ConditionWizard::equalOutputDimensions(const int index, const vector<Matche } // 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 2c8814772839..892647910d9f 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -40,15 +40,7 @@ public: // 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 87104a34c009..0c92149f4c96 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -344,11 +344,8 @@ void SimpleConditionTracker::evaluateCondition( 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. @@ -360,18 +357,13 @@ void SimpleConditionTracker::isConditionMet( 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; @@ -389,15 +381,6 @@ void SimpleConditionTracker::isConditionMet( 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 { @@ -407,15 +390,6 @@ void SimpleConditionTracker::isConditionMet( 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); - } - } } } @@ -423,41 +397,6 @@ void SimpleConditionTracker::isConditionMet( 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 47d1eceb9022..5c5cc565f783 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -48,17 +48,8 @@ public: 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/StateConditionTracker.cpp index 1965ce6015ff..7f3eeddba831 100644 --- a/cmds/statsd/src/condition/StateTracker.cpp +++ b/cmds/statsd/src/condition/StateConditionTracker.cpp @@ -16,7 +16,7 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" -#include "StateTracker.h" +#include "StateConditionTracker.h" #include "guardrail/StatsdStats.h" namespace android { @@ -27,7 +27,7 @@ using std::string; using std::unordered_set; using std::vector; -StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int index, +StateConditionTracker::StateConditionTracker(const ConfigKey& key, const int64_t& id, const int index, const SimplePredicate& simplePredicate, const unordered_map<int64_t, int>& trackerNameIndexMap, const vector<Matcher> primaryKeys) @@ -69,19 +69,19 @@ StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int in mInitialized = true; } -StateTracker::~StateTracker() { - VLOG("~StateTracker()"); +StateConditionTracker::~StateConditionTracker() { + VLOG("~StateConditionTracker()"); } -bool StateTracker::init(const vector<Predicate>& allConditionConfig, +bool StateConditionTracker::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); +void StateConditionTracker::dumpState() { + VLOG("StateConditionTracker %lld DUMP:", (long long)mConditionId); for (const auto& value : mSlicedState) { VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str()); } @@ -95,7 +95,7 @@ void StateTracker::dumpState() { } } -bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) { +bool StateConditionTracker::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; @@ -114,7 +114,7 @@ bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) { return false; } -void StateTracker::evaluateCondition(const LogEvent& event, +void StateConditionTracker::evaluateCondition(const LogEvent& event, const vector<MatchingState>& eventMatcherValues, const vector<sp<ConditionTracker>>& mAllConditions, vector<ConditionState>& conditionCache, @@ -135,7 +135,7 @@ void StateTracker::evaluateCondition(const LogEvent& event, return; } - VLOG("StateTracker evaluate event %s", event.ToString().c_str()); + VLOG("StateConditionTracker 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. @@ -151,7 +151,7 @@ void StateTracker::evaluateCondition(const LogEvent& event, } hitGuardRail(primaryKey); - VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str()); + VLOG("StateConditionTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str()); auto it = mSlicedState.find(primaryKey); if (it == mSlicedState.end()) { @@ -176,13 +176,10 @@ void StateTracker::evaluateCondition(const LogEvent& event, return; } -void StateTracker::isConditionMet( +void StateConditionTracker::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. VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]); @@ -193,10 +190,6 @@ void StateTracker::isConditionMet( 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; } @@ -208,25 +201,9 @@ void StateTracker::isConditionMet( 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 +} // namespace android diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateConditionTracker.h index 2bdf98c34c32..0efe1fb3fcb2 100644 --- a/cmds/statsd/src/condition/StateTracker.h +++ b/cmds/statsd/src/condition/StateConditionTracker.h @@ -25,14 +25,14 @@ namespace android { namespace os { namespace statsd { -class StateTracker : public virtual ConditionTracker { +class StateConditionTracker : public virtual ConditionTracker { public: - StateTracker(const ConfigKey& key, const int64_t& id, const int index, + StateConditionTracker(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(); + ~StateConditionTracker(); bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, @@ -46,8 +46,8 @@ public: 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 + * Note: dimensionFields will be ignored in StateConditionTracker, because we demand metrics + * must take the entire dimension fields from StateConditionTracker. This is to make implementation * simple and efficient. * * For example: wakelock duration by uid process states: @@ -55,22 +55,8 @@ public: */ 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; + std::vector<ConditionState>& conditionCache) const override; virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions( const std::vector<sp<ConditionTracker>>& allConditions) const { @@ -123,9 +109,9 @@ private: // maps from [primary_key] to [primary_key, exclusive_state]. std::unordered_map<HashableDimensionKey, HashableDimensionKey> mSlicedState; - FRIEND_TEST(StateTrackerTest, TestStateChange); + FRIEND_TEST(StateConditionTrackerTest, TestStateChange); }; } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index fc949b494194..972adf7d4d05 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -46,6 +46,41 @@ using std::vector; using android::base::StringPrintf; using std::unique_ptr; +class ConfigReceiverDeathRecipient : public android::IBinder::DeathRecipient { + public: + ConfigReceiverDeathRecipient(sp<ConfigManager> configManager, const ConfigKey& configKey): + mConfigManager(configManager), + mConfigKey(configKey) {} + ~ConfigReceiverDeathRecipient() override = default; + private: + sp<ConfigManager> mConfigManager; + ConfigKey mConfigKey; + + void binderDied(const android::wp<android::IBinder>& who) override { + if (IInterface::asBinder(mConfigManager->GetConfigReceiver(mConfigKey)) == who.promote()) { + mConfigManager->RemoveConfigReceiver(mConfigKey); + } + } +}; + +class ActiveConfigChangedReceiverDeathRecipient : public android::IBinder::DeathRecipient { + public: + ActiveConfigChangedReceiverDeathRecipient(sp<ConfigManager> configManager, const int uid): + mConfigManager(configManager), + mUid(uid) {} + ~ActiveConfigChangedReceiverDeathRecipient() override = default; + private: + sp<ConfigManager> mConfigManager; + int mUid; + + void binderDied(const android::wp<android::IBinder>& who) override { + if (IInterface::asBinder(mConfigManager->GetActiveConfigsChangedReceiver(mUid)) + == who.promote()) { + mConfigManager->RemoveActiveConfigsChangedReceiver(mUid); + } + } +}; + ConfigManager::ConfigManager() { } @@ -118,9 +153,11 @@ void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& confi } } -void ConfigManager::SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender) { +void ConfigManager::SetConfigReceiver(const ConfigKey& key, + const sp<IPendingIntentRef>& pir) { lock_guard<mutex> lock(mMutex); - mConfigReceivers[key] = intentSender; + mConfigReceivers[key] = pir; + IInterface::asBinder(pir)->linkToDeath(new ConfigReceiverDeathRecipient(this, key)); } void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { @@ -129,9 +166,11 @@ void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { } void ConfigManager::SetActiveConfigsChangedReceiver(const int uid, - const sp<IBinder>& intentSender) { + const sp<IPendingIntentRef>& pir) { lock_guard<mutex> lock(mMutex); - mActiveConfigsChangedReceivers[uid] = intentSender; + mActiveConfigsChangedReceivers[uid] = pir; + IInterface::asBinder(pir)->linkToDeath( + new ActiveConfigChangedReceiverDeathRecipient(this, uid)); } void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) { @@ -150,25 +189,11 @@ void ConfigManager::RemoveConfig(const ConfigKey& key) { // 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); @@ -199,12 +224,6 @@ void ConfigManager::RemoveConfigs(int uid) { // 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); @@ -238,8 +257,6 @@ void ConfigManager::RemoveAllConfigs() { uidIt = mConfigs.erase(uidIt); } - mConfigReceivers.clear(); - mActiveConfigsChangedReceivers.clear(); for (const sp<ConfigListener>& listener : mListeners) { broadcastList.push_back(listener); } @@ -266,7 +283,7 @@ vector<ConfigKey> ConfigManager::GetAllConfigKeys() const { return ret; } -const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key) const { +const sp<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const { lock_guard<mutex> lock(mMutex); auto it = mConfigReceivers.find(key); @@ -277,7 +294,7 @@ const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key } } -const sp<android::IBinder> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const { +const sp<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 c064a519f597..88e864a2520b 100644 --- a/cmds/statsd/src/config/ConfigManager.h +++ b/cmds/statsd/src/config/ConfigManager.h @@ -16,10 +16,10 @@ #pragma once -#include "binder/IBinder.h" #include "config/ConfigKey.h" #include "config/ConfigListener.h" +#include <android/os/IPendingIntentRef.h> #include <map> #include <mutex> #include <set> @@ -64,12 +64,12 @@ public: /** * Sets the broadcast receiver for a configuration key. */ - void SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender); + void SetConfigReceiver(const ConfigKey& key, const sp<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 sp<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const; /** * Returns all config keys registered. @@ -85,13 +85,13 @@ public: * 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 sp<IPendingIntentRef>& pir); /** * Returns the broadcast receiver for active configs changed for this uid. */ - const sp<IBinder> GetActiveConfigsChangedReceiver(const int uid) const; + const sp<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const; /** * Erase any active configs changed broadcast receiver associated with this uid. @@ -141,16 +141,15 @@ private: 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, sp<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, sp<IPendingIntentRef>> mActiveConfigsChangedReceivers; /** * The ConfigListeners that will be told about changes. diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp index 0d3aca05e0e5..3229ba82fe3c 100644 --- a/cmds/statsd/src/external/GpuStatsPuller.cpp +++ b/cmds/statsd/src/external/GpuStatsPuller.cpp @@ -92,10 +92,18 @@ static bool pullGpuStatsAppInfo(const sp<IGpuService>& gpuService, 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->writeBytes(int64VectorToProtoByteString(info.glDriverLoadingTime))) { + return false; + } + if (!event->writeBytes(int64VectorToProtoByteString(info.vkDriverLoadingTime))) { + return false; + } + if (!event->writeBytes(int64VectorToProtoByteString(info.angleDriverLoadingTime))) { + return false; + } if (!event->write(info.cpuVulkanInUse)) return false; + if (!event->write(info.falsePrerotation)) return false; + if (!event->write(info.gles1InUse)) return false; event->init(); data->emplace_back(event); } diff --git a/cmds/statsd/src/external/PullResultReceiver.cpp b/cmds/statsd/src/external/PullResultReceiver.cpp new file mode 100644 index 000000000000..6b6fe7d9617f --- /dev/null +++ b/cmds/statsd/src/external/PullResultReceiver.cpp @@ -0,0 +1,44 @@ +/* + * 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" + +using namespace android::binder; +using namespace android::util; +using namespace std; + +namespace android { +namespace os { +namespace statsd { + +PullResultReceiver::PullResultReceiver( + std::function<void(int32_t, bool, const vector<android::util::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 000000000000..17d06e4ff4db --- /dev/null +++ b/cmds/statsd/src/external/PullResultReceiver.h @@ -0,0 +1,44 @@ +/* + * 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 <android/os/BnPullAtomResultReceiver.h> + +using namespace std; + +namespace android { +namespace os { +namespace statsd { + +class PullResultReceiver : public BnPullAtomResultReceiver { +public: + PullResultReceiver(function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)> + pullFinishCallback); + ~PullResultReceiver(); + + /** + * Binder call for finishing a pull. + */ + binder::Status pullFinished(int32_t atomTag, bool success, + const vector<android::util::StatsEventParcel>& output) override; + +private: + function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)> + pullFinishCallback; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp index d718273e9b85..0e6b677abb46 100644 --- a/cmds/statsd/src/external/StatsCallbackPuller.cpp +++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp @@ -17,21 +17,28 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" -#include <android/os/IStatsPullerCallback.h> - #include "StatsCallbackPuller.h" + +#include <android/os/IPullAtomCallback.h> +#include <android/util/StatsEventParcel.h> + +#include "PullResultReceiver.h" +#include "StatsPullerManager.h" #include "logd/LogEvent.h" #include "stats_log_util.h" using namespace android::binder; +using namespace android::util; +using namespace std; 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 sp<IPullAtomCallback>& callback, + int64_t timeoutNs) + : StatsPuller(tagId), mCallback(callback), mTimeoutNs(timeoutNs) { + VLOG("StatsCallbackPuller created for tag %d", tagId); } bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { @@ -40,20 +47,61 @@ bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { 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>>>(); + + sp<PullResultReceiver> resultReceiver = new 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>( + const_cast<uint8_t*>(parcel.buffer.data()), parcel.buffer.size(), + /*uid=*/-1, /*useNewSchema=*/true); + sharedData->push_back(event); + } + *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); 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(mTimeoutNs), + [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 c4bfa89ba9a7..d943f9d189c5 100644 --- a/cmds/statsd/src/external/StatsCallbackPuller.h +++ b/cmds/statsd/src/external/StatsCallbackPuller.h @@ -16,8 +16,9 @@ #pragma once -#include <android/os/IStatsPullerCallback.h> +#include <android/os/IPullAtomCallback.h> #include <utils/String16.h> + #include "StatsPuller.h" namespace android { @@ -26,11 +27,17 @@ namespace statsd { class StatsCallbackPuller : public StatsPuller { public: - explicit StatsCallbackPuller(int tagId, const sp<IStatsPullerCallback>& callback); + explicit StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback, + int64_t timeoutNs); private: bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override; - const sp<IStatsPullerCallback> mCallback; + const sp<IPullAtomCallback> mCallback; + const int64_t mTimeoutNs; + + FRIEND_TEST(StatsCallbackPullerTest, PullFail); + FRIEND_TEST(StatsCallbackPullerTest, PullSuccess); + FRIEND_TEST(StatsCallbackPullerTest, PullTimeout); }; } // namespace statsd diff --git a/cmds/statsd/src/external/StatsCallbackPullerDeprecated.cpp b/cmds/statsd/src/external/StatsCallbackPullerDeprecated.cpp new file mode 100644 index 000000000000..4f88a91f9ec6 --- /dev/null +++ b/cmds/statsd/src/external/StatsCallbackPullerDeprecated.cpp @@ -0,0 +1,63 @@ +/* + * 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 "StatsCallbackPullerDeprecated.h" + +#include <android/os/IStatsPullerCallback.h> + +#include "logd/LogEvent.h" +#include "stats_log_util.h" + +using namespace android::binder; + +namespace android { +namespace os { +namespace statsd { + +StatsCallbackPullerDeprecated::StatsCallbackPullerDeprecated( + int tagId, const sp<IStatsPullerCallback>& callback) + : StatsPuller(tagId), mCallback(callback) { + VLOG("StatsCallbackPuller created for tag %d", tagId); +} + +bool StatsCallbackPullerDeprecated::PullInternal(vector<shared_ptr<LogEvent>>* data) { + 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); + if (!status.isOk()) { + ALOGW("StatsCallbackPuller::pull failed for %d", mTagId); + return false; + } + data->clear(); + for (const StatsLogEventWrapper& it : returned_value) { + LogEvent::createLogEvents(it, *data); + } + VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId); + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/StatsCallbackPullerDeprecated.h b/cmds/statsd/src/external/StatsCallbackPullerDeprecated.h new file mode 100644 index 000000000000..028902975923 --- /dev/null +++ b/cmds/statsd/src/external/StatsCallbackPullerDeprecated.h @@ -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. + */ + +#pragma once + +#include <android/os/IStatsPullerCallback.h> +#include <utils/String16.h> + +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +class StatsCallbackPullerDeprecated : public StatsPuller { +public: + explicit StatsCallbackPullerDeprecated(int tagId, const sp<IStatsPullerCallback>& callback); + +private: + bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override; + const sp<IStatsPullerCallback> mCallback; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp index 9552c0a5e35e..883bd28a4d13 100644 --- a/cmds/statsd/src/external/StatsPuller.cpp +++ b/cmds/statsd/src/external/StatsPuller.cpp @@ -40,12 +40,14 @@ bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) { lock_guard<std::mutex> lock(mLock); int64_t elapsedTimeNs = getElapsedRealtimeNs(); StatsdStats::getInstance().notePull(mTagId); - const bool shouldUseCache = elapsedTimeNs - mLastPullTimeNs < - StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs; + const bool shouldUseCache = + elapsedTimeNs - mLastPullTimeNs < + StatsPullerManager::kAllPullAtomInfo.at({.atomTag = mTagId}).coolDownNs; if (shouldUseCache) { if (mHasGoodData) { (*data) = mCachedData; StatsdStats::getInstance().notePullFromCache(mTagId); + } return mHasGoodData; } @@ -63,7 +65,8 @@ bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) { const int64_t pullDurationNs = getElapsedRealtimeNs() - elapsedTimeNs; StatsdStats::getInstance().notePullTime(mTagId, pullDurationNs); const bool pullTimeOut = - pullDurationNs > StatsPullerManager::kAllPullAtomInfo.at(mTagId).pullTimeoutNs; + pullDurationNs > + StatsPullerManager::kAllPullAtomInfo.at({.atomTag = mTagId}).pullTimeoutNs; if (pullTimeOut) { // Something went wrong. Discard the data. clearCacheLocked(); @@ -100,7 +103,7 @@ int StatsPuller::clearCacheLocked() { int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) { if (timestampNs - mLastPullTimeNs > - StatsPullerManager::kAllPullAtomInfo.at(mTagId).coolDownNs) { + StatsPullerManager::kAllPullAtomInfo.at({.atomTag = mTagId}).coolDownNs) { return clearCache(); } else { return 0; diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 1c9d7763bc83..b5680331f63e 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -17,12 +17,18 @@ #define DEBUG false #include "Log.h" +#include "StatsPullerManager.h" + +#include <android/os/IPullAtomCallback.h> #include <android/os/IStatsCompanionService.h> #include <android/os/IStatsPullerCallback.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" @@ -32,14 +38,12 @@ #include "PowerStatsPuller.h" #include "ResourceHealthManagerPuller.h" #include "StatsCallbackPuller.h" +#include "StatsCallbackPullerDeprecated.h" #include "StatsCompanionServicePuller.h" -#include "StatsPullerManager.h" #include "SubsystemSleepStatePuller.h" #include "TrainInfoPuller.h" #include "statslog.h" -#include <iostream> - using std::make_shared; using std::map; using std::shared_ptr; @@ -54,237 +58,304 @@ namespace statsd { // 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)}}, +std::map<PullerKey, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { + // wifi_bytes_transfer_by_fg_bg - {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = android::util::BLUETOOTH_BYTES_TRANSFER}, {.additiveFields = {2, 3}, .puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}}, + // kernel_wakelock - {android::util::KERNEL_WAKELOCK, + {{.atomTag = android::util::KERNEL_WAKELOCK}, {.puller = new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}}, + // subsystem_sleep_state - {android::util::SUBSYSTEM_SLEEP_STATE, {.puller = new SubsystemSleepStatePuller()}}, + {{.atomTag = android::util::SUBSYSTEM_SLEEP_STATE}, + {.puller = new SubsystemSleepStatePuller()}}, + // on_device_power_measurement - {android::util::ON_DEVICE_POWER_MEASUREMENT, {.puller = new PowerStatsPuller()}}, + {{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT}, + {.puller = new PowerStatsPuller()}}, + // cpu_time_per_freq - {android::util::CPU_TIME_PER_FREQ, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = android::util::CPU_CLUSTER_TIME}, {.additiveFields = {3}, .puller = new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}}, + // wifi_activity_energy_info - {android::util::WIFI_ACTIVITY_INFO, + {{.atomTag = android::util::WIFI_ACTIVITY_INFO}, {.puller = new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}}, + // modem_activity_info - {android::util::MODEM_ACTIVITY_INFO, + {{.atomTag = android::util::MODEM_ACTIVITY_INFO}, {.puller = new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}}, + // bluetooth_activity_info - {android::util::BLUETOOTH_ACTIVITY_INFO, + {{.atomTag = android::util::BLUETOOTH_ACTIVITY_INFO}, {.puller = new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}}, + // system_elapsed_realtime - {android::util::SYSTEM_ELAPSED_REALTIME, + {{.atomTag = 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, + {{.atomTag = android::util::SYSTEM_UPTIME}, {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}}, + // remaining_battery_capacity - {android::util::REMAINING_BATTERY_CAPACITY, + {{.atomTag = android::util::REMAINING_BATTERY_CAPACITY}, {.puller = new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}}, + // full_battery_capacity - {android::util::FULL_BATTERY_CAPACITY, + {{.atomTag = android::util::FULL_BATTERY_CAPACITY}, {.puller = new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}}, + // battery_voltage - {android::util::BATTERY_VOLTAGE, + {{.atomTag = android::util::BATTERY_VOLTAGE}, {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}}, + // battery_level - {android::util::BATTERY_LEVEL, + {{.atomTag = android::util::BATTERY_LEVEL}, {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}}, + // battery_cycle_count - {android::util::BATTERY_CYCLE_COUNT, + {{.atomTag = 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}, + {{.atomTag = android::util::PROCESS_MEMORY_STATE}, + {.additiveFields = {4, 5, 6, 7, 8}, .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 = + {{.atomTag = android::util::PROCESS_MEMORY_HIGH_WATER_MARK}, + {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}}, + + // process_memory_snapshot + {{.atomTag = android::util::PROCESS_MEMORY_SNAPSHOT}, + {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}}, + // system_ion_heap_size - {android::util::SYSTEM_ION_HEAP_SIZE, + {{.atomTag = 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, + {{.atomTag = android::util::PROCESS_SYSTEM_ION_HEAP_SIZE}, {.puller = new StatsCompanionServicePuller(android::util::PROCESS_SYSTEM_ION_HEAP_SIZE)}}, + // temperature - {android::util::TEMPERATURE, + {{.atomTag = android::util::TEMPERATURE}, {.puller = new StatsCompanionServicePuller(android::util::TEMPERATURE)}}, + // cooling_device - {android::util::COOLING_DEVICE, + {{.atomTag = android::util::COOLING_DEVICE}, {.puller = new StatsCompanionServicePuller(android::util::COOLING_DEVICE)}}, + // binder_calls - {android::util::BINDER_CALLS, + {{.atomTag = 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, + {{.atomTag = android::util::BINDER_CALLS_EXCEPTIONS}, {.puller = new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}}, + // looper_stats - {android::util::LOOPER_STATS, + {{.atomTag = android::util::LOOPER_STATS}, {.additiveFields = {5, 6, 7, 8, 9}, .puller = new StatsCompanionServicePuller(android::util::LOOPER_STATS)}}, + // Disk Stats - {android::util::DISK_STATS, + {{.atomTag = android::util::DISK_STATS}, {.puller = new StatsCompanionServicePuller(android::util::DISK_STATS)}}, + // Directory usage - {android::util::DIRECTORY_USAGE, + {{.atomTag = android::util::DIRECTORY_USAGE}, {.puller = new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}}, + // Size of app's code, data, and cache - {android::util::APP_SIZE, + {{.atomTag = android::util::APP_SIZE}, {.puller = new StatsCompanionServicePuller(android::util::APP_SIZE)}}, + // Size of specific categories of files. Eg. Music. - {android::util::CATEGORY_SIZE, + {{.atomTag = android::util::CATEGORY_SIZE}, {.puller = new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}}, + // Number of fingerprints enrolled for each user. - {android::util::NUM_FINGERPRINTS_ENROLLED, + {{.atomTag = 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, + {{.atomTag = android::util::NUM_FACES_ENROLLED}, {.puller = new StatsCompanionServicePuller(android::util::NUM_FACES_ENROLLED)}}, + // ProcStats. - {android::util::PROC_STATS, + {{.atomTag = android::util::PROC_STATS}, {.puller = new StatsCompanionServicePuller(android::util::PROC_STATS)}}, + // ProcStatsPkgProc. - {android::util::PROC_STATS_PKG_PROC, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = android::util::DEVICE_CALCULATED_POWER_USE}, {.puller = new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}}, + // DeviceCalculatedPowerBlameUid. - {android::util::DEVICE_CALCULATED_POWER_BLAME_UID, + {{.atomTag = 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, + {{.atomTag = android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER}, {.puller = new StatsCompanionServicePuller( android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}}, + // DebugElapsedClock. - {android::util::DEBUG_ELAPSED_CLOCK, + {{.atomTag = 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, + {{.atomTag = 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, + {{.atomTag = android::util::BUILD_INFORMATION}, {.puller = new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}}, + // RoleHolder. - {android::util::ROLE_HOLDER, + {{.atomTag = android::util::ROLE_HOLDER}, {.puller = new StatsCompanionServicePuller(android::util::ROLE_HOLDER)}}, + // PermissionState. - {android::util::DANGEROUS_PERMISSION_STATE, + {{.atomTag = android::util::DANGEROUS_PERMISSION_STATE}, {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}}, + // TrainInfo. - {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}}, + {{.atomTag = android::util::TRAIN_INFO}, {.puller = new TrainInfoPuller()}}, + // TimeZoneDataInfo. - {android::util::TIME_ZONE_DATA_INFO, + {{.atomTag = android::util::TIME_ZONE_DATA_INFO}, {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}}, + // ExternalStorageInfo - {android::util::EXTERNAL_STORAGE_INFO, + {{.atomTag = android::util::EXTERNAL_STORAGE_INFO}, {.puller = new StatsCompanionServicePuller(android::util::EXTERNAL_STORAGE_INFO)}}, + // GpuStatsGlobalInfo - {android::util::GPU_STATS_GLOBAL_INFO, + {{.atomTag = android::util::GPU_STATS_GLOBAL_INFO}, {.puller = new GpuStatsPuller(android::util::GPU_STATS_GLOBAL_INFO)}}, + // GpuStatsAppInfo - {android::util::GPU_STATS_APP_INFO, + {{.atomTag = android::util::GPU_STATS_APP_INFO}, {.puller = new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)}}, + // AppsOnExternalStorageInfo - {android::util::APPS_ON_EXTERNAL_STORAGE_INFO, + {{.atomTag = android::util::APPS_ON_EXTERNAL_STORAGE_INFO}, {.puller = new StatsCompanionServicePuller(android::util::APPS_ON_EXTERNAL_STORAGE_INFO)}}, + // Face Settings - {android::util::FACE_SETTINGS, + {{.atomTag = android::util::FACE_SETTINGS}, {.puller = new StatsCompanionServicePuller(android::util::FACE_SETTINGS)}}, + // App ops - {android::util::APP_OPS, + {{.atomTag = android::util::APP_OPS}, {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}}, + // VmsClientStats - {android::util::VMS_CLIENT_STATS, + {{.atomTag = 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, + {{.atomTag = android::util::NOTIFICATION_REMOTE_VIEWS}, {.puller = new StatsCompanionServicePuller(android::util::NOTIFICATION_REMOTE_VIEWS)}}, + + // PermissionStateSampled. + {{.atomTag = android::util::DANGEROUS_PERMISSION_STATE_SAMPLED}, + {.puller = + new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE_SAMPLED)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { } bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) { + AutoMutex _l(mLock); + return PullLocked(tagId, data); +} + +bool StatsPullerManager::PullLocked(int tagId, vector<shared_ptr<LogEvent>>* data) { VLOG("Initiating pulling %d", tagId); - if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) { - bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data); + if (kAllPullAtomInfo.find({.atomTag = tagId}) != kAllPullAtomInfo.end()) { + bool ret = kAllPullAtomInfo.find({.atomTag = tagId})->second.puller->Pull(data); VLOG("pulled %d items", (int)data->size()); if (!ret) { StatsdStats::getInstance().notePullFailed(tagId); @@ -298,7 +369,8 @@ bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) { 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(); + return isVendorPulledAtom(tagId) || + kAllPullAtomInfo.find({.atomTag = tagId}) != kAllPullAtomInfo.end(); } void StatsPullerManager::updateAlarmLocked() { @@ -408,7 +480,7 @@ void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { for (const auto& pullInfo : needToPull) { vector<shared_ptr<LogEvent>> data; - bool pullSuccess = Pull(pullInfo.first, &data); + bool pullSuccess = PullLocked(pullInfo.first, &data); if (pullSuccess) { StatsdStats::getInstance().notePullDelay( pullInfo.first, getElapsedRealtimeNs() - elapsedTimeNs); @@ -467,6 +539,7 @@ int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) { return totalCleared; } +// Deprecated, remove after puller API is complete. void StatsPullerManager::RegisterPullerCallback(int32_t atomTag, const sp<IStatsPullerCallback>& callback) { AutoMutex _l(mLock); @@ -477,7 +550,24 @@ void StatsPullerManager::RegisterPullerCallback(int32_t atomTag, } VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag); StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true); - kAllPullAtomInfo[atomTag] = {.puller = new StatsCallbackPuller(atomTag, callback)}; + kAllPullAtomInfo[{.atomTag = atomTag}] = { + .puller = new StatsCallbackPullerDeprecated(atomTag, callback)}; +} + +void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag, + const int64_t coolDownNs, const int64_t timeoutNs, + const vector<int32_t>& additiveFields, + const sp<IPullAtomCallback>& callback) { + AutoMutex _l(mLock); + VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag); + // TODO: linkToDeath with the callback so that we can remove it and delete the puller. + StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true); + kAllPullAtomInfo[{.atomTag = atomTag}] = { + .additiveFields = additiveFields, + .coolDownNs = coolDownNs, + .puller = new StatsCallbackPuller(atomTag, callback, timeoutNs), + .pullTimeoutNs = timeoutNs, + }; } void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) { @@ -487,7 +577,13 @@ void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) { return; } StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false); - kAllPullAtomInfo.erase(atomTag); + kAllPullAtomInfo.erase({.atomTag = atomTag}); +} + +void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) { + AutoMutex _l(mLock); + StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false); + kAllPullAtomInfo.erase({.atomTag = atomTag}); } } // namespace statsd diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h index 4ea1386bf78a..349fd47b6c9d 100644 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ b/cmds/statsd/src/external/StatsPullerManager.h @@ -16,15 +16,18 @@ #pragma once +#include <android/os/IPullAtomCallback.h> #include <android/os/IStatsCompanionService.h> #include <android/os/IStatsPullerCallback.h> #include <binder/IServiceManager.h> #include <utils/RefBase.h> #include <utils/threads.h> + #include <list> #include <string> #include <unordered_map> #include <vector> + #include "PullDataReceiver.h" #include "StatsPuller.h" #include "guardrail/StatsdStats.h" @@ -53,6 +56,27 @@ typedef struct { 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: StatsPullerManager(); @@ -92,12 +116,18 @@ public: void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService); - void RegisterPullerCallback(int32_t atomTag, - const sp<IStatsPullerCallback>& callback); + // Deprecated, remove after puller API is complete. + 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 sp<IPullAtomCallback>& callback); void UnregisterPullerCallback(int32_t atomTag); - static std::map<int, PullAtomInfo> kAllPullAtomInfo; + void UnregisterPullAtomCallback(const int uid, const int32_t atomTag); + + static std::map<PullerKey, PullAtomInfo> kAllPullAtomInfo; private: sp<IStatsCompanionService> mStatsCompanionService = nullptr; @@ -111,6 +141,8 @@ private: // mapping from simple matcher tagId to receivers std::map<int, std::list<ReceiverInfo>> mReceivers; + bool PullLocked(int tagId, vector<std::shared_ptr<LogEvent>>* data); + // locks for data receiver and StatsCompanionService changes Mutex mLock; diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp index ccfd666573c4..031c43740d9d 100644 --- a/cmds/statsd/src/external/puller_util.cpp +++ b/cmds/statsd/src/external/puller_util.cpp @@ -55,7 +55,7 @@ using std::vector; */ void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap, int tagId) { - if (StatsPullerManager::kAllPullAtomInfo.find(tagId) == + if (StatsPullerManager::kAllPullAtomInfo.find({.atomTag = tagId}) == StatsPullerManager::kAllPullAtomInfo.end()) { VLOG("Unknown pull atom id %d", tagId); return; @@ -121,7 +121,7 @@ void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const vector<shared_ptr<LogEvent>> mergedData; const vector<int>& additiveFieldsVec = - StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields; + StatsPullerManager::kAllPullAtomInfo.find({.atomTag = tagId})->second.additiveFields; const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end()); bool needMerge = true; diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 564b9ee8051c..692d91e1a82f 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -181,6 +181,8 @@ public: 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. * diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 838561e3ae10..36f4623c4dcb 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -35,15 +35,31 @@ 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; +// Msg is expected to begin at the start of the serialized atom -- it should not +// include the android_log_header_t or the StatsEventTag. +LogEvent::LogEvent(uint8_t* msg, uint32_t len, uint32_t uid) + : mBuf(msg), + mRemainingLen(len), + mLogdTimestampNs(time(nullptr)), + mLogUid(uid) +{ +#ifdef NEW_ENCODING_SCHEME + initNew(); +# else + mContext = create_android_log_parser((char*)msg, len); init(mContext); - if (mContext) { - // android_log_destroy will set mContext to NULL - android_log_destroy(&mContext); + if (mContext) android_log_destroy(&mContext); // set mContext to NULL +#endif +} + +LogEvent::LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema) + : mBuf(msg), mRemainingLen(len), mLogdTimestampNs(time(nullptr)), mLogUid(uid) { + if (useNewSchema) { + initNew(); + } else { + mContext = create_android_log_parser((char*)msg, len); + init(mContext); + if (mContext) android_log_destroy(&mContext); // set mContext to NULL } } @@ -332,6 +348,13 @@ bool LogEvent::write(float value) { return false; } +bool LogEvent::writeBytes(const string& value) { + if (mContext) { + return android_log_write_char_array(mContext, value.c_str(), value.length()) >= 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, @@ -431,6 +454,186 @@ bool LogEvent::write(const AttributionNodeInternal& node) { return false; } +void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last) { + int32_t value = readNextValue<int32_t>(); + addToValues(pos, depth, value, last); +} + +void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last) { + int64_t value = readNextValue<int64_t>(); + addToValues(pos, depth, value, last); +} + +void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last) { + 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); +} + +void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last) { + float value = readNextValue<float>(); + addToValues(pos, depth, value, last); +} + +void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last) { + // cast to int32_t because FieldValue does not support bools + int32_t value = (int32_t)readNextValue<uint8_t>(); + addToValues(pos, depth, value, last); +} + +void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last) { + 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); +} + +void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last) { + int32_t numPairs = readNextValue<uint8_t>(); + + for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) { + last[1] = (pos[1] == numPairs); + + // parse key + pos[2] = 1; + parseInt32(pos, 2, last); + + // parse value + last[2] = true; + uint8_t typeId = getTypeId(readNextValue<uint8_t>()); + switch (typeId) { + case INT32_TYPE: + pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto + parseInt32(pos, 2, last); + break; + case INT64_TYPE: + pos[2] = 3; + parseInt64(pos, 2, last); + break; + case STRING_TYPE: + pos[2] = 4; + parseString(pos, 2, last); + break; + case FLOAT_TYPE: + pos[2] = 5; + parseFloat(pos, 2, last); + break; + default: + mValid = false; + } + } + + pos[1] = pos[2] = 1; + last[1] = last[2] = false; +} + +void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last) { + 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, 2, last); + + // parse tag + pos[2] = 2; + last[2] = true; + parseString(pos, 2, last); + } + + pos[1] = pos[2] = 1; + last[1] = last[2] = false; +} + + +// This parsing logic is tied to the encoding scheme used in StatsEvent.java and +// stats_event.c +void LogEvent::initNew() { + 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--; + + + for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) { + typeInfo = readNextValue<uint8_t>(); + uint8_t typeId = getTypeId(typeInfo); + + last[0] = (pos[0] == numElements); + + // TODO(b/144373276): handle errors passed to the socket + // TODO(b/144373257): parse annotations + switch(typeId) { + case BOOL_TYPE: + parseBool(pos, 0, last); + break; + case INT32_TYPE: + parseInt32(pos, 0, last); + break; + case INT64_TYPE: + parseInt64(pos, 0, last); + break; + case FLOAT_TYPE: + parseFloat(pos, 0, last); + break; + case BYTE_ARRAY_TYPE: + parseByteArray(pos, 0, last); + break; + case STRING_TYPE: + parseString(pos, 0, last); + break; + case KEY_VALUE_PAIRS_TYPE: + parseKeyValuePairs(pos, 0, last); + break; + case ATTRIBUTION_CHAIN_TYPE: + parseAttributionChain(pos, 0, last); + break; + default: + mValid = false; + } + } + + if (mRemainingLen != 0) mValid = false; + mBuf = nullptr; +} + +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; +} + /** * 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 diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 531ce299beef..596d623debe5 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -21,9 +21,10 @@ #include <android/frameworks/stats/1.0/types.h> #include <android/os/StatsLogEventWrapper.h> #include <android/util/ProtoOutputStream.h> -#include <log/log_event_list.h> #include <log/log_read.h> #include <private/android_logger.h> +#include <stats_event_list.h> +#include <stats_event.h> #include <utils/Errors.h> #include <string> @@ -69,9 +70,14 @@ struct InstallTrainInfo { class LogEvent { public: /** - * Read a LogEvent from a log_msg. + * Read a LogEvent from the socket */ - explicit LogEvent(log_msg& msg); + explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid); + + /** + * Temp constructor to use for pulled atoms until we flip the socket schema. + */ + explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema); /** * Creates LogEvent from StatsLogEventWrapper. @@ -157,6 +163,7 @@ public: bool write(float value); bool write(const std::vector<AttributionNodeInternal>& nodes); bool write(const AttributionNodeInternal& node); + bool writeBytes(const std::string& value); bool writeKeyValuePairs(int32_t uid, const std::map<int32_t, int32_t>& int_map, const std::map<int32_t, int64_t>& long_map, @@ -205,6 +212,10 @@ public: return &mValues; } + bool isValid() { + return mValid; + } + inline LogEvent makeCopy() { return LogEvent(*this); } @@ -215,6 +226,69 @@ private: */ LogEvent(const LogEvent&); + + /** + * Parsing function for new encoding scheme. + */ + void initNew(); + + void parseInt32(int32_t* pos, int32_t depth, bool* last); + void parseInt64(int32_t* pos, int32_t depth, bool* last); + void parseString(int32_t* pos, int32_t depth, bool* last); + void parseFloat(int32_t* pos, int32_t depth, bool* last); + void parseBool(int32_t* pos, int32_t depth, bool* last); + void parseByteArray(int32_t* pos, int32_t depth, bool* last); + void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last); + void parseAttributionChain(int32_t* pos, int32_t depth, bool* last); + + /** + * mBuf is a pointer to the current location in the buffer being parsed. + * Because the buffer lives on the StatsSocketListener stack, this pointer + * is only valid during the LogEvent constructor. It will be set to null at + * the end of initNew. + */ + 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 { + value = *((T*)mBuf); + 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); + /** * Parses a log_msg into a LogEvent object. */ diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index 8dc5cef988b0..476fae37899d 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -84,7 +84,7 @@ bool combinationMatch(const vector<int>& children, const LogicalOperation& opera bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value, const string& str_match) { - if (isAttributionUidField(field, value)) { + if (isAttributionUidField(field, value) || isUidField(field, value)) { int uid = value.int_value; auto aidIt = UidMap::sAidToUidMapping.find(str_match); if (aidIt != UidMap::sAidToUidMapping.end()) { @@ -358,9 +358,10 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, 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/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index c023e6f77e7c..21ffff32f539 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::map; 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_COUNT_METRICS = 5; 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 sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs, + + const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, + eventDeactivationMap, slicedStateAtoms, stateGroupMap) { if (metric.has_bucket()) { mBucketSizeNs = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; @@ -82,12 +88,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric 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 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric 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 @@ CountMetricProducer::~CountMetricProducer() { VLOG("~CountMetricProducer() called"); } +void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId, + const HashableDimensionKey& primaryKey, int oldState, + int 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, newState); +} + void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { if (mCurrentSlicedCounter == nullptr || mCurrentSlicedCounter->size() == 0) { @@ -124,10 +139,9 @@ void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { (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 @@ void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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 @@ void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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 @@ bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { 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 @@ bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { 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 b4a910c6f410..a4711e8357f2 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,26 @@ struct CountBucket { 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 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, int oldState, + int 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: @@ -102,6 +112,7 @@ private: FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade); FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket); FRIEND_TEST(CountMetricProducerTest, TestFirstBucket); + FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 96fbf7fb5ebe..e85b97514242 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -36,6 +36,7 @@ using android::util::ProtoOutputStream; using std::string; using std::unordered_map; using std::vector; +using std::shared_ptr; namespace android { namespace os { @@ -47,30 +48,30 @@ const int FIELD_ID_DURATION_METRICS = 6; 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; // 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 size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, + const bool nesting, const sp<ConditionWizard>& wizard, + const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, + const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) + : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, + eventDeactivationMap, slicedStateAtoms, stateGroupMap), mAggregationType(metric.aggregation_type()), mStartIndex(startIndex), mStopIndex(stopIndex), @@ -100,12 +101,7 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat 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 +111,15 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields); mMetric2ConditionLinks.push_back(mc); } + mConditionSliced = true; } - mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); mUnSlicedPartCondition = ConditionState::kUnknown; 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 @@ -164,13 +156,13 @@ unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( case DurationMetric_AggregationType_SUM: return make_unique<OringDurationTracker>( mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, - mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum, + 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, + mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum, mTimeBaseNs, mBucketSizeNs, mConditionSliced, mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers); } @@ -178,13 +170,11 @@ unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( // 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 +203,11 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio 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 +215,15 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio 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 +233,14 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(bool conditio 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 +274,14 @@ void DurationMetricProducer::onActiveStateChangedLocked(const int64_t& eventTime } 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 +297,7 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, flushIfNeededLocked(eventTime); for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - for (auto& pair : whatIt.second) { - pair.second->onConditionChanged(conditionMet, eventTime); - } + whatIt.second->onConditionChanged(conditionMet, eventTime); } } @@ -526,12 +341,6 @@ void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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 +360,9 @@ void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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 (DurationBucketInfo). for (const auto& bucket : pair.second) { @@ -614,19 +410,11 @@ void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs 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 +430,15 @@ void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { (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 +448,7 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey 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,24 +460,16 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey 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); @@ -728,8 +490,8 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey 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 +509,14 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, // 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; } // Handles Stop events. @@ -766,9 +524,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, 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 +536,31 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, 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, DEFAULT_DIMENSION_KEY), 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 56c9fd68eac5..06da0f64aedb 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -38,11 +38,16 @@ namespace statsd { 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 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(); @@ -54,8 +59,8 @@ protected: 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,8 +132,7 @@ private: 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. diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 96133bd0a38d..6833f8dd0114 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -36,6 +36,7 @@ using std::map; using std::string; using std::unordered_map; using std::vector; +using std::shared_ptr; namespace android { namespace os { @@ -51,11 +52,15 @@ const int FIELD_ID_DATA = 1; 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 sp<ConditionWizard>& wizard, const int64_t startTimeNs, + const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) + : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard, eventActivationMap, + eventDeactivationMap, slicedStateAtoms, stateGroupMap) { if (metric.links().size() > 0) { for (const auto& link : metric.links()) { Metric2Condition mc; @@ -138,8 +143,8 @@ void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, 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; } diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 74e6bc845c04..e8f2119a170c 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -33,17 +33,22 @@ namespace statsd { 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 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 a64bbc1056e0..4ab6fd48f1db 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -48,19 +48,21 @@ const int FIELD_ID_GAUGE_METRICS = 8; 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; @@ -73,8 +75,13 @@ GaugeMetricProducer::GaugeMetricProducer( 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 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, wizard, eventActivationMap, + eventDeactivationMap, slicedStateAtoms, stateGroupMap), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), @@ -115,10 +122,6 @@ GaugeMetricProducer::GaugeMetricProducer( 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; @@ -127,10 +130,9 @@ GaugeMetricProducer::GaugeMetricProducer( 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. @@ -139,8 +141,11 @@ GaugeMetricProducer::GaugeMetricProducer( mBucketSizeNs); } - // Adjust start for partial bucket + // Adjust start for partial first bucket and then pull if needed mCurrentBucketStartTimeNs = startTimeNs; + if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { + pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); + } VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs, @@ -164,10 +169,9 @@ void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { (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()); } } } @@ -194,7 +198,7 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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; } @@ -209,23 +213,25 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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); } @@ -242,22 +248,9 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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). @@ -307,11 +300,6 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, } } -void GaugeMetricProducer::prepareFirstBucketLocked() { - if (mIsActive && mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { - pullAndMatchEventsLocked(mCurrentBucketStartTimeNs); - } -} void GaugeMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { bool triggerPuller = false; @@ -451,6 +439,7 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { 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; } } @@ -460,8 +449,8 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { 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; } @@ -569,7 +558,10 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, info.mBucketEndNs = fullBucketEndTimeNs; } - 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]; @@ -578,7 +570,13 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, slice.first.toString().c_str()); } } else { - mSkippedBuckets.emplace_back(info.mBucketStartNs, info.mBucketEndNs); + mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs; + mCurrentSkippedBucket.bucketEndTimeNs = eventTimeNs; + 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. @@ -597,6 +595,7 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, 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 a612adf8a38b..284bcc5d10aa 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -56,13 +56,17 @@ typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>> // 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 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 = {}, + const vector<int>& slicedStateAtoms = {}, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); virtual ~GaugeMetricProducer(); @@ -91,8 +95,8 @@ public: 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 +129,6 @@ private: void flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) override; - void prepareFirstBucketLocked() override; - void pullAndMatchEventsLocked(const int64_t timestampNs); const int mWhatMatcherIndex; @@ -156,9 +158,6 @@ private: // 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 diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 92752b29ecda..5c29cb3c27fe 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; @@ -40,6 +44,33 @@ const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_ATOM_MATCHER_INDEX = 1; 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 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)), + 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; @@ -52,38 +83,58 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo 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<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; + for (auto atomId : mSlicedStateAtoms) { + FieldValue value; + if (statePrimaryKeys.find(atomId) != statePrimaryKeys.end()) { + // found a primary key for this state, query using the key + getMappedStateValue(atomId, statePrimaryKeys[atomId], &value); + } else { + // if no MetricStateLinks exist for this state atom, + // query using the default dimension key (empty HashableDimensionKey) + getMappedStateValue(atomId, DEFAULT_DIMENSION_KEY, &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) { @@ -111,24 +162,6 @@ void MetricProducer::flushIfExpire(int64_t elapsedTimestampNs) { } } -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()) { @@ -232,6 +265,43 @@ void MetricProducer::writeActiveMetricToProtoOutputStream( } } +void MetricProducer::getMappedStateValue(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; + } + + // 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); + } +} + +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 09ad2903fa4c..99f0c64bd47c 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -17,19 +17,20 @@ #ifndef METRIC_PRODUCER_H #define METRIC_PRODUCER_H -#include <shared_mutex> - #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> +#include <log/logprint.h> +#include <utils/RefBase.h> + +#include <unordered_map> + #include "HashableDimensionKey.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" #include "config/ConfigKey.h" #include "matchers/matcher_util.h" #include "packages/PackageInfoListener.h" - -#include <log/logprint.h> -#include <utils/RefBase.h> -#include <unordered_map> +#include "state/StateListener.h" +#include "state/StateManager.h" namespace android { namespace os { @@ -69,29 +70,70 @@ enum DumpLatency { 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 +}; + +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 PackageInfoListener { +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 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(){}; @@ -105,8 +147,8 @@ public: * the flush again when the end timestamp is forced to be now, and then after flushing, update * the start timestamp to be now. */ - void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version) override { + virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, + const int64_t version) { std::lock_guard<std::mutex> lock(mMutex); if (eventTimeNs > getCurrentBucketEndTimeNs()) { @@ -119,16 +161,11 @@ public: // is a partial bucket and can merge it with the previous bucket. }; - void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override{ + void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) { // Force buckets to split on removal also. notifyAppUpgrade(eventTimeNs, apk, uid, 0); }; - void onUidMapReceived(const int64_t& eventTimeNs) override{ - // Purposefully don't flush partial buckets on a new snapshot. - // This occurs if a new user is added/removed or statsd crashes. - }; - // 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); @@ -150,6 +187,9 @@ public: return mConditionSliced; }; + void onStateChanged(const int64_t eventTimeNs, const int32_t atomId, + const HashableDimensionKey& primaryKey, int oldState, int 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, @@ -168,11 +208,6 @@ public: return clearPastBucketsLocked(dumpTimeNs); } - void dumpStates(FILE* out, bool verbose) const { - std::lock_guard<std::mutex> lock(mMutex); - dumpStatesLocked(out, verbose); - } - // Returns the memory in bytes currently used to store this metric's data. Does not change // state. size_t byteSize() const { @@ -180,34 +215,9 @@ public: 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) { - 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) { + void dumpStates(FILE* out, bool verbose) const { std::lock_guard<std::mutex> lock(mMutex); - loadActiveMetricLocked(activeMetric, currentTimeNs); + dumpStatesLocked(out, verbose); } // Let MetricProducer drop in-memory data to save memory. @@ -219,9 +229,9 @@ public: 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) { @@ -239,44 +249,43 @@ public: 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); + // Start: getters/setters + inline const int64_t& getMetricId() const { + return mMetricId; + } - void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs); - void cancelEventActivationLocked(int deactivationTrackerIndex); + // For test only. + inline int64_t getCurrentBucketNum() const { + return mCurrentBucketNum; + } - inline bool isActiveLocked() const { - return mIsActive; + int64_t getBucketSizeInNs() const { + std::lock_guard<std::mutex> lock(mMutex); + return mBucketSizeNs; } - void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs); + inline const std::vector<int> getSlicedStateAtoms() { + std::lock_guard<std::mutex> lock(mMutex); + return mSlicedStateAtoms; + } - virtual void prepareFirstBucketLocked() {}; + /* 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. @@ -284,14 +293,6 @@ protected: 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 @@ -304,12 +305,65 @@ protected: 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 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 { @@ -320,7 +374,17 @@ protected: return (endNs - mTimeBaseNs) / mBucketSizeNs - 1; } - virtual void dropDataLocked(const int64_t dropTimeNs) = 0; + // Query StateManager for original state value. + // If no state map exists for this atom, return the original value. + // Otherwise, return the group_id mapped to the atom and original value. + void getMappedStateValue(const int32_t atomId, const HashableDimensionKey& queryKey, + FieldValue* value); + + 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; @@ -342,21 +406,17 @@ protected: 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. @@ -366,43 +426,8 @@ protected: 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; @@ -412,6 +437,25 @@ protected: bool mIsActive; + // The slice_by_state atom ids defined in statsd_config. + std::vector<int32_t> mSlicedStateAtoms; + + // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>). + 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(DurationMetricE2eTest, TestOneBucket); FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); @@ -431,6 +475,10 @@ protected: FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes); FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); + + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 7b7d0cac0d30..088f607ecfce 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -15,8 +15,12 @@ */ #define DEBUG false // STOPSHIP if true #include "Log.h" + #include "MetricsManager.h" -#include "statslog.h" + +#include <log/logprint.h> +#include <private/android_filesystem_config.h> +#include <utils/SystemClock.h> #include "CountMetricProducer.h" #include "atoms_info.h" @@ -26,12 +30,10 @@ #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 <log/logprint.h> -#include <private/android_filesystem_config.h> -#include <utils/SystemClock.h> +#include "stats_util.h" +#include "statslog.h" using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_INT32; @@ -150,6 +152,12 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, } MetricsManager::~MetricsManager() { + for (auto it : mAllMetricProducers) { + for (int atomId : it->getSlicedStateAtoms()) { + StateManager::getInstance().unregisterListener(atomId, it); + } + } + VLOG("~MetricsManager()"); } @@ -175,6 +183,10 @@ bool MetricsManager::isConfigValid() const { 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); + } // check if we care this package if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) { return; @@ -186,6 +198,10 @@ void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& 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); + } // check if we care this package if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) { return; @@ -196,6 +212,9 @@ void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& } 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. if (mAllowedPkg.size() == 0) { return; } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 8efca1e10de5..6d20822fd54c 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -35,7 +35,7 @@ namespace os { namespace statsd { // A MetricsManager is responsible for managing metrics from one single config source. -class MetricsManager : public PackageInfoListener { +class MetricsManager : public virtual android::RefBase { public: MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs, const int64_t currentTimeNs, const sp<UidMap>& uidMap, @@ -63,15 +63,11 @@ public: unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet); void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version) override; + const int64_t version); - void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override; + void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid); - void onUidMapReceived(const int64_t& eventTimeNs) override; - - bool shouldAddUidMapListener() const { - return !mAllowedPkg.empty(); - } + void onUidMapReceived(const int64_t& eventTimeNs); bool shouldWriteToDisk() const { return mNoReportMetricIds.size() != mAllMetricProducers.size(); @@ -260,17 +256,6 @@ private: 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); @@ -293,12 +278,21 @@ private: TestActivationOnBootMultipleActivationsDifferentActivationTypes); FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); + 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(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 0e33a0f9f29b..d2db6e9c9ead 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -51,19 +51,22 @@ const int FIELD_ID_VALUE_METRICS = 7; 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; @@ -82,8 +85,13 @@ ValueMetricProducer::ValueMetricProducer( const ConfigKey& key, const ValueMetric& metric, 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) - : 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, conditionWizard, + eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), @@ -109,7 +117,7 @@ ValueMetricProducer::ValueMetricProducer( 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()) { @@ -125,10 +133,7 @@ ValueMetricProducer::ValueMetricProducer( 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) { @@ -139,11 +144,16 @@ ValueMetricProducer::ValueMetricProducer( 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; @@ -160,6 +170,15 @@ ValueMetricProducer::ValueMetricProducer( // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs); + + // Kicks off the puller immediately if condition is true and diff based. + if (mIsActive && mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { + pullAndMatchEventsLocked(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); } @@ -171,14 +190,31 @@ ValueMetricProducer::~ValueMetricProducer() { } } -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, int oldState, + int 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, newState); + // If condition is not true, we do not need to pull for this state change. + if (mCondition != ConditionState::kTrue) { + return; } - // Now that activations are processed, start the condition timer if needed. - mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue, - mCurrentBucketStartTimeNs); + 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, @@ -188,11 +224,9 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition 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); } @@ -218,10 +252,10 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, if (pullNeeded) { switch (dumpLatency) { case FAST: - invalidateCurrentBucket(); + invalidateCurrentBucket(dumpTimeNs, BucketDropReason::DUMP_REPORT_REQUESTED); break; case NO_TIME_CONSTRAINTS: - pullAndMatchEventsLocked(dumpTimeNs, mCondition); + pullAndMatchEventsLocked(dumpTimeNs); break; } } @@ -243,23 +277,26 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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); } @@ -275,21 +312,17 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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). @@ -311,7 +344,7 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, 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( @@ -346,23 +379,33 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, } } -void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase() { +void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase(const int64_t dropTimeNs, + const BucketDropReason reason) { if (!mCurrentBucketIsInvalid) { - // Only report once per invalid bucket. + // Only report to StatsdStats once per invalid bucket. StatsdStats::getInstance().noteInvalidatedBucket(mMetricId); + + mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs; + mCurrentSkippedBucket.bucketEndTimeNs = getCurrentBucketEndTimeNs(); + } + + if (!maxDropEventsReached()) { + mCurrentSkippedBucket.dropEvents.emplace_back(buildDropEvent(dropTimeNs, reason)); } mCurrentBucketIsInvalid = true; } -void ValueMetricProducer::invalidateCurrentBucket() { - invalidateCurrentBucketWithoutResetBase(); +void ValueMetricProducer::invalidateCurrentBucket(const int64_t dropTimeNs, + const BucketDropReason reason) { + invalidateCurrentBucketWithoutResetBase(dropTimeNs, reason); resetBase(); } 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; + baseInfo.hasCurrentState = false; } } mHasGlobalBase = false; @@ -374,9 +417,10 @@ void ValueMetricProducer::resetBase() { // - 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. @@ -389,7 +433,7 @@ void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) // 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. @@ -409,65 +453,73 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, 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(); - } + // If the config is not active, skip the event. + if (!mIsActive) { + mCondition = isEventTooLate ? ConditionState::kUnknown : newCondition; + return; + } - // 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); - } + // 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; + } - // 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(); - } - } + // 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); } - mCondition = isEventTooLate ? initialCondition(mConditionTrackerIndex) : newCondition; + // 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); + } - if (mIsActive) { - flushIfNeededLocked(eventTimeNs); - mConditionTimer.onConditionChanged(mCondition, 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::pullAndMatchEventsLocked(const int64_t timestampNs, - ConditionState condition) { +void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { 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(); + invalidateCurrentBucket(timestampNs, BucketDropReason::PULL_FAILED); return; } - accumulateEvents(allData, timestampNs, timestampNs, condition); + accumulateEvents(allData, timestampNs, timestampNs); } int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) { @@ -480,33 +532,33 @@ int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTime void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, bool pullSuccess, int64_t originalPullTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - if (mCondition == 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. @@ -514,18 +566,18 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven } 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, @@ -533,7 +585,7 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log 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; } @@ -551,14 +603,20 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log 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; + baseInfo.hasCurrentState = false; } } } @@ -572,7 +630,7 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log // 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(); } } @@ -587,10 +645,10 @@ void ValueMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { 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()); } } } @@ -658,6 +716,7 @@ bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) ret.setDouble(value.mValue.double_value); break; default: + return false; break; } return true; @@ -666,17 +725,30 @@ bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) 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. @@ -701,10 +773,26 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn 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()); + baseInfos.resize(mFieldMatchers.size()); + } + + for (auto baseInfo : baseInfos) { + if (!baseInfo.hasCurrentState) { + baseInfo.currentState = DEFAULT_DIMENSION_KEY; + 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()); - multiIntervals.resize(mFieldMatchers.size()); + intervals.resize(mFieldMatchers.size()); } // We only use anomaly detection under certain cases. @@ -717,7 +805,8 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn 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; if (!getDoubleOrLong(event, matcher, value)) { @@ -728,60 +817,61 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn 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,12 +896,13 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn interval.hasValue = true; } interval.sampleSize += 1; + baseInfo.currentState = stateKey; } // 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; @@ -828,7 +919,7 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn 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; } @@ -857,7 +948,8 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, 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::MULTIPLE_BUCKETS_SKIPPED); } VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, @@ -867,6 +959,18 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, // Close the current bucket. int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime); bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs; + if (!isBucketLargeEnough) { + // If the bucket is valid, this is the only drop reason and we need to + // set the skipped bucket start and end times. + if (!mCurrentBucketIsInvalid) { + mCurrentSkippedBucket.bucketStartTimeNs = mCurrentBucketStartTimeNs; + mCurrentSkippedBucket.bucketEndTimeNs = bucketEndTime; + } + if (!maxDropEventsReached()) { + mCurrentSkippedBucket.dropEvents.emplace_back( + buildDropEvent(eventTimeNs, BucketDropReason::BUCKET_TOO_SMALL)); + } + } if (isBucketLargeEnough && !mCurrentBucketIsInvalid) { // The current bucket is large enough to keep. for (const auto& slice : mCurrentSlicedBucket) { @@ -879,7 +983,7 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, } } } else { - mSkippedBuckets.emplace_back(mCurrentBucketStartTimeNs, bucketEndTime); + mSkippedBuckets.emplace_back(mCurrentSkippedBucket); } appendToFullBucket(eventTimeNs, fullBucketEndTimeNs); @@ -932,9 +1036,12 @@ void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) } else { it++; } + // TODO: remove mCurrentBaseInfo entries when obsolete } mCurrentBucketIsInvalid = 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) { diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 739f6ef07cc4..19fb6942928f 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -52,12 +52,17 @@ struct ValueBucket { // - 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 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(); @@ -73,16 +78,19 @@ public: return; } if (mIsPulled && mCondition) { - pullAndMatchEventsLocked(eventTimeNs, mCondition); + pullAndMatchEventsLocked(eventTimeNs); } flushCurrentBucketLocked(eventTimeNs, eventTimeNs); }; + void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey, + int oldState, int 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, @@ -116,8 +124,6 @@ private: void flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) override; - void prepareFirstBucketLocked() override; - void dropDataLocked(const int64_t dropTimeNs) override; // Calculate previous bucket end time based on current time. @@ -127,8 +133,9 @@ private: 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); const int mWhatMatcherIndex; @@ -140,7 +147,10 @@ private: 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; @@ -152,10 +162,6 @@ private: 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. @@ -167,16 +173,26 @@ private: 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. @@ -185,11 +201,10 @@ private: 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); @@ -246,7 +261,6 @@ private: 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); @@ -256,10 +270,6 @@ private: 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); @@ -290,9 +300,19 @@ private: 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, 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 class ValueMetricProducerTestHelper; }; diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 081e61ed21fa..afe93d445e1d 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -60,7 +60,7 @@ 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, + 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,7 +70,6 @@ public: mWizard(wizard), mConditionTrackerIndex(conditionIndex), mBucketSizeNs(bucketSizeNs), - mDimensionInCondition(dimensionInCondition), mNested(nesting), mCurrentBucketStartTimeNs(currentBucketStartNs), mDuration(0), @@ -83,8 +82,6 @@ public: 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 noteStop(const HashableDimensionKey& key, const int64_t eventTime, @@ -180,8 +177,6 @@ protected: const int64_t mBucketSizeNs; - const std::vector<Matcher>& mDimensionInCondition; - const bool mNested; int64_t mCurrentBucketStartTimeNs; @@ -196,7 +191,6 @@ protected: 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 6868b8c24c71..2be5855e90e6 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -27,36 +27,14 @@ namespace statsd { MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, - const vector<Matcher>& dimensionInCondition, bool nesting, + 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, + : DurationTracker(key, id, eventKey, wizard, conditionIndex, 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; - } } bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { @@ -252,17 +230,11 @@ void MaxDurationTracker::onSlicedConditionMayChange(bool overallCondition, 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); } diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index 8e8f2cd6c582..efb8dc70afd1 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -30,7 +30,7 @@ class MaxDurationTracker : public DurationTracker { 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 @@ public: 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, diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index 956383a99eea..57f39656fdfe 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -26,27 +26,16 @@ using std::pair; OringDurationTracker::OringDurationTracker( const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, - sp<ConditionWizard> wizard, int conditionIndex, const vector<Matcher>& dimensionInCondition, + 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, + : 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) { @@ -227,17 +216,10 @@ void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition, ++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()); @@ -262,17 +244,10 @@ void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition, ++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()); diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index 8e73256d4a01..c3aad668aa78 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -29,7 +29,7 @@ class OringDurationTracker : public DurationTracker { public: OringDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard, - int conditionIndex, const std::vector<Matcher>& dimensionInCondition, + int conditionIndex, bool nesting, int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced, bool fullLink, @@ -37,8 +37,6 @@ public: 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, diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 46442b57126c..73c121242523 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -19,24 +19,26 @@ #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 <inttypes.h> #include "atoms_info.h" +#include "FieldValue.h" +#include "MetricProducer.h" +#include "condition/CombinationConditionTracker.h" +#include "condition/SimpleConditionTracker.h" +#include "condition/StateConditionTracker.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" -#include <inttypes.h> - using std::set; using std::string; using std::unordered_map; @@ -137,6 +139,105 @@ bool handleMetricWithConditions( 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) { @@ -184,13 +285,13 @@ bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap, } /** - * A StateTracker is built from a SimplePredicate which has only "start", and no "stop" + * A StateConditionTracker 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. + * field. For example, the StateConditionTracker is used in tracking UidProcessState and ScreenState. * */ -bool isStateTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) { +bool isStateConditionTracker(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( @@ -242,8 +343,8 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, 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, + if (isStateConditionTracker(condition.simple_predicate(), &primaryKeys)) { + allConditionTrackers.push_back(new StateConditionTracker(key, condition.id(), index, condition.simple_predicate(), logTrackerMap, primaryKeys)); } else { @@ -285,24 +386,61 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, 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, UidMap& uidMap, + 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, 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 for (int i = 0; i < config.count_metric_size(); i++) { @@ -324,10 +462,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t 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 { @@ -337,8 +474,31 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } - sp<MetricProducer> countProducer = - new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs); + 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, + eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap); allMetricProducers.push_back(countProducer); } @@ -406,9 +566,18 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + 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); + trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, + currentTimeNs, eventActivationMap, eventDeactivationMap); allMetricProducers.push_back(durationMetric); } @@ -443,8 +612,17 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } - sp<MetricProducer> eventMetric = - new EventMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs); + 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, eventActivationMap, + eventDeactivationMap); allMetricProducers.push_back(eventMetric); } @@ -500,9 +678,41 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + 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); + timeBaseTimeNs, currentTimeNs, pullerManager, eventActivationMap, + eventDeactivationMap, slicedStateAtoms, stateGroupMap); allMetricProducers.push_back(valueProducer); } @@ -586,10 +796,19 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + 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); + timeBaseTimeNs, currentTimeNs, pullerManager, + eventActivationMap, eventDeactivationMap); allMetricProducers.push_back(gaugeProducer); } for (int i = 0; i < config.no_report_metric_size(); ++i) { @@ -601,7 +820,12 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t noReportMetricIds.insert(no_report_metric); } for (const auto& it : allMetricProducers) { - uidMap.addListener(it); + // Register metrics to StateTrackers + for (int atomId : it->getSlicedStateAtoms()) { + if (!StateManager::getInstance().registerListener(atomId, it)) { + return false; + } + } } return true; } @@ -707,73 +931,6 @@ bool initAlarms(const StatsdConfig& config, const ConfigKey& key, 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, @@ -794,6 +951,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& unordered_map<int64_t, int> logTrackerMap; unordered_map<int64_t, int> conditionTrackerMap; 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"); @@ -806,11 +965,16 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& ALOGE("initConditionTrackers failed"); return false; } - - if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap, - conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers, + if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) { + ALOGE("initStates failed"); + return false; + } + if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap, + conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps, + allConditionTrackers, allMetricProducers, conditionToMetricMap, trackerToMetricMap, metricProducerMap, - noReportMetricIds)) { + noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation)) { ALOGE("initMetricProducers failed"); return false; } @@ -824,14 +988,6 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& 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 028231ff908c..95b2ab81fa53 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -68,6 +68,17 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, std::unordered_map<int, std::vector<int>>& trackerToConditionMap, std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks); +// 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: // [key]: the config key that this config belongs to @@ -75,6 +86,9 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, // [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 @@ -87,11 +101,16 @@ bool initMetrics( 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, 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. @@ -113,7 +132,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds); -bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys); +bool isStateConditionTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys); } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index d4b57dd68134..7e63bbff2d0a 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -119,7 +119,7 @@ int64_t UidMap::getAppVersion(int uid, const string& packageName) const { void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, const vector<int64_t>& versionCode, const vector<String16>& versionString, const vector<String16>& packageName, const vector<String16>& installer) { - vector<wp<PackageInfoListener>> broadcastList; + wp<PackageInfoListener> broadcast = NULL; { lock_guard<mutex> lock(mMutex); // Exclusively lock for updates. @@ -150,25 +150,22 @@ void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid, ensureBytesUsedBelowLimit(); StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); - getListenerListCopyLocked(&broadcastList); + broadcast = mSubscriber; } // To avoid invoking callback while holding the internal lock. we get a copy of the listener - // list and invoke the callback. It's still possible that after we copy the list, a - // listener removes itself before we call it. It's then the listener's job to handle it (expect - // the callback to be called after listener is removed, and the listener should properly - // ignore it). - for (const auto& weakPtr : broadcastList) { - auto strongPtr = weakPtr.promote(); - if (strongPtr != NULL) { - strongPtr->onUidMapReceived(timestamp); - } + // and invoke the callback. It's still possible that after we copy the listener, it removes + // itself before we call it. It's then the listener's job to handle it (expect the callback to + // be called after listener is removed, and the listener should properly ignore it). + auto strongPtr = broadcast.promote(); + if (strongPtr != NULL) { + strongPtr->onUidMapReceived(timestamp); } } void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid, const int64_t& versionCode, const String16& versionString, const String16& installer) { - vector<wp<PackageInfoListener>> broadcastList; + wp<PackageInfoListener> broadcast = NULL; string appName = string(String8(app_16).string()); { lock_guard<mutex> lock(mMutex); @@ -195,7 +192,7 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i // for the first time, then we don't notify the listeners. // It's also OK to split again if we're forming a partial bucket after re-installing an // app after deletion. - getListenerListCopyLocked(&broadcastList); + broadcast = mSubscriber; } mChanges.emplace_back(false, timestamp, appName, uid, versionCode, newVersionString, prevVersion, prevVersionString); @@ -205,11 +202,9 @@ void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const i StatsdStats::getInstance().setUidMapChanges(mChanges.size()); } - for (const auto& weakPtr : broadcastList) { - auto strongPtr = weakPtr.promote(); - if (strongPtr != NULL) { - strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode); - } + auto strongPtr = broadcast.promote(); + if (strongPtr != NULL) { + strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode); } } @@ -230,21 +225,8 @@ void UidMap::ensureBytesUsedBelowLimit() { } } -void UidMap::getListenerListCopyLocked(vector<wp<PackageInfoListener>>* output) { - for (auto weakIt = mSubscribers.begin(); weakIt != mSubscribers.end();) { - auto strongPtr = weakIt->promote(); - if (strongPtr != NULL) { - output->push_back(*weakIt); - weakIt++; - } else { - weakIt = mSubscribers.erase(weakIt); - VLOG("The UidMap listener is gone, remove it now"); - } - } -} - void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) { - vector<wp<PackageInfoListener>> broadcastList; + wp<PackageInfoListener> broadcast = NULL; string app = string(String8(app_16).string()); { lock_guard<mutex> lock(mMutex); @@ -271,25 +253,18 @@ void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const i ensureBytesUsedBelowLimit(); StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed); StatsdStats::getInstance().setUidMapChanges(mChanges.size()); - getListenerListCopyLocked(&broadcastList); + broadcast = mSubscriber; } - for (const auto& weakPtr : broadcastList) { - auto strongPtr = weakPtr.promote(); - if (strongPtr != NULL) { - strongPtr->notifyAppRemoved(timestamp, app, uid); - } + auto strongPtr = broadcast.promote(); + if (strongPtr != NULL) { + strongPtr->notifyAppRemoved(timestamp, app, uid); } } -void UidMap::addListener(wp<PackageInfoListener> producer) { - lock_guard<mutex> lock(mMutex); // Lock for updates - mSubscribers.insert(producer); -} - -void UidMap::removeListener(wp<PackageInfoListener> producer) { +void UidMap::setListener(wp<PackageInfoListener> listener) { lock_guard<mutex> lock(mMutex); // Lock for updates - mSubscribers.erase(producer); + mSubscriber = listener; } void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) { diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index a7c5fb27375c..2d3f6ee9c2e8 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -118,12 +118,10 @@ public: // adb shell cmd stats print-uid-map void printUidMap(int outFd) const; - // Commands for indicating to the map that a producer should be notified if an app is updated. - // This allows the metric producer to distinguish when the same uid or app represents a - // different version of an app. - void addListener(wp<PackageInfoListener> producer); - // Remove the listener from the set of metric producers that subscribe to updates. - void removeListener(wp<PackageInfoListener> producer); + // Command for indicating to the map that StatsLogProcessor should be notified if an app is + // updated. This allows metric producers and managers to distinguish when the same uid or app + // represents a different version of an app. + void setListener(wp<PackageInfoListener> listener); // Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date. void OnConfigUpdated(const ConfigKey& key); @@ -167,8 +165,6 @@ private: std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const; string normalizeAppName(const string& appName) const; - void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output); - void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings, bool includeInstaller, const std::set<int32_t>& interestingUids, std::set<string>* str_set, ProtoOutputStream* proto); @@ -195,8 +191,8 @@ private: // Store which uid and apps represent deleted ones. std::list<std::pair<int, string>> mDeletedApps; - // Metric producers that should be notified if there's an upgrade in any app. - set<wp<PackageInfoListener>> mSubscribers; + // Notify StatsLogProcessor if there's an upgrade/removal in any app. + wp<PackageInfoListener> mSubscriber; // Mapping of config keys we're aware of to the epoch time they last received an update. This // lets us know it's safe to delete events older than the oldest update. The value is nanosec. diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp index b59d88dc1cea..4308a1107039 100755 --- a/cmds/statsd/src/socket/StatsSocketListener.cpp +++ b/cmds/statsd/src/socket/StatsSocketListener.cpp @@ -39,8 +39,6 @@ namespace android { namespace os { namespace statsd { -static const int kLogMsgHeaderSize = 28; - StatsSocketListener::StatsSocketListener(std::shared_ptr<LogEventQueue> queue) : SocketListener(getLogSocket(), false /*start listen*/), mQueue(queue) { } @@ -95,7 +93,7 @@ bool StatsSocketListener::onDataAvailable(SocketClient* cli) { 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 @@ -124,18 +122,13 @@ bool StatsSocketListener::onDataAvailable(SocketClient* cli) { } } - 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; int64_t oldestTimestamp; - if (!mQueue->push(std::make_unique<LogEvent>(msg), &oldestTimestamp)) { + if (!mQueue->push(std::make_unique<LogEvent>(msg, len, uid), &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 000000000000..d1af1968ac38 --- /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, int oldState, + int 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 000000000000..ea776fae0583 --- /dev/null +++ b/cmds/statsd/src/state/StateManager.cpp @@ -0,0 +1,92 @@ +/* + * 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" + +namespace android { +namespace os { +namespace statsd { + +StateManager& StateManager::getInstance() { + static StateManager sStateManager; + return sStateManager; +} + +void StateManager::clear() { + mStateTrackers.clear(); +} + +void StateManager::onLogEvent(const LogEvent& event) { + if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) { + mStateTrackers[event.GetTagId()]->onLogEvent(event); + } +} + +bool StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) { + // Check if state tracker already exists. + if (mStateTrackers.find(atomId) == mStateTrackers.end()) { + // Create a new state tracker iff atom is a state atom. + auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId); + if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) { + mStateTrackers[atomId] = new StateTracker(atomId, it->second); + } else { + ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId); + return false; + } + } + mStateTrackers[atomId]->registerListener(listener); + return true; +} + +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; +} + +} // 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 000000000000..8bc24612be90 --- /dev/null +++ b/cmds/statsd/src/state/StateManager.h @@ -0,0 +1,86 @@ +/* + * 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 <gtest/gtest_prod.h> +#include <inttypes.h> +#include <utils/RefBase.h> + +#include "HashableDimensionKey.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); + + // Returns true if atomId is being tracked and is associated with a state + // atom. StateManager notifies the correct StateTracker to register listener. + // If the correct StateTracker does not exist, a new StateTracker is created. + bool 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; + + 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; +}; + +} // 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 000000000000..3ad21e0c96ae --- /dev/null +++ b/cmds/statsd/src/state/StateTracker.cpp @@ -0,0 +1,183 @@ +/* + * 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, const util::StateAtomFieldOptions& stateAtomInfo) + : mAtomId(atomId), mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) { + // create matcher for each primary field + for (const auto& primaryField : stateAtomInfo.primaryFields) { + if (primaryField == util::FIRST_UID_IN_CHAIN) { + Matcher matcher = getFirstUidMatcher(atomId); + mPrimaryFields.push_back(matcher); + } else { + Matcher matcher = getSimpleMatcher(atomId, primaryField); + mPrimaryFields.push_back(matcher); + } + } + + // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting +} + +void StateTracker::onLogEvent(const LogEvent& event) { + int64_t eventTimeNs = event.GetElapsedTimestampNs(); + + // Parse event for primary field values i.e. primary key. + HashableDimensionKey primaryKey; + if (mPrimaryFields.size() > 0) { + if (!filterValues(mPrimaryFields, event.getValues(), &primaryKey) || + primaryKey.getValues().size() != mPrimaryFields.size()) { + ALOGE("StateTracker error extracting primary key from log event."); + handleReset(eventTimeNs); + return; + } + } else { + // Use an empty HashableDimensionKey if atom has no primary fields. + primaryKey = DEFAULT_DIMENSION_KEY; + } + + // Parse event for state value. + FieldValue stateValue; + int32_t state; + if (!filterValues(mStateField, event.getValues(), &stateValue) || + stateValue.mValue.getType() != INT) { + ALOGE("StateTracker error extracting state from log event. Type: %d", + stateValue.mValue.getType()); + handlePartialReset(eventTimeNs, primaryKey); + return; + } + state = stateValue.mValue.int_value; + + if (state == mResetState) { + VLOG("StateTracker Reset state: %s", stateValue.mValue.toString().c_str()); + handleReset(eventTimeNs); + } + + // Track and update state. + int32_t oldState = 0; + int32_t newState = 0; + updateState(primaryKey, state, &oldState, &newState); + + // Notify all listeners if state has changed. + if (oldState != newState) { + VLOG("StateTracker updated state"); + for (auto listener : mListeners) { + auto sListener = listener.promote(); // safe access to wp<> + if (sListener != nullptr) { + sListener->onStateChanged(eventTimeNs, mAtomId, primaryKey, oldState, newState); + } + } + } else { + VLOG("StateTracker NO updated state"); + } +} + +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 = mStateField.mMatcher; + + // Check that the query key has the correct number of primary fields. + if (queryKey.getValues().size() == mPrimaryFields.size()) { + auto it = mStateMap.find(queryKey); + if (it != mStateMap.end()) { + output->mValue = it->second.state; + return true; + } + } else if (queryKey.getValues().size() > mPrimaryFields.size()) { + ALOGE("StateTracker query key size > primary key size is illegal"); + } else { + ALOGE("StateTracker query key size < primary key size is not supported"); + } + + // Set the state value to unknown if: + // - query key size is incorrect + // - query key is not found in state map + output->mValue = StateTracker::kStateUnknown; + return false; +} + +void StateTracker::handleReset(const int64_t eventTimeNs) { + VLOG("StateTracker handle reset"); + for (const auto pair : mStateMap) { + for (auto l : mListeners) { + auto sl = l.promote(); + if (sl != nullptr) { + sl->onStateChanged(eventTimeNs, mAtomId, pair.first, pair.second.state, + mDefaultState); + } + } + } + mStateMap.clear(); +} + +void StateTracker::handlePartialReset(const int64_t eventTimeNs, + const HashableDimensionKey& primaryKey) { + VLOG("StateTracker handle partial reset"); + if (mStateMap.find(primaryKey) != mStateMap.end()) { + for (auto l : mListeners) { + auto sl = l.promote(); + if (sl != nullptr) { + sl->onStateChanged(eventTimeNs, mAtomId, primaryKey, + mStateMap.find(primaryKey)->second.state, mDefaultState); + } + } + mStateMap.erase(primaryKey); + } +} + +void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int32_t eventState, + int32_t* oldState, int32_t* newState) { + // get old state (either current state in map or default state) + auto it = mStateMap.find(primaryKey); + if (it != mStateMap.end()) { + *oldState = it->second.state; + } else { + *oldState = mDefaultState; + } + + // update state map + if (eventState == mDefaultState) { + // remove (key, state) pair if state returns to default state + VLOG("\t StateTracker changed to default state") + mStateMap.erase(primaryKey); + } else { + mStateMap[primaryKey].state = eventState; + mStateMap[primaryKey].count = 1; + } + *newState = eventState; + + // TODO(tsaichristine): support atoms with nested counting +} + +} // 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 000000000000..70f16274c7f6 --- /dev/null +++ b/cmds/statsd/src/state/StateTracker.h @@ -0,0 +1,99 @@ +/* + * 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 <atoms_info.h> +#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, const util::StateAtomFieldOptions& stateAtomInfo); + + 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; // state value + int count; // nested count (only used for binary states) + }; + + const int32_t mAtomId; // id of the state atom being tracked + + Matcher mStateField; // matches the atom's exclusive state field + + std::vector<Matcher> mPrimaryFields; // matches the atom's primary fields + + int32_t mDefaultState = kStateUnknown; + + int32_t mResetState = kStateUnknown; + + // 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 default state. + void handleReset(const int64_t eventTimeNs); + + // Reset only the state value mapped to the given primary key to default state. + // Partial resets are used when we only need to update the state of one primary + // key instead of clearing/reseting every key in the map. + void handlePartialReset(const int64_t eventTimeNs, const HashableDimensionKey& primaryKey); + + // Update the StateMap based on the received state value. + // Store the old and new states. + void updateState(const HashableDimensionKey& primaryKey, const int32_t eventState, + int32_t* oldState, int32_t* newState); +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index b87547056ad3..c45274e4a3de 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -41,6 +41,15 @@ message DimensionsValueTuple { 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 CountBucketInfo { 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,13 @@ message DurationBucketInfo { message DurationMetricData { optional DimensionsValue dimensions_in_what = 1; - optional DimensionsValue dimensions_in_condition = 2; + optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; repeated DurationBucketInfo bucket_info = 3; repeated DimensionsValue dimension_leaf_values_in_what = 4; - repeated DimensionsValue dimension_leaf_values_in_condition = 5; + repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; } message ValueBucketInfo { @@ -136,13 +147,15 @@ message ValueBucketInfo { 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 +179,13 @@ message GaugeBucketInfo { message GaugeMetricData { optional DimensionsValue dimensions_in_what = 1; - optional DimensionsValue dimensions_in_condition = 2; + optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; repeated GaugeBucketInfo bucket_info = 3; repeated DimensionsValue dimension_leaf_values_in_what = 4; - repeated DimensionsValue dimension_leaf_values_in_condition = 5; + repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; } message StatsLogReport { @@ -180,11 +193,40 @@ message StatsLogReport { // 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; + }; + + 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 +262,7 @@ message StatsLogReport { 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 +405,7 @@ message StatsdStatsReport { 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; @@ -483,7 +525,7 @@ message AlertTriggerDetails { 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 67625eb82454..76c193679eef 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -53,6 +53,11 @@ const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8; 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; @@ -416,6 +421,23 @@ void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& value 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 && @@ -445,6 +467,8 @@ int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) { 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: diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index a28165d09cdf..f3e94331a23e 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -40,6 +40,8 @@ void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension, void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers, util::ProtoOutputStream* protoOutput); +void writeStateToProto(const FieldValue& state, util::ProtoOutputStream* protoOutput); + // Convert the TimeUnit enum to the bucket size in millis with a guardrail on // bucket size. int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit); diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 79c06b98a82d..736aa9be2fdb 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -35,7 +35,7 @@ enum Position { 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 @@ enum TimeUnit { SIX_HOURS = 7; TWELVE_HOURS = 8; ONE_DAY = 9; + ONE_WEEK = 10; CTS = 1000; } @@ -150,6 +151,24 @@ message Predicate { } } +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 @@ message MetricConditionLink { 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; @@ -182,11 +209,15 @@ message CountMetric { 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]; } message DurationMetric { @@ -207,7 +238,7 @@ message DurationMetric { optional FieldMatcher dimensions_in_what = 6; - optional FieldMatcher dimensions_in_condition = 8; + optional FieldMatcher dimensions_in_condition = 8 [deprecated = true]; optional TimeUnit bucket = 7; } @@ -225,7 +256,7 @@ message GaugeMetric { optional FieldMatcher dimensions_in_what = 5; - optional FieldMatcher dimensions_in_condition = 8; + optional FieldMatcher dimensions_in_condition = 8 [deprecated = true]; optional TimeUnit bucket = 6; @@ -259,12 +290,14 @@ message ValueMetric { 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; @@ -294,6 +327,8 @@ message ValueMetric { optional int32 max_pull_delay_sec = 16 [default = 10]; optional bool split_bucket_for_app_upgrade = 17 [default = true]; + + optional FieldMatcher dimensions_in_condition = 9 [deprecated = true]; } message Alert { @@ -438,6 +473,8 @@ message StatsdConfig { optional bool persist_locally = 20 [default = false]; + repeated State state = 21; + // Field number 1000 is reserved for later use. reserved 1000; } diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index f2c6f1ad6759..f1320c2f746d 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -52,7 +52,6 @@ const int FIELD_ID_TRIGGER_DETAILS = 4; const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1; const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1; const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2; -const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3; const int FIELD_ID_METRIC_VALUE_VALUE = 4; const int FIELD_ID_PACKAGE_INFO = 3; @@ -84,10 +83,8 @@ void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensio 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); @@ -106,13 +103,6 @@ void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensio } } - 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, diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp index 25d2257c752b..a37cad14fcbc 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; using std::unordered_map; @@ -29,12 +28,32 @@ namespace statsd { using std::vector; +class BroadcastSubscriberDeathRecipient : public android::IBinder::DeathRecipient { + public: + BroadcastSubscriberDeathRecipient(const ConfigKey& configKey, int64_t subscriberId): + mConfigKey(configKey), + mSubscriberId(subscriberId) {} + ~BroadcastSubscriberDeathRecipient() override = default; + private: + ConfigKey mConfigKey; + int64_t mSubscriberId; + + void binderDied(const android::wp<android::IBinder>& who) override { + if (IInterface::asBinder(SubscriberReporter::getInstance().getBroadcastSubscriber( + mConfigKey, mSubscriberId)) == who.promote()) { + SubscriberReporter::getInstance().unsetBroadcastSubscriber(mConfigKey, mSubscriberId); + } + } +}; + void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId, - const sp<IBinder>& intentSender) { + const sp<IPendingIntentRef>& pir) { VLOG("SubscriberReporter::setBroadcastSubscriber called."); lock_guard<std::mutex> lock(mLock); - mIntentMap[configKey][subscriberId] = intentSender; + mIntentMap[configKey][subscriberId] = pir; + IInterface::asBinder(pir)->linkToDeath( + new BroadcastSubscriberDeathRecipient(configKey, subscriberId)); } void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey, @@ -50,12 +69,6 @@ void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey, } } -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 { @@ -97,18 +110,13 @@ void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey, sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey); } -void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender, +void SubscriberReporter::sendBroadcastLocked(const sp<IPendingIntentRef>& pir, const ConfigKey& configKey, const Subscription& subscription, const vector<String16>& 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(), @@ -117,6 +125,20 @@ void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender, getStatsDimensionsValue(dimKey.getDimensionKeyInWhat())); } +sp<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey, + int64_t subscriberId) { + lock_guard<std::mutex> lock(mLock); + auto subscriberMapIt = mIntentMap.find(configKey); + if (subscriberMapIt == mIntentMap.end()) { + return nullptr; + } + auto pirMapIt = subscriberMapIt->second.find(subscriberId); + if (pirMapIt == subscriberMapIt->second.end()) { + return nullptr; + } + return pirMapIt->second; +} + void getStatsDimensionsValueHelper(const vector<FieldValue>& dims, size_t* index, int depth, int prefix, vector<StatsDimensionsValue>* output) { size_t count = dims.size(); diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h index 2a7f771a0ba4..087a1b84b91f 100644 --- a/cmds/statsd/src/subscriber/SubscriberReporter.h +++ b/cmds/statsd/src/subscriber/SubscriberReporter.h @@ -16,6 +16,7 @@ #pragma once +#include <android/os/IPendingIntentRef.h> #include <android/os/IStatsCompanionService.h> #include <utils/RefBase.h> @@ -47,32 +48,17 @@ public: 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 sp<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,6 +68,8 @@ public: const Subscription& subscription, const MetricDimensionKey& dimKey) const; + sp<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId); + static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim); private: @@ -92,15 +80,15 @@ private: /** Binder interface for communicating with StatsCompanionService. */ sp<IStatsCompanionService> mStatsCompanionService = nullptr; - /** Maps <ConfigKey, SubscriberId> -> IBinder (which represents an IIntentSender). */ + /** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */ std::unordered_map<ConfigKey, - std::unordered_map<int64_t, sp<android::IBinder>>> mIntentMap; + std::unordered_map<int64_t, sp<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 sp<IPendingIntentRef>& pir, const ConfigKey& configKey, const Subscription& subscription, const std::vector<String16>& cookies, diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp index f1cad92c336b..f4a59ed14d10 100644 --- a/cmds/statsd/tests/FieldValue_test.cpp +++ b/cmds/statsd/tests/FieldValue_test.cpp @@ -480,6 +480,137 @@ TEST(AtomMatcherTest, TestWriteAtomToProto) { 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); + EXPECT_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); + EXPECT_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/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index 2b9528f7d1de..441d3c896467 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -29,6 +29,7 @@ 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; @@ -297,6 +298,46 @@ TEST(AtomMatcherTest, TestAttributionMatcher) { 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"); + + // Set up the event + LogEvent event(TAG_ID, 0); + event.write(1111); + event.init(); + + LogEvent event2(TAG_ID_2, 0); + event2.write(1111); + event2.write("some value"); + event2.init(); + + // Tag not in kAtomsWithUidField + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Tag found in kAtomsWithUidField and has matching uid + simpleMatcher->set_atom_id(TAG_ID_2); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); + + // Tag found in kAtomsWithUidField but has non-matching uid + simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2)); +} + TEST(AtomMatcherTest, TestNeqAnyStringMatcher) { UidMap uidMap; uidMap.updateMap( diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index 504ee22f72e4..9f50701d5e9e 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -17,6 +17,8 @@ #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 <stats_event.h> + #ifdef __ANDROID__ @@ -25,9 +27,277 @@ namespace os { namespace statsd { using std::string; +using std::vector; using util::ProtoOutputStream; using util::ProtoReader; + +#ifdef NEW_ENCODING_SCHEME + +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); + + // 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); + } + + return f; +} + +TEST(LogEventTest, TestPrimitiveParsing) { + struct stats_event* event = stats_event_obtain(); + stats_event_set_atom_id(event, 100); + stats_event_write_int32(event, 10); + stats_event_write_int64(event, 0x123456789); + stats_event_write_float(event, 2.0); + stats_event_write_bool(event, true); + stats_event_build(event); + + size_t size; + uint8_t* buf = stats_event_get_buffer(event, &size); + + LogEvent logEvent(buf, size, /*uid=*/ 1000); + EXPECT_TRUE(logEvent.isValid()); + EXPECT_EQ(100, logEvent.GetTagId()); + + const vector<FieldValue>& values = logEvent.getValues(); + EXPECT_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); + + stats_event_release(event); +} + + +TEST(LogEventTest, TestStringAndByteArrayParsing) { + struct stats_event* event = stats_event_obtain(); + stats_event_set_atom_id(event, 100); + string str = "test"; + stats_event_write_string8(event, str.c_str()); + stats_event_write_byte_array(event, (uint8_t*)str.c_str(), str.length()); + stats_event_build(event); + + size_t size; + uint8_t* buf = stats_event_get_buffer(event, &size); + + LogEvent logEvent(buf, size, /*uid=*/ 1000); + EXPECT_TRUE(logEvent.isValid()); + EXPECT_EQ(100, logEvent.GetTagId()); + + const vector<FieldValue>& values = logEvent.getValues(); + EXPECT_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); + + stats_event_release(event); +} + +TEST(LogEventTest, TestEmptyString) { + struct stats_event* event = stats_event_obtain(); + stats_event_set_atom_id(event, 100); + string empty = ""; + stats_event_write_string8(event, empty.c_str()); + stats_event_build(event); + + size_t size; + uint8_t* buf = stats_event_get_buffer(event, &size); + + LogEvent logEvent(buf, size, /*uid=*/ 1000); + EXPECT_TRUE(logEvent.isValid()); + EXPECT_EQ(100, logEvent.GetTagId()); + + const vector<FieldValue>& values = logEvent.getValues(); + EXPECT_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(empty, item.mValue.str_value); + + stats_event_release(event); +} + +TEST(LogEventTest, TestByteArrayWithNullCharacter) { + struct stats_event* event = stats_event_obtain(); + stats_event_set_atom_id(event, 100); + uint8_t message[] = {'\t', 'e', '\0', 's', 't'}; + stats_event_write_byte_array(event, message, 5); + stats_event_build(event); + + size_t size; + uint8_t* buf = stats_event_get_buffer(event, &size); + + LogEvent logEvent(buf, size, /*uid=*/ 1000); + EXPECT_TRUE(logEvent.isValid()); + EXPECT_EQ(100, logEvent.GetTagId()); + + const vector<FieldValue>& values = logEvent.getValues(); + EXPECT_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::STORAGE, item.mValue.getType()); + vector<uint8_t> expectedValue(message, message + 5); + EXPECT_EQ(expectedValue, item.mValue.storage_value); + + stats_event_release(event); +} + +TEST(LogEventTest, TestKeyValuePairs) { + struct stats_event* event = stats_event_obtain(); + stats_event_set_atom_id(event, 100); + + struct key_value_pair pairs[4]; + pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = 1}; + pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789}; + pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 2.0}; + string str = "test"; + pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()}; + + stats_event_write_key_value_pairs(event, pairs, 4); + stats_event_build(event); + + size_t size; + uint8_t* buf = stats_event_get_buffer(event, &size); + + LogEvent logEvent(buf, size, /*uid=*/ 1000); + EXPECT_TRUE(logEvent.isValid()); + EXPECT_EQ(100, logEvent.GetTagId()); + + const vector<FieldValue>& values = logEvent.getValues(); + EXPECT_EQ(8, values.size()); // 2 FieldValues per key-value pair + + // Check the keys first + for (int i = 0; i < values.size() / 2; i++) { + const FieldValue& item = values[2 * i]; + int32_t depth1Pos = i + 1; + bool depth1Last = i == (values.size() / 2 - 1); + Field expectedField = getField(100, {1, depth1Pos, 1}, 2, {true, depth1Last, false}); + + EXPECT_EQ(expectedField, item.mField); + EXPECT_EQ(Type::INT, item.mValue.getType()); + EXPECT_EQ(i, item.mValue.int_value); + } + + // Check the values now + // Note: pos[2] = index of type in KeyValuePair in atoms.proto + const FieldValue& int32Item = values[1]; + Field expectedField = getField(100, {1, 1, 2}, 2, {true, false, true}); + EXPECT_EQ(expectedField, int32Item.mField); + EXPECT_EQ(Type::INT, int32Item.mValue.getType()); + EXPECT_EQ(1, int32Item.mValue.int_value); + + const FieldValue& int64Item = values[3]; + expectedField = getField(100, {1, 2, 3}, 2, {true, false, true}); + EXPECT_EQ(expectedField, int64Item.mField); + EXPECT_EQ(Type::LONG, int64Item.mValue.getType()); + EXPECT_EQ(0x123456789, int64Item.mValue.long_value); + + const FieldValue& floatItem = values[5]; + expectedField = getField(100, {1, 3, 5}, 2, {true, false, true}); + EXPECT_EQ(expectedField, floatItem.mField); + EXPECT_EQ(Type::FLOAT, floatItem.mValue.getType()); + EXPECT_EQ(2.0, floatItem.mValue.float_value); + + const FieldValue& stringItem = values[7]; + expectedField = getField(100, {1, 4, 4}, 2, {true, true, true}); + EXPECT_EQ(expectedField, stringItem.mField); + EXPECT_EQ(Type::STRING, stringItem.mValue.getType()); + EXPECT_EQ(str, stringItem.mValue.str_value); + + stats_event_release(event); +} + +TEST(LogEventTest, TestAttributionChain) { + struct stats_event* event = stats_event_obtain(); + stats_event_set_atom_id(event, 100); + + string tag1 = "tag1"; + string tag2 = "tag2"; + + uint32_t uids[] = {1001, 1002}; + const char* tags[] = {tag1.c_str(), tag2.c_str()}; + + stats_event_write_attribution_chain(event, uids, tags, 2); + stats_event_build(event); + + size_t size; + uint8_t* buf = stats_event_get_buffer(event, &size); + + LogEvent logEvent(buf, size, /*uid=*/ 1000); + EXPECT_TRUE(logEvent.isValid()); + EXPECT_EQ(100, logEvent.GetTagId()); + + const vector<FieldValue>& values = logEvent.getValues(); + EXPECT_EQ(4, values.size()); // 2 per attribution node + + // 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); + + stats_event_release(event); +} + +#else // NEW_ENCODING_SCHEME + TEST(LogEventTest, TestLogParsing) { LogEvent event1(1, 2000); @@ -582,7 +852,7 @@ TEST(LogEventTest, TestBinaryFieldAtom) { 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.writeBytes(extension_str); event1.init(); ProtoOutputStream proto; @@ -621,7 +891,7 @@ TEST(LogEventTest, TestBinaryFieldAtom_empty) { 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.writeBytes(extension_str); event1.init(); ProtoOutputStream proto; @@ -659,6 +929,7 @@ TEST(LogEventTest, TestWriteExperimentIdsToProto) { EXPECT_EQ(proto[1], 0xae); EXPECT_EQ(proto[2], 0x27); } +#endif // NEW_ENCODING_SCHEME } // namespace statsd diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index fe25a257aa67..69e11ed9b836 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -76,9 +76,9 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { // 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 +103,7 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { 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 +136,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { 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); } @@ -1199,87 +1199,66 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi 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()); + EXPECT_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()); - - 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()); - - 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); - - 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); + EXPECT_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 + + auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1]; + EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2); + EXPECT_TRUE(metricProducer1_2->isActive()); + + EXPECT_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); + + 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()); + 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. @@ -1293,55 +1272,34 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi EXPECT_EQ(1, processor2->mMetricsManagers.size()); it = processor2->mMetricsManagers.find(cfgKey1); EXPECT_TRUE(it != processor2->mMetricsManagers.end()); - auto& metricsManager1001 = it->second; - EXPECT_TRUE(metricsManager1001->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()); - - 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()); - - 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); - - i = 0; - for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) { - if (metricsManager1001->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } + auto& metricsManager2 = it->second; + EXPECT_TRUE(metricsManager2->isActive()); - 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); + EXPECT_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()); + + auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1]; + EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2); + EXPECT_TRUE(metricProducer2_2->isActive()); + + EXPECT_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); + + 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,13 +1308,14 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi // 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. @@ -1369,23 +1328,23 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi // 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. @@ -1399,55 +1358,34 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi EXPECT_EQ(1, processor3->mMetricsManagers.size()); it = processor3->mMetricsManagers.find(cfgKey1); EXPECT_TRUE(it != processor3->mMetricsManagers.end()); - auto& metricsManagerTimeBase3 = it->second; - EXPECT_TRUE(metricsManagerTimeBase3->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()); - - 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()); - - 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); - - i = 0; - for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) { - if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() == - metric1ActivationTrigger2->atom_matcher_id()) { - break; - } - } + auto& metricsManager3 = it->second; + EXPECT_TRUE(metricsManager3->isActive()); - 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); + EXPECT_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()); + + auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1]; + EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2); + EXPECT_TRUE(metricProducer3_2->isActive()); + + EXPECT_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); + + 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,13 +1394,13 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi // 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()); // }}}------------------------------------------------------------------------------- @@ -1473,15 +1411,16 @@ TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActi ); 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()); // }}}--------------------------------------------------------------------------- } @@ -1713,6 +1652,11 @@ TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) { 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); } #else diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index e826a52c2f33..6eaa2311e5ed 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -268,8 +268,6 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { 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*/, @@ -321,9 +319,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { 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 @@ -389,9 +387,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { // 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,8 +397,6 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { 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*/, @@ -445,9 +441,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { 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 @@ -489,10 +485,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { // 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]); } @@ -500,8 +495,6 @@ 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); @@ -555,9 +548,9 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { const auto queryKey = getWakeLockQueryKey(position, uid_list1, 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 @@ -594,9 +587,9 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { // TEST QUERY const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, 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]); @@ -628,17 +621,17 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { // TEST QUERY const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, 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); 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/StateConditionTracker_test.cpp index 9a66254afce0..fbf6efdcf966 100644 --- a/cmds/statsd/tests/condition/StateTracker_test.cpp +++ b/cmds/statsd/tests/condition/StateConditionTracker_test.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "src/condition/StateTracker.h" +#include "src/condition/StateConditionTracker.h" #include "tests/statsd_test_util.h" #include <gmock/gmock.h> @@ -50,7 +50,7 @@ void makeUidProcStateEvent(int32_t uid, int32_t state, LogEvent* event) { event->init(); } -TEST(StateTrackerTest, TestStateChange) { +TEST(StateConditionTrackerTest, TestStateChange) { int uid1 = 111; int uid2 = 222; @@ -60,7 +60,7 @@ TEST(StateTrackerTest, TestStateChange) { trackerNameIndexMap[StringToId("UidProcState")] = 0; vector<Matcher> primaryFields; primaryFields.push_back(getSimpleMatcher(kUidProcTag, 1)); - StateTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(), + StateConditionTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(), trackerNameIndexMap, primaryFields); LogEvent event(kUidProcTag, 0 /*timestamp*/); diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp index b98dc60086ac..325e869e5a9b 100644 --- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp @@ -96,6 +96,11 @@ TEST(ConfigTtlE2eTest, TestCountMetric) { 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); } 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 000000000000..15fc468ffe57 --- /dev/null +++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp @@ -0,0 +1,802 @@ +/* + * 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__ + +/** + * 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. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); + EXPECT_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) + | (ScreenUnknownEvent) + */ + // Initialize log events - first bucket. + int appUid = 123; + std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 50 * NS_PER_SEC)); // 1:00 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 75 * NS_PER_SEC)); // 1:25 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 + + // Initialize log events - second bucket. + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 350 * NS_PER_SEC)); // 6:00 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 400 * NS_PER_SEC)); // 6:50 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 450 * NS_PER_SEC)); // 7:40 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 475 * NS_PER_SEC)); // 8:05 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, + bucketStartTimeNs + 500 * NS_PER_SEC)); // 8:30 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 520 * NS_PER_SEC)); // 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); + EXPECT_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_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_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()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(1).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_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()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, 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 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; + + auto state = CreateScreenStateWithOnOffMap(); + *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. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), SCREEN_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + + StateMap map = state.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][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 + | (ScreenStateUnknownEvent = 0) + | | (ScreenStateOffEvent = 1) + | (ScreenStateDozeEvent = 3) + | (ScreenStateDozeSuspendEvent = 4) + -----------------------------------------------------------SCREEN_ON events + | | (ScreenStateOnEvent = 2) + | (ScreenStateVrEvent = 5) + | (ScreenStateOnSuspendEvent = 6) + */ + // Initialize log events - first bucket. + int appUid = 123; + std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")}; + + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, + bucketStartTimeNs + 30 * NS_PER_SEC)); // 0:40 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 120 * NS_PER_SEC)); // 2:10 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_VR, + bucketStartTimeNs + 180 * NS_PER_SEC)); // 3:10 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, + bucketStartTimeNs + 210 * NS_PER_SEC)); // 3:40 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 285 * NS_PER_SEC)); // 4:55 + + // Initialize log events - second bucket. + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 360 * NS_PER_SEC)); // 6:10 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND, + bucketStartTimeNs + 390 * NS_PER_SEC)); // 6:40 + events.push_back(CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_DOZE_SUSPEND, + bucketStartTimeNs + 430 * NS_PER_SEC)); // 7:20 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 440 * NS_PER_SEC)); // 7:30 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 540 * NS_PER_SEC)); // 9:10 + events.push_back(CreateSyncStartEvent(attributions1, "sync_name", + bucketStartTimeNs + 570 * NS_PER_SEC)); // 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); + EXPECT_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_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(3, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_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(StringToId("SCREEN_OFF"), data.slice_by_state(0).group_id()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(4, data.bucket_info(0).count()); + EXPECT_EQ(2, data.bucket_info(1).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_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(StringToId("SCREEN_ON"), data.slice_by_state(0).group_id()); + EXPECT_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 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", android::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(android::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. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), UID_PROCESS_STATE_ATOM_ID); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); + EXPECT_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 (ProcessStateTopEvent = 1002) + 1 1 (ProcessStateForegroundServiceEvent = 1003) + 2 (ProcessStateImportantBackgroundEvent = 1006) + 1 1 1 (ProcessStateImportantForegroundEvent = 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(1 /* uid */, bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + bucketStartTimeNs + 30 * NS_PER_SEC)); // 0:40 + events.push_back( + CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, + bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 120 * NS_PER_SEC)); // 2:10 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, + bucketStartTimeNs + 210 * NS_PER_SEC)); // 3:40 + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 + events.push_back(CreateUidProcessStateChangedEvent( + 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, + bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 + events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, + bucketStartTimeNs + 285 * NS_PER_SEC)); // 4:55 + + // Initialize log events - second bucket. + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 360 * NS_PER_SEC)); // 6:10 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, + bucketStartTimeNs + 390 * NS_PER_SEC)); // 6:40 + events.push_back(CreateUidProcessStateChangedEvent( + 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, + bucketStartTimeNs + 430 * NS_PER_SEC)); // 7:20 + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 440 * NS_PER_SEC)); // 7:30 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + bucketStartTimeNs + 540 * NS_PER_SEC)); // 9:10 + events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, + bucketStartTimeNs + 570 * NS_PER_SEC)); // 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); + EXPECT_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_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(5, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(3); + EXPECT_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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(4); + EXPECT_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()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + EXPECT_EQ(2, data.bucket_info(1).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", android::util::APP_CRASH_OCCURRED); + *config.add_atom_matcher() = appCrashMatcher; + + auto state1 = CreateScreenStateWithOnOffMap(); + *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(android::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. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_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); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + EXPECT_EQ(metricProducer->mMetric2StateLinks.size(), 1); + + StateMap map = state1.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[SCREEN_STATE_ATOM_ID][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 + | (ScreenStateUnknownEvent = 0) + | | (ScreenStateOffEvent = 1) + | (ScreenStateDozeEvent = 3) + -----------------------------------------------------------SCREEN_ON events + | | (ScreenStateOnEvent = 2) + | (ScreenStateOnSuspendEvent = 6) + -----------------------------------------------------------PROCESS STATE events + 1 2 (ProcessStateTopEvent = 1002) + 1 (ProcessStateForegroundServiceEvent = 1003) + 2 (ProcessStateImportantBackgroundEvent = 1006) + 1 1 1 (ProcessStateImportantForegroundEvent = 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( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + bucketStartTimeNs + 5 * NS_PER_SEC)); // 0:15 + events.push_back( + CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 20 * NS_PER_SEC)); // 0:30 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_UNKNOWN, + bucketStartTimeNs + 30 * NS_PER_SEC)); // 0:40 + events.push_back( + CreateAppCrashOccurredEvent(1 /* uid */, bucketStartTimeNs + 60 * NS_PER_SEC)); // 1:10 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, + bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 90 * NS_PER_SEC)); // 1:40 + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 120 * NS_PER_SEC)); // 2:10 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + bucketStartTimeNs + 150 * NS_PER_SEC)); // 2:40 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 160 * NS_PER_SEC)); // 2:50 + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 200 * NS_PER_SEC)); // 3:30 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_DOZE, + bucketStartTimeNs + 210 * NS_PER_SEC)); // 3:40 + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 250 * NS_PER_SEC)); // 4:20 + events.push_back(CreateUidProcessStateChangedEvent( + 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP, + bucketStartTimeNs + 280 * NS_PER_SEC)); // 4:50 + events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, + bucketStartTimeNs + 285 * NS_PER_SEC)); // 4:55 + + // Initialize log events - second bucket. + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 360 * NS_PER_SEC)); // 6:10 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE, + bucketStartTimeNs + 380 * NS_PER_SEC)); // 6:30 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON_SUSPEND, + bucketStartTimeNs + 390 * NS_PER_SEC)); // 6:40 + events.push_back(CreateUidProcessStateChangedEvent( + 2 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, + bucketStartTimeNs + 420 * NS_PER_SEC)); // 7:10 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 440 * NS_PER_SEC)); // 7:30 + events.push_back(CreateAppCrashOccurredEvent(1 /* uid */, + bucketStartTimeNs + 450 * NS_PER_SEC)); // 7:40 + events.push_back( + CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 520 * NS_PER_SEC)); // 8:50 + events.push_back(CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + bucketStartTimeNs + 540 * NS_PER_SEC)); // 9:10 + events.push_back(CreateAppCrashOccurredEvent(2 /* uid */, + bucketStartTimeNs + 570 * NS_PER_SEC)); // 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); + EXPECT_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_TRUE(reports.reports(0).metrics(0).has_count_metrics()); + EXPECT_EQ(6, reports.reports(0).metrics(0).count_metrics().data_size()); + + // For each CountMetricData, check StateValue info is correct and buckets + // have correct counts. + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + EXPECT_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(StringToId("SCREEN_OFF"), 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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(1); + EXPECT_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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(2); + EXPECT_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(StringToId("SCREEN_OFF"), 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()); + EXPECT_EQ(2, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).count()); + EXPECT_EQ(1, data.bucket_info(1).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(3); + EXPECT_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(StringToId("SCREEN_ON"), 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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(4); + EXPECT_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(StringToId("SCREEN_ON"), 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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(1, data.bucket_info(0).count()); + + data = reports.reports(0).metrics(0).count_metrics().data(5); + EXPECT_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(StringToId("SCREEN_OFF"), 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()); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, data.bucket_info(0).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 e4186b7200a0..000000000000 --- 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 f3ecd56dd946..000000000000 --- 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 489bb0b21a2e..000000000000 --- 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 5da0fca2f3ed..909315552e3f 100644 --- a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp @@ -271,19 +271,19 @@ TEST(DurationMetricE2eTest, TestWithActivation) { // Turn screen off. event = CreateScreenStateChangedEvent( android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * NS_PER_SEC); // 0:02 - processor.OnLogEvent(event.get()); + 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()); + processor.OnLogEvent(event.get(), durationStartNs); // Activate metric. 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()); + processor.OnLogEvent(event.get(), activationStartNs); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 1); @@ -296,7 +296,7 @@ TEST(DurationMetricE2eTest, TestWithActivation) { // Expire activation. const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC; event = CreateScreenBrightnessChangedEvent(64, expirationNs); // 0:47 - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), expirationNs); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 2); @@ -310,24 +310,24 @@ TEST(DurationMetricE2eTest, TestWithActivation) { // 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()); + 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()); + 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()); + processor.OnLogEvent(event.get(), duration2EndNs); // Activate metric. 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()); + processor.OnLogEvent(event.get(), activation2StartNs); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); diff --git a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp index f1b6029f0ab0..b6a6492fce75 100644 --- a/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/MetricActivation_e2e_test.cpp @@ -290,14 +290,14 @@ TEST(MetricActivationE2eTest, TestCountMetric) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + 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); @@ -312,12 +312,12 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -330,7 +330,7 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -344,11 +344,11 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + 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()); + 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. @@ -364,7 +364,7 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // Re-activate metric via screen on. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -379,7 +379,7 @@ TEST(MetricActivationE2eTest, TestCountMetric) { // 4th processed event. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); ConfigMetricsReportList reports; vector<uint8_t> buffer; @@ -509,14 +509,14 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + 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); @@ -532,12 +532,12 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -551,7 +551,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -566,11 +566,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + 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()); + 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. @@ -587,7 +587,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // Re-activate metric via screen on. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -603,11 +603,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 4th processed event. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + 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); @@ -623,11 +623,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // 5th processed event. event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - processor.OnLogEvent(event.get()); + 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()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 60); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -643,7 +643,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // Screen-on activation expired. event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); - processor.OnLogEvent(event.get()); + 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. @@ -658,11 +658,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { EXPECT_EQ(eventDeactivationMap[3][0], eventActivationMap[0]); event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - processor.OnLogEvent(event.get()); + 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); @@ -678,7 +678,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation) { // Cancel battery saver mode activation. event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 6); @@ -835,14 +835,14 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + 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); @@ -859,12 +859,12 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -879,7 +879,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -895,11 +895,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + 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()); + 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. @@ -917,7 +917,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // Re-activate metric via screen on. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 3); @@ -934,11 +934,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 4th processed event. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + 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); @@ -955,11 +955,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // 5th processed event. event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - processor.OnLogEvent(event.get()); + 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()); + 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. @@ -976,7 +976,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // Screen-on activation expired. event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 4); @@ -991,11 +991,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { EXPECT_EQ(eventDeactivationMap[4][0], eventActivationMap[2]); event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - processor.OnLogEvent(event.get()); + 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); @@ -1012,7 +1012,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations) { // Cancel battery saver mode and screen on activation. event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 16); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 6); @@ -1170,11 +1170,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // Event that should be ignored. event = CreateAppCrashEvent(111, bucketStartTimeNs + 1); - processor.OnLogEvent(event.get()); + 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()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 1); @@ -1186,11 +1186,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 1st processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + 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); @@ -1201,12 +1201,12 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 2nd processed event. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 + 40); - processor.OnLogEvent(event.get()); + 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()); + processor.OnLogEvent(event.get(), firstDeactivation); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); // New broadcast since the config is no longer active. @@ -1217,11 +1217,11 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // Should be ignored event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 61 + 80); - processor.OnLogEvent(event.get()); + 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); @@ -1233,12 +1233,12 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // 3rd processed event. event = CreateAppCrashEvent(555, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 80); - processor.OnLogEvent(event.get()); + 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()); + processor.OnLogEvent(event.get(), secondDeactivation); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_EQ(broadcastCount, 4); @@ -1248,7 +1248,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation) { // Should be ignored. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13 + 80); ConfigMetricsReportList reports; vector<uint8_t> buffer; @@ -1388,9 +1388,9 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { std::unique_ptr<LogEvent> event; event = CreateAppCrashEvent(111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); event = CreateMoveToForegroundEvent(1111, bucketStartTimeNs + 5); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 5); EXPECT_FALSE(metricsManager->isActive()); EXPECT_FALSE(metricProducer->mIsActive); EXPECT_FALSE(metricProducer2->mIsActive); @@ -1398,7 +1398,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 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); @@ -1424,14 +1424,14 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // First processed event. event = CreateAppCrashEvent(222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); event = CreateMoveToForegroundEvent(2222, bucketStartTimeNs + 15); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 15); // Activated by screen on event. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 20); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + 20); EXPECT_TRUE(metricsManager->isActive()); EXPECT_TRUE(metricProducer->mIsActive); EXPECT_EQ(eventActivationMap[0]->state, ActivationState::kActive); @@ -1455,9 +1455,9 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 2nd processed event. // The activation by screen_on event expires, but the one by battery save mode is still active. event = CreateAppCrashEvent(333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); event = CreateMoveToForegroundEvent(3333, bucketStartTimeNs + NS_PER_SEC * 60 * 2 + 25); - processor.OnLogEvent(event.get()); + 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); @@ -1482,15 +1482,15 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 3rd processed event. event = CreateAppCrashEvent(444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); event = CreateMoveToForegroundEvent(4444, bucketStartTimeNs + NS_PER_SEC * 60 * 5 + 25); - processor.OnLogEvent(event.get()); + 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()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 8); event = CreateMoveToForegroundEvent(5555, bucketStartTimeNs + NS_PER_SEC * 60 * 8); - processor.OnLogEvent(event.get()); + 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); @@ -1517,7 +1517,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // Re-activate metric via screen on. event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 10 + 10); EXPECT_TRUE(metricsManager->isActive()); EXPECT_EQ(broadcastCount, 3); EXPECT_EQ(activeConfigsBroadcast.size(), 1); @@ -1543,13 +1543,13 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 4th processed event. event = CreateAppCrashEvent(666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); event = CreateMoveToForegroundEvent(6666, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 1); - processor.OnLogEvent(event.get()); + 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); @@ -1575,13 +1575,13 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // 5th processed event. event = CreateAppCrashEvent(777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); event = CreateMoveToForegroundEvent(7777, bucketStartTimeNs + NS_PER_SEC * 60 * 11 + 40); - processor.OnLogEvent(event.get()); + 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()); + 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); @@ -1607,9 +1607,9 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // Screen-on activation expired. event = CreateAppCrashEvent(888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); event = CreateMoveToForegroundEvent(8888, bucketStartTimeNs + NS_PER_SEC * 60 * 13); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 13); EXPECT_FALSE(metricsManager->isActive()); EXPECT_EQ(broadcastCount, 4); EXPECT_EQ(activeConfigsBroadcast.size(), 0); @@ -1633,13 +1633,13 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { EXPECT_EQ(eventDeactivationMap2[4][0], eventActivationMap2[2]); event = CreateAppCrashEvent(999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(), bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); event = CreateMoveToForegroundEvent(9999, bucketStartTimeNs + NS_PER_SEC * 60 * 14 + 1); - processor.OnLogEvent(event.get()); + 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); @@ -1665,7 +1665,7 @@ TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations) { // Cancel battery saver mode and screen on activation. event = CreateScreenBrightnessChangedEvent(140, bucketStartTimeNs + NS_PER_SEC * 60 * 16); - processor.OnLogEvent(event.get()); + processor.OnLogEvent(event.get(),bucketStartTimeNs + NS_PER_SEC * 60 * 16); EXPECT_FALSE(metricsManager->isActive()); EXPECT_EQ(broadcastCount, 6); EXPECT_EQ(activeConfigsBroadcast.size(), 0); diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp index 309d251e4a89..16b51d99535b 100644 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -28,16 +28,16 @@ namespace statsd { #ifdef __ANDROID__ -const string kAndroid = "android"; 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) { 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, @@ -50,7 +50,7 @@ ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestam ConfigMetricsReportList reports; reports.ParseFromArray(output.data(), output.size()); EXPECT_EQ(1, reports.reports_size()); - return reports.reports(0); + return reports.reports(kCallingUid); } StatsdConfig MakeConfig() { @@ -162,7 +162,10 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { ConfigMetricsReport report = GetReports(service.mProcessor, start + 4); backfillStartEndTimestamp(&report); - EXPECT_EQ(1, report.metrics_size()); + + 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). @@ -186,7 +189,10 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { ConfigMetricsReport report = GetReports(service.mProcessor, start + 4); backfillStartEndTimestamp(&report); - EXPECT_EQ(1, report.metrics_size()); + + 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). @@ -228,8 +234,9 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { ConfigMetricsReport report = GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true); backfillStartEndTimestamp(&report); - EXPECT_EQ(1, report.metrics_size()); - EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size()); + + ASSERT_EQ(1, report.metrics_size()); + ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size()); EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos()); // Can't test the start time since it will be based on the actual time when the pulling occurs. EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), @@ -270,8 +277,8 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { ConfigMetricsReport report = GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true); backfillStartEndTimestamp(&report); - EXPECT_EQ(1, report.metrics_size()); - EXPECT_EQ(1, report.metrics(0).gauge_metrics().skipped_size()); + ASSERT_EQ(1, report.metrics_size()); + ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size()); // Can't test the start time since it will be based on the actual time when the pulling occurs. EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos()); EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp index fb878dc7efed..e8d2ec514cad 100644 --- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp @@ -369,6 +369,168 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { EXPECT_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", android::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(android::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. + EXPECT_EQ(1U, processor->mMetricsManagers.size()); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(1, metricsManager->mAllMetricProducers.size()); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(1, metricProducer->mSlicedStateAtoms.size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0)); + EXPECT_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", android::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(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); + *valueMetric->mutable_dimensions_in_what() = + CreateDimensions(android::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(android::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. + EXPECT_EQ(1U, processor->mMetricsManagers.size()); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(1, metricsManager->mAllMetricProducers.size()); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(1, metricProducer->mSlicedStateAtoms.size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0)); + EXPECT_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", android::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(android::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(android::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. + EXPECT_EQ(0, processor->mMetricsManagers.size()); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp index 32409184b02f..ae92705aff4c 100644 --- a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp +++ b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp @@ -57,8 +57,10 @@ 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 bool FALSE_PREROTATION = true; +static const bool GLES_1_IN_USE = true; static const size_t NUMBER_OF_VALUES_GLOBAL = 13; -static const size_t NUMBER_OF_VALUES_APP = 6; +static const size_t NUMBER_OF_VALUES_APP = 8; // clang-format on class MockGpuStatsPuller : public GpuStatsPuller { @@ -151,6 +153,8 @@ TEST_F(GpuStatsPuller_test, PullGpuStatsAppInfo) { EXPECT_TRUE(event->write(int64VectorToProtoByteString(vkDriverLoadingTime))); EXPECT_TRUE(event->write(int64VectorToProtoByteString(angleDriverLoadingTime))); EXPECT_TRUE(event->write(CPU_VULKAN_IN_USE)); + EXPECT_TRUE(event->write(FALSE_PREROTATION)); + EXPECT_TRUE(event->write(GLES_1_IN_USE)); event->init(); inData.emplace_back(event); MockGpuStatsPuller mockPuller(android::util::GPU_STATS_APP_INFO, &inData); @@ -169,6 +173,8 @@ TEST_F(GpuStatsPuller_test, PullGpuStatsAppInfo) { EXPECT_EQ(int64VectorToProtoByteString(angleDriverLoadingTime), outData[0]->getValues()[4].mValue.str_value); EXPECT_EQ(CPU_VULKAN_IN_USE, outData[0]->getValues()[5].mValue.int_value); + EXPECT_EQ(FALSE_PREROTATION, outData[0]->getValues()[6].mValue.int_value); + EXPECT_EQ(GLES_1_IN_USE, outData[0]->getValues()[7].mValue.int_value); } } // namespace statsd diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp new file mode 100644 index 000000000000..2b0590d11f54 --- /dev/null +++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp @@ -0,0 +1,210 @@ +// 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 <android/os/BnPullAtomCallback.h> +#include <android/os/IPullAtomResultReceiver.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 "tests/statsd_test_util.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +using namespace testing; +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; + +stats_event* createSimpleEvent(int64_t value) { + stats_event* event = stats_event_obtain(); + stats_event_set_atom_id(event, pullTagId); + stats_event_write_int64(event, value); + stats_event_build(event); + return event; +} + +void executePull(const sp<IPullAtomResultReceiver>& resultReceiver) { + // Convert stats_events into StatsEventParcels. + std::vector<android::util::StatsEventParcel> parcels; + for (int i = 0; i < values.size(); i++) { + stats_event* event = createSimpleEvent(values[i]); + size_t size; + uint8_t* buffer = stats_event_get_buffer(event, &size); + + android::util::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)); + stats_event_release(event); + } + + sleep_for(std::chrono::nanoseconds(pullDelayNs)); + resultReceiver->pullFinished(pullTagId, pullSuccess, parcels); +} + +class FakePullAtomCallback : public BnPullAtomCallback { +public: + binder::Status onPullAtom(int atomTag, + const sp<IPullAtomResultReceiver>& resultReceiver) override { + // Force pull to happen in separate thread to simulate binder. + pullThread = std::thread(executePull, resultReceiver); + return binder::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) { + sp<FakePullAtomCallback> cb = new FakePullAtomCallback(); + int64_t value = 43; + pullSuccess = true; + values.push_back(value); + + StatsCallbackPuller puller(pullTagId, cb, pullTimeoutNs); + + vector<std::shared_ptr<LogEvent>> dataHolder; + int64_t startTimeNs = getElapsedRealtimeNs(); + EXPECT_TRUE(puller.PullInternal(&dataHolder)); + int64_t endTimeNs = getElapsedRealtimeNs(); + + EXPECT_EQ(1, dataHolder.size()); + EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId()); + EXPECT_LT(startTimeNs, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_GT(endTimeNs, dataHolder[0]->GetElapsedTimestampNs()); + EXPECT_EQ(1, dataHolder[0]->size()); + EXPECT_EQ(value, dataHolder[0]->getValues()[0].mValue.int_value); +} + +TEST_F(StatsCallbackPullerTest, PullFail) { + sp<FakePullAtomCallback> cb = new FakePullAtomCallback(); + pullSuccess = false; + int64_t value = 1234; + values.push_back(value); + + StatsCallbackPuller puller(pullTagId, cb, pullTimeoutNs); + + vector<std::shared_ptr<LogEvent>> dataHolder; + EXPECT_FALSE(puller.PullInternal(&dataHolder)); + EXPECT_EQ(0, dataHolder.size()); +} + +TEST_F(StatsCallbackPullerTest, PullTimeout) { + sp<FakePullAtomCallback> cb = new FakePullAtomCallback(); + pullSuccess = true; + pullDelayNs = 500000000; // 500ms. + pullTimeoutNs = 10000; // 10 microseconds. + int64_t value = 4321; + values.push_back(value); + + StatsCallbackPuller puller(pullTagId, cb, pullTimeoutNs); + + vector<std::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); + EXPECT_EQ(0, dataHolder.size()); + + // Let the pull return and make sure that the dataHolder is not modified. + pullThread.join(); + EXPECT_EQ(0, dataHolder.size()); +} + +// Register a puller and ensure that the timeout logic works. +TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) { + sp<FakePullAtomCallback> cb = new FakePullAtomCallback(); + pullSuccess = true; + pullDelayNs = 500000000; // 500 ms. + pullTimeoutNs = 10000; // 10 microsseconds. + int64_t value = 4321; + values.push_back(value); + + StatsPullerManager pullerManager; + pullerManager.RegisterPullAtomCallback(/*uid=*/-1, pullTagId, pullCoolDownNs, pullTimeoutNs, + vector<int32_t>(), cb); + vector<std::shared_ptr<LogEvent>> dataHolder; + int64_t startTimeNs = getElapsedRealtimeNs(); + // Returns false, since StatsPuller code will evaluate the timeout. + EXPECT_FALSE(pullerManager.Pull(pullTagId, &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); + EXPECT_EQ(0, dataHolder.size()); + + // Let the pull return and make sure that the dataHolder is not modified. + pullThread.join(); + EXPECT_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/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp index 76e2097a90b8..c40719a17f62 100644 --- a/cmds/statsd/tests/external/StatsPuller_test.cpp +++ b/cmds/statsd/tests/external/StatsPuller_test.cpp @@ -35,6 +35,7 @@ using std::vector; using std::this_thread::sleep_for; using testing::Contains; +namespace { // cooldown time 1sec. int pullTagId = 10014; @@ -76,7 +77,9 @@ public: } }; -TEST_F(StatsPullerTest, PullSucces) { +} // Anonymous namespace. + +TEST_F(StatsPullerTest, PullSuccess) { pullData.push_back(createSimpleEvent(1111L, 33)); pullSuccess = true; diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 67c704eb87fd..8915c7364bc7 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -43,8 +43,8 @@ TEST(CountMetricProducerTest, TestFirstBucket) { metric.set_bucket(ONE_MINUTE); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - 5, 600 * NS_PER_SEC + NS_PER_SEC/2); + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5, + 600 * NS_PER_SEC + NS_PER_SEC / 2); EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, countProducer.mCurrentBucketNum); EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); @@ -131,7 +131,8 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs); + CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, + bucketStartTimeNs); countProducer.onConditionChanged(true, bucketStartTimeNs); countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); @@ -187,9 +188,9 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { {getMockedDimensionKey(conditionTagId, 2, "222")}; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse)); + EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); - EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue)); + EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, bucketStartTimeNs, bucketStartTimeNs); @@ -396,6 +397,26 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { 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 } // namespace os } // namespace android diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index d2fd95c818cf..f30873b92afc 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -113,9 +113,9 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { 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(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); - 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); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index b9553a8fded8..308c43da426b 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -12,19 +12,22 @@ // 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 "tests/statsd_test_util.h" + using namespace testing; using android::sp; using std::set; @@ -79,8 +82,6 @@ TEST(GaugeMetricProducerTest, TestFirstBucket) { 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); @@ -126,8 +127,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { 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); @@ -211,7 +210,6 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { logEventMatcherIndex, eventMatcherWizard, -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); @@ -303,7 +301,6 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { 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); @@ -370,7 +367,6 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { 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); @@ -431,7 +427,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); @@ -485,10 +480,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { 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); @@ -496,18 +487,14 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { 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; })); @@ -529,7 +516,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8); @@ -538,9 +524,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { EXPECT_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()); vector<shared_ptr<LogEvent>> allData; @@ -583,7 +566,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); Alert alert; alert.set_id(101); @@ -692,7 +674,6 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) { logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -777,7 +758,6 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -807,6 +787,70 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { 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, _)) + // Bucket start. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })); + + int triggerId = 5; + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, + tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); + + LogEvent trigger(triggerId, bucketStartTimeNs + 3); + trigger.init(); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); + + // 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()); + EXPECT_EQ(0, report.gauge_metrics().data_size()); + EXPECT_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()); + EXPECT_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 bf047520cc77..100220b730d7 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -52,7 +52,6 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); - vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -63,7 +62,7 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { int64_t bucketNum = 0; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); @@ -88,7 +87,6 @@ TEST(MaxDurationTrackerTest, TestStopAll) { 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,7 +97,7 @@ TEST(MaxDurationTrackerTest, TestStopAll) { int64_t bucketNum = 0; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); @@ -124,7 +122,6 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { 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,7 +132,7 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { int64_t bucketNum = 0; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); @@ -165,7 +162,6 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { 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,7 +172,7 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { int64_t bucketNum = 0; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition, + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); @@ -202,7 +198,6 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { const HashableDimensionKey conditionDimKey = key1; - vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey conditionKey1; @@ -223,7 +218,7 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { int64_t eventStopTimeNs = conditionStops2 + 8 * NS_PER_SEC; int64_t metricId = 1; - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, true, false, {}); EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); @@ -246,7 +241,6 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { } TEST(MaxDurationTrackerTest, TestAnomalyDetection) { - vector<Matcher> dimensionInCondition; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey conditionKey1; @@ -273,7 +267,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { sp<AlarmMonitor> alarmMonitor; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {anomalyTracker}); @@ -295,7 +289,6 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { // 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,7 +326,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) { sp<AlarmMonitor> alarmMonitor; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {anomalyTracker}); @@ -381,7 +374,6 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp) { // 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,7 +408,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) { sp<AlarmMonitor> alarmMonitor; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {anomalyTracker}); @@ -434,4 +426,4 @@ TEST(MaxDurationTrackerTest, TestAnomalyPredictedTimestamp_UpdatedOnStop) { } // 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 7c2b4236add8..1cd7bdbf7bb0 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -51,7 +51,6 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { 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,7 +61,7 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { int64_t eventStartTimeNs = bucketStartTimeNs + 1; int64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); @@ -84,7 +83,6 @@ TEST(OringDurationTrackerTest, TestDurationNested) { 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,7 +92,7 @@ TEST(OringDurationTrackerTest, TestDurationNested) { int64_t bucketNum = 0; int64_t eventStartTimeNs = bucketStartTimeNs + 1; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); @@ -117,7 +115,6 @@ TEST(OringDurationTrackerTest, TestStopAll) { {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,7 +124,7 @@ TEST(OringDurationTrackerTest, TestStopAll) { int64_t bucketNum = 0; int64_t eventStartTimeNs = bucketStartTimeNs + 1; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); @@ -147,7 +144,6 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { 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,7 +154,7 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { int64_t eventStartTimeNs = bucketStartTimeNs + 1; int64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {}); @@ -186,13 +182,12 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { 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,7 +198,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { int64_t eventStartTimeNs = bucketStartTimeNs + 1; int64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {}); @@ -224,13 +219,12 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { 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,7 +237,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { int64_t eventStartTimeNs = bucketStartTimeNs + 1; int64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {}); @@ -266,13 +260,12 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { 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,7 +275,7 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { int64_t bucketNum = 0; int64_t eventStartTimeNs = bucketStartTimeNs + 1; - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {}); @@ -306,7 +299,6 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { 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,7 +316,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { sp<AlarmMonitor> alarmMonitor; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {anomalyTracker}); @@ -371,7 +363,6 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { } TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) { - vector<Matcher> dimensionInCondition; Alert alert; alert.set_id(101); alert.set_metric_id(1); @@ -387,7 +378,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp2) { 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 +406,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp3) { 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); @@ -432,7 +423,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp3) { sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); OringDurationTracker tracker(kConfigKey, metricId, DEFAULT_METRIC_DIMENSION_KEY, - wizard, 1, dimensionInCondition, + wizard, 1, true, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, true, false, {anomalyTracker}); @@ -472,7 +463,6 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { 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,7 +481,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) { sp<AlarmMonitor> alarmMonitor; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, bucketStartTimeNs, bucketNum, bucketStartTimeNs, bucketSizeNs, false, false, {anomalyTracker}); @@ -522,7 +512,6 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { 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,7 +530,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { sp<AlarmMonitor> alarmMonitor; sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey, alarmMonitor); - OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition, + OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/, bucketStartTimeNs, 0, bucketStartTimeNs, bucketSizeNs, false, false, {anomalyTracker}); @@ -589,4 +578,4 @@ TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) { } // 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 2262c76e64f9..92e8241d9ec2 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; @@ -105,7 +107,6 @@ class ValueMetricProducerTestHelper { kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); return valueProducer; } @@ -125,11 +126,48 @@ class ValueMetricProducerTestHelper { new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); valueProducer->mCondition = ConditionState::kFalse; return valueProducer; } + static sp<ValueMetricProducer> createValueProducerWithNoInitialCondition( + sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) { + 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, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( + kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + 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, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( + kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, + {}, slicedStateAtoms, stateGroupMap); + return valueProducer; + } + static ValueMetric createMetric() { ValueMetric metric; metric.set_id(metricId); @@ -145,8 +183,13 @@ class ValueMetricProducerTestHelper { 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; + } +}; /* * Tests that the first bucket works correctly @@ -169,7 +212,6 @@ TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) { 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)); EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10)); @@ -199,7 +241,6 @@ TEST(ValueMetricProducerTest, TestFirstBucket) { 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(); EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, valueProducer.mCurrentBucketNum); @@ -210,7 +251,7 @@ TEST(ValueMetricProducerTest, TestFirstBucket) { * 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) { @@ -237,10 +278,12 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + 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()); @@ -257,9 +300,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { // has one slice EXPECT_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()); @@ -278,9 +322,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_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()); @@ -294,7 +339,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { } TEST(ValueMetricProducerTest, TestPartialBucketCreated) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, Pull(tagId, _)) // Initialize bucket. @@ -351,7 +396,7 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) { * Tests pulled atoms with filtering */ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -378,10 +423,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { })); sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); + kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -396,9 +439,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { EXPECT_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()); @@ -415,8 +459,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { // No new data seen, so data has been cleared. EXPECT_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()); @@ -432,10 +476,11 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_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()); @@ -467,9 +512,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { // has one slice EXPECT_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()); @@ -483,8 +529,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { // has one slice EXPECT_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()); @@ -500,8 +547,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_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()); @@ -534,9 +582,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { // has one slice EXPECT_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()); @@ -550,8 +599,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { // has one slice EXPECT_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()); @@ -564,8 +614,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_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()); @@ -618,9 +669,10 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { // has one slice EXPECT_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()); @@ -637,8 +689,9 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { // has one slice EXPECT_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); @@ -648,9 +701,10 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { // has one slice EXPECT_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}); @@ -670,7 +724,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { 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); @@ -702,7 +755,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { } TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -728,7 +781,6 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -779,7 +831,6 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -854,7 +905,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { 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); @@ -868,6 +918,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { // has one slice EXPECT_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); @@ -897,7 +948,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); valueProducer.mCondition = ConditionState::kFalse; shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); @@ -972,7 +1022,6 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { 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); @@ -1037,7 +1086,7 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { // 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)); sp<ValueMetricProducer> valueProducer = @@ -1057,10 +1106,11 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { EXPECT_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()); @@ -1075,9 +1125,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { // has one slice EXPECT_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}); @@ -1094,9 +1145,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); EXPECT_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}); } @@ -1139,16 +1191,18 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { EXPECT_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()); // pull on bucket boundary come late, condition change happens before it valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); - EXPECT_EQ(false, curInterval.hasBase); + EXPECT_EQ(false, curBaseInfo.hasBase); // Now the alarm is delivered. // since the condition turned to off before this pull finish, it has no effect @@ -1158,7 +1212,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); 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); } @@ -1211,9 +1266,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { EXPECT_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()); @@ -1222,15 +1278,17 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); EXPECT_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}); 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 @@ -1240,8 +1298,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { 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}); @@ -1269,7 +1328,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { 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); @@ -1314,7 +1372,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { 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); @@ -1361,7 +1418,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { 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); @@ -1412,7 +1468,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { 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); @@ -1458,7 +1513,6 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { 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); @@ -1473,8 +1527,9 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { EXPECT_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); @@ -1493,8 +1548,9 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { 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); + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15); @@ -1504,8 +1560,9 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); EXPECT_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); valueProducer.flushIfNeededLocked(bucket3StartTimeNs); @@ -1532,7 +1589,6 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { 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); @@ -1547,27 +1603,29 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { 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); + 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); + 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); @@ -1577,14 +1635,17 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { 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); + 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); shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15); event4->write(1); @@ -1593,14 +1654,16 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { 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); + 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); @@ -1647,9 +1710,11 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { EXPECT_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()); @@ -1669,8 +1734,8 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(11, interval1.base.long_value); + 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,11 +1745,19 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { 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); @@ -1722,11 +1795,13 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { 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); + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto& interval1 = it->second[0]; + auto& 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()); @@ -1746,22 +1821,31 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(11, interval1.base.long_value); + 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); + // auto itBase = valueProducer->mCurrentBaseInfo.begin(); + // for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) { + // if (itBase != iterBase) { + // break; + // } + // } + EXPECT_TRUE(it2 != it); + // EXPECT_TRUE(itBase != iterBase); + auto& interval2 = it2->second[0]; + auto& 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()); @@ -1776,8 +1860,8 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - 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_EQ(true, valueProducer->mHasGlobalBase); EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); @@ -1796,17 +1880,24 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { 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); + it = valueProducer->mCurrentSlicedBucket.begin(); + it2 = std::next(valueProducer->mCurrentSlicedBucket.begin()); + interval1 = it->second[0]; + interval2 = it2->second[0]; + baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; + baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0]; + + EXPECT_EQ(true, baseInfo1.hasBase); + EXPECT_EQ(5, baseInfo1.base.long_value); + EXPECT_EQ(false, interval1.hasValue); + EXPECT_EQ(5, interval1.value.long_value); EXPECT_EQ(true, valueProducer->mHasGlobalBase); + + EXPECT_EQ(true, baseInfo2.hasBase); + EXPECT_EQ(13, baseInfo2.base.long_value); + EXPECT_EQ(false, interval2.hasValue); + EXPECT_EQ(8, interval2.value.long_value); + EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); } @@ -1836,9 +1927,11 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_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()); vector<shared_ptr<LogEvent>> allData; @@ -1857,8 +1950,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(11, interval1.base.long_value); + 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); @@ -1870,11 +1963,19 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { 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_FALSE(interval2.seenNewData); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); @@ -1887,12 +1988,13 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { event1->init(); allData.push_back(event1); valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - // Only one interval left. One was trimmed. + // Only one interval left. One was trimmed. EXPECT_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}); @@ -1906,8 +2008,9 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { 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()); @@ -1943,14 +2046,15 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfB EXPECT_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); + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, valueProducer->mHasGlobalBase); } @@ -1980,8 +2084,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { EXPECT_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()); @@ -1990,7 +2095,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { // has one slice EXPECT_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); } @@ -2032,7 +2137,8 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { EXPECT_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); } @@ -2081,7 +2187,6 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucket2StartTimeNs, bucket2StartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); valueProducer.mCondition = ConditionState::kFalse; // Event should be skipped since it is from previous bucket. @@ -2116,13 +2221,17 @@ TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { EXPECT_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>(); @@ -2176,13 +2285,38 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { EXPECT_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()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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); @@ -2193,7 +2327,8 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) { // First onConditionChanged .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { for (int i = 0; i < 2000; i++) { - shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); + shared_ptr<LogEvent> event = + make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); event->write(i); event->write(i); event->init(); @@ -2209,9 +2344,47 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) { valueProducer->onConditionChanged(true, bucketStartTimeNs + 2); EXPECT_EQ(true, valueProducer->mCurrentBucketIsInvalid); EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(0UL, valueProducer->mSkippedBuckets.size()); + + // Bucket 2 start. + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event->write(1); + event->write(10); + event->init(); + allData.push_back(event); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + // First bucket added to mSkippedBuckets after flush. + EXPECT_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()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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>(); @@ -2271,13 +2444,39 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { EXPECT_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()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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>(); @@ -2318,8 +2517,6 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { allData.push_back(event); 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); @@ -2335,17 +2532,39 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); - // Last pull failed so based has been reset. + // Last pull failed so base has been reset. EXPECT_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()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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, _)) // Start bucket. @@ -2412,7 +2631,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { EXPECT_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); @@ -2420,7 +2640,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); EXPECT_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); } @@ -2468,7 +2689,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { EXPECT_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); @@ -2478,9 +2700,10 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_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); @@ -2527,59 +2750,19 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { // Key 1 should be reset since in not present in the most pull. EXPECT_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) { ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); @@ -2646,8 +2829,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { 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); @@ -2758,6 +2942,7 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { EXPECT_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); @@ -2768,6 +2953,7 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}); } +// TODO: b/145705635 fix or delete this test TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); @@ -2815,25 +3001,8 @@ TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { 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; -} - TEST(ValueMetricProducerTest, TestPullNeededFastDump) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -2862,12 +3031,10 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) { 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); @@ -2876,7 +3043,7 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) { } TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -2905,7 +3072,6 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -2919,9 +3085,8 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { 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. @@ -2930,7 +3095,7 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { } TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -2969,12 +3134,10 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { 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); @@ -3008,15 +3171,15 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges // condition becomes true .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent( - bucketStartTimeNs + 30, 10)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10)); return true; })) // condition becomes false .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent( - bucketStartTimeNs + 50, 20)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 50, 20)); return true; })); sp<ValueMetricProducer> valueProducer = @@ -3029,11 +3192,11 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges EXPECT_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)); @@ -3041,7 +3204,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}); 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); } @@ -3054,8 +3218,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { // condition becomes true .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent( - bucketStartTimeNs + 30, 10)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10)); return true; })); sp<ValueMetricProducer> valueProducer = @@ -3072,7 +3236,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}); 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); } @@ -3103,8 +3268,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) { // condition becomes true .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent( - bucketStartTimeNs + 30, 10)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10)); return true; })) .WillOnce(Return(false)); @@ -3124,6 +3289,1319 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) { 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, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + + // 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()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + + // Condition change event. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); + + // Bucket boundary pull. + vector<shared_ptr<LogEvent>> allData; + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs); + event->write("field1"); + event->write(15); + event->init(); + allData.push_back(event); + 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()); + EXPECT_EQ(1, report.value_metrics().data_size()); + EXPECT_EQ(1, report.value_metrics().skipped_size()); + + EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), + report.value_metrics().skipped(0).start_bucket_elapsed_millis()); + EXPECT_EQ(NanoToMillis(bucket3StartTimeNs), + report.value_metrics().skipped(0).end_bucket_elapsed_millis()); + EXPECT_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 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, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })) + // Dump report requested. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 100); + event->write("field1"); + event->write(15); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + + // Condition change event. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); + + // Bucket boundary pull. + vector<shared_ptr<LogEvent>> allData; + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs); + event->write("field1"); + event->write(15); + event->init(); + allData.push_back(event); + valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); + + allData.clear(); + event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100); + event->write("field1"); + event->write(20); + event->init(); + allData.push_back(event); + + // 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()); + EXPECT_EQ(1, report.value_metrics().data_size()); + EXPECT_EQ(1, report.value_metrics().skipped_size()); + + EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), + report.value_metrics().skipped(0).start_bucket_elapsed_millis()); + EXPECT_EQ(NanoToMillis(bucket3StartTimeNs), + report.value_metrics().skipped(0).end_bucket_elapsed_millis()); + EXPECT_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, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })) + // Dump report requested. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 100); + event->write("field1"); + event->write(15); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithNoInitialCondition(pullerManager, + metric); + + // Condition change event. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucketStartTimeNs + 100, true /* include recent buckets */, true, + NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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(bucketStartTimeNs + 100), 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, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })) + // Dump report requested, pull fails. + .WillOnce(Return(false)); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + + // Condition change event. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 50); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucketStartTimeNs + 100, true /* include recent buckets */, true, + NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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 + 100), 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, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })) + // Dump report requested. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = + make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1000); + event->write("field1"); + event->write(15); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + + // Condition change event. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); + + // Condition change event that skips forward by three buckets. + valueProducer->onConditionChanged(false, bucket4StartTimeNs + 10); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucket4StartTimeNs + 1000, true /* include recent buckets */, true, + NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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()); +} + +/* + * 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, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })) + // Dump report requested. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = + make_shared<LogEvent>(tagId, bucketStartTimeNs + 9000000); + event->write("field1"); + event->write(15); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + + // Condition change event. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, + true, NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_EQ(1, report.value_metrics().skipped_size()); + + EXPECT_EQ(NanoToMillis(bucketStartTimeNs), + report.value_metrics().skipped(0).start_bucket_elapsed_millis()); + EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), + report.value_metrics().skipped(0).end_bucket_elapsed_millis()); + EXPECT_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(bucketStartTimeNs + 9000000), 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, _)) + // Condition change to true. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithNoInitialCondition(pullerManager, + metric); + + // Condition change event. + valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true, + FAST /* dumpLatency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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(bucketStartTimeNs + 1000), 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, _)) + // First condition change event. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + 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); + } + 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, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 220); + event->write("field1"); + event->write(10); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithNoInitialCondition(pullerManager, + metric); + + // 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; + // 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()); + EXPECT_EQ(0, report.value_metrics().data_size()); + EXPECT_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()); + EXPECT_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, _)) + // ValueMetricProducer initialized. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs); + event->write("field1"); + event->write(3); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to ON. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 5); + event->write("field1"); + event->write(5); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to OFF. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event->write("field1"); + event->write(9); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to ON. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15); + event->write("field1"); + event->write(21); + event->init(); + data->push_back(event); + return true; + })) + // Dump report requested. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50); + event->write("field1"); + event->write(30); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithState( + pullerManager, metric, {android::util::SCREEN_STATE_CHANGED}, {}); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().clear(); + StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + + // Bucket status after metric initialized. + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(3, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, kStateUnknown} + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after screen state change kStateUnknown->ON. + auto screenEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 5); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(5, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, kStateUnknown} + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Bucket status after screen state change ON->OFF. + screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 10); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(9, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, ON} + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(4, it->second[0].value.long_value); + // Value for dimension, state key {{}, kStateUnknown} + it++; + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Bucket status after screen state change OFF->ON. + screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 15); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(21, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, OFF} + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(12, it->second[0].value.long_value); + // Value for dimension, state key {{}, ON} + it++; + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(4, it->second[0].value.long_value); + // Value for dimension, state key {{}, kStateUnknown} + it++; + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(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()); + EXPECT_EQ(3, report.value_metrics().data_size()); + + auto data = report.value_metrics().data(0); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); + + data = report.value_metrics().data(1); + EXPECT_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); + EXPECT_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, _)) + // ValueMetricProducer initialized. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs); + event->write("field1"); + event->write(3); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to ON. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 5); + event->write("field1"); + event->write(5); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to VR. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event->write("field1"); + event->write(9); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to OFF. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15); + event->write("field1"); + event->write(21); + event->init(); + data->push_back(event); + return true; + })) + // Dump report requested. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50); + event->write("field1"); + event->write(30); + event->init(); + data->push_back(event); + return true; + })); + + const StateMap& stateMap = CreateScreenStateOnOffMap(); + 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(); + } + } + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithState( + pullerManager, metric, {android::util::SCREEN_STATE_CHANGED}, stateGroupMap); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().clear(); + StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + + // Bucket status after metric initialized. + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(3, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, {}} + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after screen state change kStateUnknown->ON. + auto screenEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 5); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(5, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, kStateUnknown} + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Bucket status after screen state change ON->VR (also ON). + screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_VR, + bucketStartTimeNs + 10); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(9, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, ON GROUP} + EXPECT_EQ(screenOnGroup.group_id(), + it->first.getStateValuesKey().getValues()[0].mValue.long_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(4, it->second[0].value.long_value); + // Value for dimension, state key {{}, kStateUnknown} + it++; + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Bucket status after screen state change VR->OFF. + screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 15); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(21, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, ON GROUP} + EXPECT_EQ(screenOnGroup.group_id(), + it->first.getStateValuesKey().getValues()[0].mValue.long_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(16, it->second[0].value.long_value); + // Value for dimension, state key {{}, kStateUnknown} + it++; + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(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()); + EXPECT_EQ(3, report.value_metrics().data_size()); + + auto data = report.value_metrics().data(0); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); + + data = report.value_metrics().data(1); + EXPECT_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); + EXPECT_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, _)) + // ValueMetricProducer initialized. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs); + event->write(2 /* uid */); + event->write(7); + event->init(); + data->push_back(event); + + event = make_shared<LogEvent>(tagId, bucketStartTimeNs); + event->write(1 /* uid */); + event->write(3); + event->init(); + data->push_back(event); + return true; + })) + // Uid 1 process state change from kStateUnknown -> Foreground + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); + event->write(1 /* uid */); + event->write(6); + event->init(); + data->push_back(event); + + // This event should be skipped. + event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); + event->write(2 /* uid */); + event->write(8); + event->init(); + data->push_back(event); + return true; + })) + // Uid 2 process state change from kStateUnknown -> Background + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 40); + event->write(2 /* uid */); + event->write(9); + event->init(); + data->push_back(event); + + // This event should be skipped. + event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 40); + event->write(1 /* uid */); + event->write(12); + event->init(); + data->push_back(event); + return true; + })) + // Uid 1 process state change from Foreground -> Background + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20); + event->write(1 /* uid */); + event->write(13); + event->init(); + data->push_back(event); + + // This event should be skipped. + event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20); + event->write(2 /* uid */); + event->write(11); + event->init(); + data->push_back(event); + return true; + })) + // Uid 1 process state change from Background -> Foreground + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 40); + event->write(1 /* uid */); + event->write(17); + event->init(); + data->push_back(event); + + // This event should be skipped. + event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 40); + event->write(2 /* uid */); + event->write(15); + event->init(); + data->push_back(event); + return true; + })) + // Dump report pull. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50); + event->write(2 /* uid */); + event->write(20); + event->init(); + data->push_back(event); + + event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50); + event->write(1 /* uid */); + event->write(21); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithState( + pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().clear(); + StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); + + // Bucket status after metric initialized. + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {uid 1}. + auto it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(3, itBase->second[0].base.long_value); + // Value for dimension, state key {{uid 1}, kStateUnknown} + // TODO(tsaichristine): test equality of state values key + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + // Base for dimension key {uid 2} + it++; + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(7, itBase->second[0].base.long_value); + // Value for dimension, state key {{uid 2}, kStateUnknown} + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after uid 1 process state change kStateUnknown -> Foreground. + auto uidProcessEvent = CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucketStartTimeNs + 20); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {uid 1}. + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(6, itBase->second[0].base.long_value); + // Value for key {uid 1, kStateUnknown}. + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(3, it->second[0].value.long_value); + + // Base for dimension key {uid 2} + it++; + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(7, itBase->second[0].base.long_value); + // Value for key {uid 2, kStateUnknown} + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after uid 2 process state change kStateUnknown -> Background. + uidProcessEvent = CreateUidProcessStateChangedEvent( + 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucketStartTimeNs + 40); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {uid 1}. + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(6, itBase->second[0].base.long_value); + // Value for key {uid 1, kStateUnknown}. + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(3, it->second[0].value.long_value); + + // Base for dimension key {uid 2} + it++; + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(9, itBase->second[0].base.long_value); + // Value for key {uid 2, kStateUnknown} + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Pull at end of first bucket. + vector<shared_ptr<LogEvent>> allData; + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs); + event->write(1 /* uid */); + event->write(10); + event->init(); + allData.push_back(event); + + event = make_shared<LogEvent>(tagId, bucket2StartTimeNs); + event->write(2 /* uid */); + event->write(15); + event->init(); + allData.push_back(event); + + valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); + + // Buckets flushed after end of first bucket. + // None of the buckets should have a value. + EXPECT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(4UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); + // Base for dimension key {uid 2}. + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(15, itBase->second[0].base.long_value); + // Value for key {uid 2, BACKGROUND}. + EXPECT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(1006, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + + // Base for dimension key {uid 1} + it++; + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(10, itBase->second[0].base.long_value); + // Value for key {uid 1, kStateUnknown} + EXPECT_EQ(0, it->first.getStateValuesKey().getValues().size()); + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + + // Value for key {uid 1, FOREGROUND} + it++; + EXPECT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + + // Value for key {uid 2, kStateUnknown} + it++; + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after uid 1 process state change from Foreground -> Background. + uidProcessEvent = CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucket2StartTimeNs + 20); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + + EXPECT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(4UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); + // Base for dimension key {uid 2}. + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(15, itBase->second[0].base.long_value); + // Value for key {uid 2, BACKGROUND}. + EXPECT_EQ(false, it->second[0].hasValue); + // Base for dimension key {uid 1} + it++; + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(13, itBase->second[0].base.long_value); + // Value for key {uid 1, kStateUnknown} + EXPECT_EQ(false, it->second[0].hasValue); + // Value for key {uid 1, FOREGROUND} + it++; + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(3, it->second[0].value.long_value); + // Value for key {uid 2, kStateUnknown} + it++; + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after uid 1 process state change Background->Foreground. + uidProcessEvent = CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucket2StartTimeNs + 40); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + + EXPECT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); + // Base for dimension key {uid 2} + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(15, itBase->second[0].base.long_value); + EXPECT_EQ(false, it->second[0].hasValue); + + it++; + EXPECT_EQ(false, it->second[0].hasValue); + + // Base for dimension key {uid 1} + it++; + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(17, itBase->second[0].base.long_value); + // Value for key {uid 1, BACKGROUND} + EXPECT_EQ(1006, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(4, it->second[0].value.long_value); + // Value for key {uid 1, FOREGROUND} + it++; + EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(3, it->second[0].value.long_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()); + EXPECT_EQ(5, report.value_metrics().data_size()); + + auto data = report.value_metrics().data(0); + EXPECT_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); + EXPECT_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()); + + 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()); + EXPECT_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); + EXPECT_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()); + + 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()); + EXPECT_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()); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp index 7b9c0d6ab28e..108df04b45cb 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp +++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp @@ -26,10 +26,23 @@ HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) { 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 @@ void buildSimpleAtomFieldMatcher(const int tagId, const int fieldNum, FieldMatch } // 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 97c107228f9c..09c4d9e41fc2 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ b/cmds/statsd/tests/metrics/metrics_test_helper.h @@ -26,11 +26,9 @@ namespace statsd { 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 { @@ -49,10 +47,13 @@ class MockUidMap : public UidMap { 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/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp new file mode 100644 index 000000000000..84aaa54bc5bf --- /dev/null +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -0,0 +1,504 @@ +/* + * 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 "state/StateManager.h" +#include "state/StateTracker.h" +#include "state/StateListener.h" + +#include "tests/statsd_test_util.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +/** + * 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, int oldState, int newState) { + updates.emplace_back(primaryKey, newState); + } +}; + +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. +// State with no primary fields - ScreenStateChanged +std::shared_ptr<LogEvent> buildScreenEvent(int state) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::SCREEN_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)state); + event->init(); + return event; +} + +// State with one primary field - UidProcessStateChanged +std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)uid); + event->write((int32_t)state); + event->init(); + return event; +} + +// State with first uid in attribution chain as primary field - WakelockStateChanged +std::shared_ptr<LogEvent> buildPartialWakelockEvent(int uid, const std::string& tag, bool acquire) { + std::vector<AttributionNodeInternal> chain; + chain.push_back(AttributionNodeInternal()); + AttributionNodeInternal& attr = chain.back(); + attr.set_uid(uid); + + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, 1000 /* timestamp */); + event->write(chain); + event->write((int32_t)1); // PARTIAL_WAKE_LOCK + event->write(tag); + event->write(acquire ? 1 : 0); + event->init(); + return event; +} + +// State with multiple primary fields - OverlayStateChanged +std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)uid); + event->write(packageName); + event->write(true); // using_alert_window + event->write((int32_t)state); + event->init(); + return event; +} + +// Incorrect event - missing fields +std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)uid); + event->write(packageName); + event->write((int32_t)state); + event->init(); + return event; +} + +// Incorrect event - exclusive state has wrong type +std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)uid); + event->write(packageName); + event->write(true); + event->write("string"); // exclusive state: string instead of int + event->init(); + return event; +} +// END: build event functions. + +// 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)); +} + +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)); +} + +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)); +} + +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 + +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(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); +} + +/** + * 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()); + EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1)); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Register listener to existing StateTracker + EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2)); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Register already registered listener to existing StateTracker + EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2)); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Register listener to non-state atom + EXPECT_FALSE(mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2)); + EXPECT_EQ(1, 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(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_EQ(0, mgr.getStateTrackersCount()); + EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Unregister non-registered listener from existing StateTracker + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Unregister second-to-last listener from StateTracker + mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2); + mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Unregister last listener from StateTracker + mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2); + EXPECT_EQ(0, mgr.getStateTrackersCount()); + EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); +} + +/** + * 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(android::util::SCREEN_STATE_CHANGED, listener1); + + // log event + std::shared_ptr<LogEvent> event = + buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); + mgr.onLogEvent(*event); + + // check listener was updated + EXPECT_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, android::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(android::util::UID_PROCESS_STATE_CHANGED, listener1); + + // log event + std::shared_ptr<LogEvent> event = + buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP); + mgr.onLogEvent(*event); + + // check listener was updated + EXPECT_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, android::util::UID_PROCESS_STATE_CHANGED, queryKey)); +} + +TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) { + sp<TestStateListener> listener1 = new TestStateListener(); + StateManager mgr; + mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener1); + + // Log event. + std::shared_ptr<LogEvent> event = + buildPartialWakelockEvent(1001 /* uid */, "tag1", false /* acquire */); + mgr.onLogEvent(*event); + + EXPECT_EQ(1, mgr.getStateTrackersCount()); + EXPECT_EQ(1, mgr.getListenersCount(android::util::WAKELOCK_STATE_CHANGED)); + + // Check listener was updated. + EXPECT_EQ(1, listener1->updates.size()); + EXPECT_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("tag1", listener1->updates[0].mKey.getValues()[2].mValue.str_value); + EXPECT_EQ(WakelockStateChanged::RELEASE, listener1->updates[0].mState); + + // Check StateTracker was updated by querying for state. + HashableDimensionKey queryKey; + getPartialWakelockKey(1001 /* uid */, "tag1", &queryKey); + EXPECT_EQ(WakelockStateChanged::RELEASE, + getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey)); + + // No state stored for this query key. + HashableDimensionKey queryKey2; + getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2); + EXPECT_EQ(-1, getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey2)); + + // Partial query fails. + HashableDimensionKey queryKey3; + getPartialWakelockKey(1001 /* uid */, &queryKey3); + EXPECT_EQ(-1, getStateInt(mgr, android::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(android::util::OVERLAY_STATE_CHANGED, listener1); + + // log event + std::shared_ptr<LogEvent> event = + buildOverlayEvent(1000 /* uid */, "package1", 1); // state: ENTERED + mgr.onLogEvent(*event); + + // check listener was updated + EXPECT_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, android::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(android::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); + EXPECT_EQ(0, listener1->updates.size()); + mgr.onLogEvent(*event2); + EXPECT_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(android::util::SCREEN_STATE_CHANGED, listener1); + mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener2); + mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener3); + mgr.registerListener(android::util::WAKELOCK_STATE_CHANGED, listener4); + + std::shared_ptr<LogEvent> event1 = buildUidProcessEvent( + 1000, + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + std::shared_ptr<LogEvent> event2 = buildUidProcessEvent( + 1001, + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: + // 1003 + std::shared_ptr<LogEvent> event3 = buildUidProcessEvent( + 1002, + android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000 + std::shared_ptr<LogEvent> event4 = buildUidProcessEvent( + 1001, + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + std::shared_ptr<LogEvent> event5 = + buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); + std::shared_ptr<LogEvent> event6 = + buildOverlayEvent(1000, "package1", OverlayStateChanged::ENTERED); + std::shared_ptr<LogEvent> event7 = + buildOverlayEvent(1000, "package2", OverlayStateChanged::EXITED); + std::shared_ptr<LogEvent> event8 = buildPartialWakelockEvent(1005, "tag1", true); + std::shared_ptr<LogEvent> event9 = buildPartialWakelockEvent(1005, "tag2", false); + + 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, android::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, android::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, android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); + + // Query for ScreenState + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + getStateInt(mgr, android::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, android::util::OVERLAY_STATE_CHANGED, queryKey3)); + + // Query for WakelockState of uid 1005, tag 2 + HashableDimensionKey queryKey4; + getPartialWakelockKey(1005, "tag2", &queryKey4); + EXPECT_EQ(WakelockStateChanged::RELEASE, + getStateInt(mgr, android::util::WAKELOCK_STATE_CHANGED, queryKey4)); + + // Query for WakelockState of uid 1005, tag 1 + HashableDimensionKey queryKey5; + getPartialWakelockKey(1005, "tag1", &queryKey5); + EXPECT_EQ(WakelockStateChanged::ACQUIRE, + getStateInt(mgr, android::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 2c4f3c7692c9..7b651dfed7fc 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -18,6 +18,23 @@ 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; @@ -251,6 +268,101 @@ Predicate CreateIsInBackgroundPredicate() { return predicate; } +State CreateScreenState() { + State state; + state.set_id(StringToId("ScreenState")); + state.set_atom_id(android::util::SCREEN_STATE_CHANGED); + return state; +} + +State CreateUidProcessState() { + State state; + state.set_id(StringToId("UidProcessState")); + state.set_atom_id(android::util::UID_PROCESS_STATE_CHANGED); + return state; +} + +State CreateOverlayState() { + State state; + state.set_id(StringToId("OverlayState")); + state.set_atom_id(android::util::OVERLAY_STATE_CHANGED); + return state; +} + +State CreateScreenStateWithOnOffMap() { + State state; + state.set_id(StringToId("ScreenStateOnOff")); + state.set_atom_id(android::util::SCREEN_STATE_CHANGED); + + auto map = CreateScreenStateOnOffMap(); + *state.mutable_map() = map; + + return state; +} + +State CreateScreenStateWithInDozeMap() { + State state; + state.set_id(StringToId("ScreenStateInDoze")); + state.set_atom_id(android::util::SCREEN_STATE_CHANGED); + + auto map = CreateScreenStateInDozeMap(); + *state.mutable_map() = map; + + return state; +} + +StateMap_StateGroup CreateScreenStateOnGroup() { + StateMap_StateGroup group; + group.set_group_id(StringToId("SCREEN_ON")); + group.add_value(2); + group.add_value(5); + group.add_value(6); + return group; +} + +StateMap_StateGroup CreateScreenStateOffGroup() { + StateMap_StateGroup group; + group.set_group_id(StringToId("SCREEN_OFF")); + group.add_value(0); + group.add_value(1); + group.add_value(3); + group.add_value(4); + return group; +} + +StateMap CreateScreenStateOnOffMap() { + StateMap map; + *map.add_group() = CreateScreenStateOnGroup(); + *map.add_group() = CreateScreenStateOffGroup(); + return map; +} + +StateMap_StateGroup CreateScreenStateInDozeGroup() { + StateMap_StateGroup group; + group.set_group_id(StringToId("SCREEN_DOZE")); + group.add_value(3); + group.add_value(4); + return group; +} + +StateMap_StateGroup CreateScreenStateNotDozeGroup() { + StateMap_StateGroup group; + group.set_group_id(StringToId("SCREEN_NOT_DOZE")); + group.add_value(0); + group.add_value(1); + group.add_value(2); + group.add_value(5); + group.add_value(6); + return group; +} + +StateMap CreateScreenStateInDozeMap() { + StateMap map; + *map.add_group() = CreateScreenStateInDozeGroup(); + *map.add_group() = CreateScreenStateNotDozeGroup(); + return map; +} + void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combinationPredicate) { combinationPredicate->mutable_combination()->add_predicate(predicate.id()); @@ -438,6 +550,15 @@ std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampN uid, ProcessLifeCycleStateChanged::CRASHED, timestampNs); } +std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(const int uid, uint64_t timestampNs) { + auto event = std::make_unique<LogEvent>(android::util::APP_CRASH_OCCURRED, timestampNs); + event->write(uid); + event->write("eventType"); + event->write("processName"); + event->init(); + return event; +} + std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent( int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs) { auto logEvent = std::make_unique<LogEvent>( @@ -449,6 +570,15 @@ std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent( return logEvent; } +std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( + int uid, const android::app::ProcessStateEnum state, uint64_t timestampNs) { + auto event = std::make_unique<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, timestampNs); + event->write(uid); + event->write(state); + event->init(); + return event; +} + sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs, const StatsdConfig& config, const ConfigKey& key) { sp<UidMap> uidMap = new UidMap(); diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 635c5835333a..9bdfeebe561f 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -27,8 +27,15 @@ namespace android { namespace os { namespace statsd { +using android::util::ProtoReader; using google::protobuf::RepeatedPtrField; +const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED; +const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED; + +// 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); @@ -104,6 +111,37 @@ Predicate CreateIsSyncingPredicate(); // 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(); + +State CreateScreenStateWithOnOffMap(); + +State CreateScreenStateWithInDozeMap(); + +// Create StateGroup proto for ScreenState ON group +StateMap_StateGroup CreateScreenStateOnGroup(); + +// Create StateGroup proto for ScreenState OFF group +StateMap_StateGroup CreateScreenStateOffGroup(); + +// Create StateMap proto for ScreenState ON/OFF map +StateMap CreateScreenStateOnOffMap(); + +// Create StateGroup proto for ScreenState IN DOZE group +StateMap_StateGroup CreateScreenStateInDozeGroup(); + +// Create StateGroup proto for ScreenState NOT IN DOZE group +StateMap_StateGroup CreateScreenStateNotDozeGroup(); + +// Create StateMap proto for ScreenState IN DOZE map +StateMap CreateScreenStateInDozeMap(); + // Add a predicate to the predicate combination. void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination); @@ -161,6 +199,9 @@ std::unique_ptr<LogEvent> CreateSyncEndEvent( std::unique_ptr<LogEvent> CreateAppCrashEvent( const int uid, uint64_t timestampNs); +// Create log event for an app crash. +std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(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, @@ -175,6 +216,10 @@ std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent( int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs); +// Create log event for uid process state change. +std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( + int uid, const android::app::ProcessStateEnum state, uint64_t timestampNs); + // Helper function to create an AttributionNodeInternal proto. AttributionNodeInternal CreateAttribution(const int& uid, const string& tag); @@ -319,4 +364,4 @@ void backfillStartEndTimestampForSkippedBuckets(const int64_t timeBaseNs, T* met } } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tools/dogfood/Android.bp b/cmds/statsd/tools/dogfood/Android.bp deleted file mode 100644 index bb494a6025af..000000000000 --- 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 52673fbdcf92..000000000000 --- 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 Binary files differdeleted file mode 100644 index 55621cc1074f..000000000000 --- a/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 11ec2068be19..000000000000 --- a/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 7c02b784aa5d..000000000000 --- a/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 915d91441349..000000000000 --- a/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null 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 784ed40ce2c2..000000000000 --- 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 d05006124994..000000000000 --- a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config +++ /dev/null @@ -1,109 +0,0 @@ -¹ÓõÕ¯¤”årÝèåâÕ»éÉ7¬ìŸ‘Ψê.–´šÄÁŒç¶ÚšäÑùôºZªÑ€¸ëÐé푘‹Ý™ûÆŽ6ˆ™ÁÖ΂õ›Ã¥¼à¨É—ÿQÝàåÈ߀äÍ@ÆÙÑ„ÓІ„èѿ̲±™¨lŸÄ€ìúˆÖü¶ú̳Šéâ‡ãà§ØÈË´Ù%óÓÚ«¹¼ÜyÛ®±ë¸«”º?¹•繡¸Œ€â鿨ŒÙœ®‘)çæ©Á”§äéîÀ‚Ò®ö‡.ÐÙ•°‹ÁÈy"'(WòóÀ–ðßæ¬ùÕàÀ™ýëZ”ÔÆ™Ž¤š¹»" -(2!ûÄߪæþªâÚ -V¢¦€¡’€Ç€¡ùÕàÀ™ýëZÈ‹ÄÂë¢Ä¯Ø" -(2 ª–¨Œ×ÿ¹¡k -V‡úÌòá㿘>ùÕàÀ™ýëZáž‹â‘Ý»ä" -(2!÷‘–ä–Œ¿¯ -WØõé¼ÃóÀàþùÕàÀ™ýëZ°ÔØùΣ‘¯" -(2!Ïä†÷ɯ -VÚœäÌþ½¬jùÕàÀ™ýëZ³Ö‰Ó¾ ½Ñ" -(2!Ÿ¡ªˆëôùÇ -V¨¾ñ≎̅ùÕàÀ™ýëZ“Ðåÿèåò?" -(2!½šòÐ䤿Œ -U…£²†µÜ‡‘_ùÕàÀ™ýëZñîôÒ‡¤´†z" -(2!é’¬¸ç¼‡Óª -K‘ã´¶üÅçúñ±Ë壆¨D”ÔÆ™Ž¤š¹»" -#(2ûÄߪæþªâÚ#Ië§ì·ºê•Æúñ±Ë壆¨DÈ‹ÄÂë¢Ä¯Ø" -#(2ª–¨Œ×ÿ¹¡k#JÙ¢Öúƒ†ðúñ±Ë壆¨Dáž‹â‘Ý»ä" -#(2÷‘–ä–Œ¿¯#JãýØåóªü)úñ±Ë壆¨D°ÔØùΣ‘¯" -#(2Ïä†÷ɯ#KËÅÝÇž¹Åãúñ±Ë壆¨D³Ö‰Ó¾ ½Ñ" -#(2Ÿ¡ªˆëôùÇ#Jɳ¸ó€„ø‘Ãúñ±Ë壆¨D“Ðåÿèåò?" -#(2½šòÐ䤿Œ#J•–ó«ðô¤Þïúñ±Ë壆¨DñîôÒ‡¤´†z" -#(2é’¬¸ç¼‡Óª#J•ºÔ¯•…Î’:›ºô¿Úó¾kŒÓÜ¢¾ÌÊ" -(2ûÄߪæþªâÚI»ÓòÓí‰àÖ -›ºô¿Úó¾kǮ›ŸÊŸäâ" -(2ª–¨Œ×ÿ¹¡kJÖÐük÷y›ºô¿Úó¾kÿ‚¾žÃ“’§õ" -(2÷‘–ä–Œ¿¯JøÿÊ“åøË¨›ºô¿Úó¾k˜ý˜‘¢ŒÐÀI" -(2Ïä†÷ɯKõß°—’ì¾×›ºô¿Úó¾kì‘áÒîŸÙÍá" -(2Ÿ¡ªˆëôùÇJà¸ëòä¥û݈›ºô¿Úó¾kôé‹¿¿¸ßÄ" -(2½šòÐ䤿ŒJУŒð†¬µÙ›ºô¿Úó¾k‰±Öйˆ–Ôõ" -(2é’¬¸ç¼‡ÓªK—ÍË é÷ÐM鮣öü©µŒÓÜ¢¾ÌÊ" -(2ûÄߪæþªâÚJÌŽë á«¡´{鮣öü©µÇ®Â›ŸÊŸäâ" -(2ª–¨Œ×ÿ¹¡kL˜†ŒÏØì¡‹³é®£öü©µÿ‚¾žÃ“’§õ" -(2÷‘–ä–Œ¿¯Ká°Ñàƒ½†¶ý鮣öü©µ˜ý˜‘¢ŒÐÀI" -(2Ïä†÷ɯKÛ”Ÿò§…ÂÁY鮣öü©µì‘áÒîŸÙÍá" -(2Ÿ¡ªˆëôùÇKϜݗÞç±ù¥é®£öü©µôé‹¿¿¸ßÄ" -(2½šòÐ䤿ŒKÚÖ•·‰–B鮣öü©µ‰±Öйˆ–Ôõ" -(2é’¬¸ç¼‡Óª,Ä®öèÀƒ‘âÌ¥•€†ÜÖ×AÐÙ•°‹ÁÈy" -(€õ»•÷¾ÓçȪŠéñðȃŹ~”‰àÉ®¶f"(2é’¬¸ç¼‡Óª2½šòÐ䤿Œ2Ÿ¡ªˆëôùÇLشͼá™þþæ¿É¶ˆœ‚æð±ûÄߪæþªâÚ" --(2ûÄߪæþªâÚ-JÉ¥˜‘Ûüˆ‹ò¿É¶ˆœ‚æð±ª–¨Œ×ÿ¹¡k" --(2ª–¨Œ×ÿ¹¡k-L¡©äÞ¨ô¶ø½¿É¶ˆœ‚æð±÷‘–ä–Œ¿¯" --(2÷‘–ä–Œ¿¯-L‡õÇþ¡¾ÚÈþ¿É¶ˆœ‚æð±Ïä†÷ɯ" --(2Ïä†÷ɯ-L—î«ãïÚ½û¬¿É¶ˆœ‚æð±Ÿ¡ªˆëôùÇ" --(2Ÿ¡ªˆëôùÇ-LñÍý‚ÿô¿É¶ˆœ‚æð±½šòÐ䤿Œ" --(2½šòÐ䤿Œ-K‹òʰ«»íè|¿É¶ˆœ‚æð±é’¬¸ç¼‡Óª" --(2é’¬¸ç¼‡Óª-"9äëæÚòœ§=̨…¥”ìãÏ‘N ÐÙ•°‹ÁÈy*‘N0@":šˆž‚ÎñÐ̨…¥”ìãÏ‘N ÐÙ•°‹ÁÈy*‘N0@":ù»Žôç¾ãˆÐ̨…¥”ìãÏ‘N ÐÙ•°‹ÁÈy*‘N0@"9›™›ŸÝ†—ãS̨…¥”ìãÏ‘N ÐÙ•°‹ÁÈy*‘N0@":‹ããùŸáŽôºßñ¼ûÌ•˜“N ÐÙ•°‹ÁÈy*“N0@":ç΀àë Œƒôºßñ¼ûÌ•˜“N ÐÙ•°‹ÁÈy*“N0@":™˜µøÖ²Äìõôºßñ¼ûÌ•˜“N ÐÙ•°‹ÁÈy*“N0@":¬Öœ¥¥ÃÆŒÑôºßñ¼ûÌ•˜“N ÐÙ•°‹ÁÈy*“N0@"5ôÇ™±¶Ï¢‘ò•™ÔéŠñ˜Èu›N ÐÙ•°‹ÁÈy*›N0@"4«÷µÿ󲂜e•™ÔéŠñ˜Èu›N ÐÙ•°‹ÁÈy*›N0@"4å¢úˆÑÙúý•™ÔéŠñ˜Èu›N ÐÙ•°‹ÁÈy*›N0@"+ž—Õõ‰î¶±~‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@",‘ó‚³ïÙù¦‚‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@"+òßù¥·«Äà;‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@",ÓûÅñÒ¢ô§Â‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@"+¨²°žò³ƒ¨R‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@"+ÞìøݱþÖq‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@",͉šæÜŽÄÀ‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@"3Ä»±ê¾¸ô°˜þËÉÇåãÃ2 ÐÙ•°‹ÁÈy*0@"‰Ñ½Ëñɤ¡™a˜þËÉÇåãÃ2 ”‰àÉ®¶f*0:é’¬¸ç¼‡Óª:½šòÐ䤿Œ:Ÿ¡ªˆëôùÇ@":ÃêåÒýéó¦ï‘ŽÁ°×ËÇÅß–N ÐÙ•°‹ÁÈy*–N0@":Àš‚ÑÀí£Ï‹‘ŽÁ°×ËÇÅß–N ÐÙ•°‹ÁÈy*–N0@"9¾¢ðÛßÒëéø›²Ö·ÔÈšN ÐÙ•°‹ÁÈy*šN0@"9÷´ç鼯Š~½ ¤„¹®Ð§¨˜N ÐÙ•°‹ÁÈy*˜N0@2YÝæÆËò ‚ú‘»óÓ¤ãËÕ”ÔÆ™Ž¤š¹»"!ûÄߪæþªâÚ -(2 -82X°öòΪáŠþ„»óÓ¤ãËÕÈ‹ÄÂë¢Ä¯Ø" ª–¨Œ×ÿ¹¡k -(2 -82X°ç¦ùºŒó·0»óÓ¤ãËÕáž‹â‘Ý»ä"!÷‘–ä–Œ¿¯ -(2 -82Yôæï°Þ¼ ”»óÓ¤ãËÕ°ÔØùΣ‘¯"!Ïä†÷ɯ -(2 -82Y¡Î°Ãåüý»óÓ¤ãËÕ³Ö‰Ó¾ ½Ñ"!Ÿ¡ªˆëôùÇ -(2 -82XÕ¤ô®Ã£ÇÓÈ»óÓ¤ãËÕ“Ðåÿèåò?"!½šòÐ䤿Œ -(2 -82XÖÂªßøæÌÊ»óÓ¤ãËÕñîôÒ‡¤´†z"!é’¬¸ç¼‡Óª -(2 -825’ÌíûÍõ÷Æ»óÓ¤ãËÕ®Èùé€ù’±W(2 -82X²¹÷߆ŒÀ¶`»óÓ¤ãËÕ”ÔÆ™Ž¤š¹»"!ûÄߪæþªâÚ -(2 -82W÷ÞŸÉÔ ëÍd»óÓ¤ãËÕÈ‹ÄÂë¢Ä¯Ø" ª–¨Œ×ÿ¹¡k -(2 -82XäÁš´¤·ñì.»óÓ¤ãËÕáž‹â‘Ý»ä"!÷‘–ä–Œ¿¯ -(2 -82YîæÍçâ»óÓ¤ãËÕ°ÔØùΣ‘¯"!Ïä†÷ɯ -(2 -82Y˜²Ì´×³ìóÓ¤ãËÕ³Ö‰Ó¾ ½Ñ"!Ÿ¡ªˆëôùÇ -(2 -82W¾¯†‘§È“¿P»óÓ¤ãËÕ“Ðåÿèåò?"!½šòÐ䤿Œ -(2 -82XÂÀ¶òšË˜‚ð»óÓ¤ãËÕñîôÒ‡¤´†z"!é’¬¸ç¼‡Óª -(2 -82U—îïÛ²¸¸†µ»óÓ¤ãËÕ”ÔÆ™Ž¤š¹»"!ûÄߪæþªâÚ -(2 -82Tû¯ûû²¶ýñ§»óÓ¤ãËÕÈ‹ÄÂë¢Ä¯Ø" ª–¨Œ×ÿ¹¡k -(2 -82T†ñù™ŸÈ¨«»óÓ¤ãËÕáž‹â‘Ý»ä"!÷‘–ä–Œ¿¯ -(2 -82T҈πŸ¯‰ŠD»óÓ¤ãËÕ°ÔØùΣ‘¯"!Ïä†÷ɯ -(2 -82Uš¤Ë„阿ö†»óÓ¤ãËÕ³Ö‰Ó¾ ½Ñ"!Ÿ¡ªˆëôùÇ -(2 -82SË©ËÞ¯öÈÞ&»óÓ¤ãËÕ“Ðåÿèåò?"!½šòÐ䤿Œ -(2 -82TάóýᔺèÞ»óÓ¤ãËÕñîôÒ‡¤´†z"!é’¬¸ç¼‡Óª -(2 -82#àšÉ±ûÿ±»óÓ¤ãËÕ®Èùé€ù’±W(82#ÞžÞïƒ÷“…±œ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(82"䣞ƒƒ¿Èòrœ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(82*¦‚ó©ÂÊÓŒ3œ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(2%82*ÅМ½à®ÄŠœ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(2%82"‡è‘²†ÙÓÂ+œ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(82J›»ÜŽáø©ÔӈƄת¶£«ŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ'(2'82H®Ñä‚Òš„ÝqˆÆ„ת¶£«Ç®Â›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k'(2'82JêýÂÄ…Œ™ÉˆƄת¶£«ÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯'(2'82Iâ븛ÚðÖóˆÆ„×ª¶£«˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ'(2'82Iÿ±å¨ ïÉKˆÆ„ת¶£«ì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ'(2'82Hý©ëÞ¥é÷“\ˆÆ„ת¶£«ôé‹¿¿¸ßÄ"½šòÐ䤿Œ'(2'82JÁ°Õ즊ý…¯ˆÆ„ת¶£«‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª'(2'82ƒ˜Êã•ÿÊÙÚ·æúŸÌ½‚¡”‰àÉ®¶f"é’¬¸ç¼‡Óª"½šòÐ䤿Œ"Ÿ¡ªˆëôùÇ(282‚ÊùÑèÂóžàY·æúŸÌ½‚¡”‰àÉ®¶f"é’¬¸ç¼‡Óª"½šòÐ䤿Œ"Ÿ¡ªˆëôùÇ(282MÚËñ´ä‡¼Ä'ª ÿçû¤é刌ÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(2 -82LŽÏ—ꤦݯ5ª ÿçû¤éåˆÇ®Â›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(2 -82Nø‚ÍÁœÆÚª ÿçû¤éåˆÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(2 -82LŸ¿ŽöåèÆ)ª ÿçû¤é刘ý˜‘¢ŒÐÀI"Ïä†÷ɯ(2 -82NþÝÒÿ§Ù¡±˜ª ÿçû¤éåˆì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(2 -82M³Ñ×饊ÿ¦ª ÿçû¤éåˆôé‹¿¿¸ßÄ"½šòÐ䤿Œ(2 -82M–ÎÜ‹ó¯Ë„ª ÿçû¤é刉±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(2 -82ƒÍê´ýÞÁöª ÿçû¤é刔‰àÉ®¶f"é’¬¸ç¼‡Óª"½šòÐ䤿Œ"Ÿ¡ªˆëôùÇ(282,Ü›ä¤ö¸Åˆºª ÿçû¤éåˆÐÙ•°‹ÁÈy(282+Ù¼‡úýõ‰—êâÓ»ä¾ÙÙ9ÐÙ•°‹ÁÈy(282.ƒÚæË›Õ²“^âÓ»ä¾ÙÙ9ÐÙ•°‹ÁÈy(2 -82/òìšÀ·ÄŒÃ#óª·Žð–äÑŠÐÙ•°‹ÁÈy(2 -82+¢ØþËÄÝßíyóª·Žð–äÑŠÐÙ•°‹ÁÈy(282"йʢäÞ†#½³ úùš±KÐÙ•°‹ÁÈy(82I¾Œ…û±ªÓþν³ úùš±KŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282HÞûÅŠëŽî½³ úùš±KǮ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282HÚÇÍý›ôÍô_½³ úùš±Kÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282Gá”ܰ§¼Üï{½³ úùš±K˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282IŒú’ìÌšŒÿ½³ úùš±Kì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282HðÒ¬¼ïˆâ½³ úùš±Kôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282Hˆ¼ÂÁŒ‚ªŠE½³ úùš±K‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(282 ðÚžºÖèà몖¨Œ×ÿ¹¡k(282"…ïèÙŸåóþÎûÄߪæþªâÚ(282!ÆÉ¨‡âž¯‰÷‘–ä–Œ¿¯(282"Õ™õê×Ì·›‹Ïä†÷ɯ(282!Þíó¾ó¿ÚªˆëôùÇ(282!ž¥ÌÔѾۛL½šòÐ䤿Œ(282!„±ÂææÄÁOé’¬¸ç¼‡Óª(282+¦‚ýª¬¢˜þǪ–¨Œ×ÿ¹¡kÐÙ•°‹ÁÈy(282+Ó®¿Œïɳ=ûÄߪæþªâÚÐÙ•°‹ÁÈy(282+ØŽ„Øø¨²c÷‘–ä–Œ¿¯ÐÙ•°‹ÁÈy(282,ýŒ®ê£ÀËŠÏä†÷ɯÐÙ•°‹ÁÈy(282+’ÐÝç’묩uŸ¡ªˆëôùÇÐÙ•°‹ÁÈy(282,ÌÅ„„ŒúÔÞ ½šòÐ䤿ŒÐÙ•°‹ÁÈy(282,ÿªØ¦ƒ®èý»é’¬¸ç¼‡ÓªÐÙ•°‹ÁÈy(282#©Ô†«¹ÛÈÊœä”Û›ÅËèÄÐÙ•°‹ÁÈy(82,‡¼ã™“ЧӜä”Û›ÅËèÄÐÙ•°‹ÁÈy(282#ëÜÄêÖª©ÞùÃýب°Ï=ÐÙ•°‹ÁÈy(82I¬Í˜ÝïÚÈéùÃýب°Ï=ŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282Hã;ܲÍË®øùÃýب°Ï=Ǯ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282IÌ™¹ý¢ÞúŒ…ùÃýب°Ï=ÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282GÚ´¯Ðèà £YùÃýب°Ï=˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282IƒÈæÑ€¸µ§µùÃýب°Ï=ì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282Gׇã ÉÈùÃýب°Ï=ôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282HÁµÏ›é¥¸ãUùÃýب°Ï=‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(282"§±¿Œ·¾íé„ò݃‰…Ï™sÐÙ•°‹ÁÈy(82I®‰Å„ÿ··„ò݃‰…Ï™sŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282H°ªåÛòÿ™˜„ò݃‰…Ï™sǮ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282HÖïˆÇ÷ðî’ -„ò݃‰…Ï™sÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282Gæü•о°‹„ò݃‰…Ï™s˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282HáŒêË ûÜÝ„ò݃‰…Ï™sì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282GµüŒºÊ¥„ò݃‰…Ï™sôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282HÏææŸÞñ˜È'„ò݃‰…Ï™s‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(282#䪧¸ëð»ÍÖ“®ß®þÏ¿ÐÙ•°‹ÁÈy(82HâÈòŸ‚Ϫ¶;“®ß®þÏ¿ŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282GÅöôŽåËà@“®ß®þϿǮ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282H±¤ñþÜÀ«‡-“®ß®þÏ¿ÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282GЇ¶ÇéíËI“®ß®þÏ¿˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282H¶ÛŽˆÊÜÇÔ -“®ß®þÏ¿ì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282Gñå°ì’³¯Ç“®ß®þÏ¿ôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282H¥×©ë¹ÖÕÅb“®ß®þÏ¿‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(282#¼ù´–ºîª¡Ÿ¤øÝŽ£î“KÐÙ•°‹ÁÈy(82H…üÖ½ßÇЃqŸ¤øÝŽ£î“KŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282GÀê÷ÅÒ×´øŸ¤øÝŽ£î“KǮ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282Iô ¨øÍ¹€Ÿ¤øÝŽ£î“Kÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282HÍÆýö®£ÏåËŸ¤øÝŽ£î“K˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282H¿¾Š “ʽ៤øÝŽ£î“Kì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282GºÉ¬¦“ÆçÜpŸ¤øÝŽ£î“Kôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282H•úŸþî¯ø6Ÿ¤øÝŽ£î“K‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(28:â鿨ŒÙœ®‘*:ÔØ’¯—꓌’ („:ðÿÈ€ÀíÐð© (¬:ì¹ó¢â¯¢ÚÌ (Ü:ôïÆ þ©ƒ„ø (À:õµ…çÈηŽB (¤
:¸Î½Èìù„ (:¶›èõÖð®÷m (è:¨ÆºÀõµÈï¹ (” -:ãï©Ê½˜š¡® (¼:¥Ã¯œí…â¥w (ø -:å½ÈÂÏò“Þó (ˆ:¶Æèܦ™¥ìß( -( -( -( -( -B.ª ÿçû¤éåˆ!ÂŒÝëð ¢Ý›ºô¿Úó¾k2 -B"󄽾؜ߞ}™ºŽÔ–í”"žÈ¶ž•ÞŠè( -B-âÓ»ä¾ÙÙ9!µêЊîÄa鮣öü©µ2 -B,·æúŸÌ½‚¡ûó⿼‘ÿÿƒ«ŸˆÇŽ¿¡ 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 60948a181a1b..000000000000 --- 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 b6b16e40a4b2..000000000000 --- 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 4f4dd011e419..000000000000 --- 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 bf87fc51dce1..000000000000 --- 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 2bf8ca95d846..000000000000 --- 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 Binary files differdeleted file mode 100644 index 55621cc1074f..000000000000 --- a/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 11ec2068be19..000000000000 --- a/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 7c02b784aa5d..000000000000 --- a/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 915d91441349..000000000000 --- a/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null 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 d6f804734385..000000000000 --- 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 b03da06f7a77..000000000000 --- 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 Binary files differdeleted file mode 100755 index 24221908cbeb..000000000000 --- a/cmds/statsd/tools/loadtest/res/raw/loadtest_config +++ /dev/null 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 c2407d3b85f2..000000000000 --- 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 e8ae3f82a7e3..000000000000 --- 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 3c93a0613183..000000000000 --- 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 bab0c1e3f540..000000000000 --- 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 203d97acefd8..000000000000 --- 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 2e0161be8096..000000000000 --- 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 d55f3f31fd9f..000000000000 --- 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 769f78c726e8..000000000000 --- 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 01eebf2ad1cf..000000000000 --- 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 af7bd4d35966..000000000000 --- 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 555e6dd2d99d..000000000000 --- 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 7a01adedfaa4..000000000000 --- 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 8613ac1c4796..000000000000 --- 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 e000918fa0f7..000000000000 --- 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 5dcce9acb401..000000000000 --- 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 3939e7e0b2fa..000000000000 --- 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 d9f0ca9d2461..000000000000 --- 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/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java index de6e8857c1e5..2ed2678bc877 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 class Svc { 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/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java deleted file mode 100644 index e31cb5381afc..000000000000 --- 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 7431aea4dc14..95265e817c1b 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 @@ fi export CLASSPATH=/system/framework/svc.jar exec app_process /system/bin com.android.commands.svc.Svc "$@" + 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 455e4bbc0b76..b23bf5da5c8d 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 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge { 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 }, |