diff options
Diffstat (limited to 'cmds')
164 files changed, 4838 insertions, 8193 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..e1cb7ca6a697 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(); 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/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..e643ab567644 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) { 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..1a0d4438f1b3 100644 --- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h +++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h @@ -29,11 +29,12 @@ 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; + void visit(const IdmapData::TypeEntry& type_entry) override; private: void Write16(uint16_t value); diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index ebbb5ffc989d..f2cae58b910a 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -56,20 +56,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 @@ -155,7 +149,7 @@ class IdmapData { PackageId target_package_id_; uint16_t type_count_; - friend Idmap; + friend IdmapData; DISALLOW_COPY_AND_ASSIGN(Header); }; @@ -194,12 +188,15 @@ class IdmapData { uint16_t entry_offset_; std::vector<EntryId> entries_; - friend Idmap; + friend IdmapData; DISALLOW_COPY_AND_ASSIGN(TypeEntry); }; 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_; } @@ -232,9 +229,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); diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h index 5111bb2eaab2..f0f141a3757c 100644 --- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h @@ -33,11 +33,12 @@ 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; + void visit(const IdmapData::TypeEntry& type_entry) override; private: std::ostream& stream_; diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h index 2e543d4fabdd..cd3897109a32 100644 --- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h @@ -34,11 +34,12 @@ 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; + void visit(const IdmapData::TypeEntry& type_entry) override; private: void print(uint16_t value, const char* fmt, ...); diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h new file mode 100644 index 000000000000..c3e1ef06c20f --- /dev/null +++ b/cmds/idmap2/include/idmap2/ResourceMapping.h @@ -0,0 +1,132 @@ +/* + * 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/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); + + // 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); + + // 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); + + 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..abc2df1a147f 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -21,17 +21,28 @@ #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 { 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 requiredSystemPropertyValue; // 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) }; @@ -41,6 +52,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/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 4649675965db..389ade59200c 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,10 +42,6 @@ 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) { @@ -97,28 +94,6 @@ 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; - } - int id = packages[0]->GetPackageId(); - return loaded_arsc.GetPackageById(id); -} - Result<uint32_t> GetCrc(const ZipFile& zip) { const Result<uint32_t> a = zip.Crc("resources.arsc"); const Result<uint32_t> b = zip.Crc("AndroidManifest.xml"); @@ -266,95 +241,57 @@ 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"); - } - - 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"); - } + MatchingResources matching_resources; + for (const auto mapping : resource_mapping.GetTargetToOverlayMap()) { + if (mapping.second.data_type != Res_value::TYPE_REFERENCE) { + // The idmap format must change to support non-references. + continue; + } - 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()); + matching_resources.Add(mapping.first, mapping.second.data_value); } - // 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()); + // 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)); } - return Result<Unit>({}); + std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header()); + data_header->target_package_id_ = resource_mapping.GetTargetPackageId(); + data_header->type_count_ = data->type_entries_.size(); + 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 +303,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,7 +327,7 @@ 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_)); @@ -404,70 +336,24 @@ Result<std::unique_ptr<const Idmap>> Idmap::FromApkAssets(const std::string& tar 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()); + auto resource_mapping = + ResourceMapping::FromApkAssets(target_apk_assets, overlay_apk_assets, *overlay_info, + fulfilled_policies, enforce_overlayable); + 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)); - + idmap->data_.push_back(std::move(*idmap_data)); return {std::move(idmap)}; } diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp new file mode 100644 index 000000000000..95ae626664dd --- /dev/null +++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp @@ -0,0 +1,411 @@ +/* + * 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 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) { + 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 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(WARNING) << "failed to find resource \"" << *target_resource << "\" in target resources"; + continue; + } + + 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_package_id == EXTRACT_PACKAGE(overlay_resource->data) + : false; + + 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 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()); + const ResourceId target_resource = target_am->GetResourceId(full_name); + if (target_resource == 0U) { + continue; + } + + resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid, + /* rewrite_overlay_reference */ true); + } + + 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) { + 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(WARNING) << "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) { + 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)); + } 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); + } + + 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) { + 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); + + // Remove rewriting of overlay resource id to target resource id. + if (value.data_type != Res_value::TYPE_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..9d3269207c91 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -22,18 +22,18 @@ #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) { +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 +65,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()); } - iter = tag->find("targetName"); - if (iter != tag->end()) { - info.target_name = iter->second; + if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) { + info.target_name = *result_str; } - iter = tag->find("isStatic"); - if (iter != tag->end()) { - info.is_static = std::stoul(iter->second) != 0U; + if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) { + if ((*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("priority"); - if (iter != tag->end()) { - info.priority = std::stoi(iter->second); + if (auto result_value = overlay_it->GetAttributeValue("isStatic")) { + if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT && + (*result_value).dataType <= Res_value::TYPE_LAST_INT) { + info.is_static = (*result_value).data != 0U; + } else { + return Error("android:isStatic is not a boolean in AndroidManifest.xml of %s", path.c_str()); + } + } + + 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..43fdc9a78186 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -75,9 +75,8 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) { 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/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 0f47f1e77d7b..47e5b17f4a98 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -179,8 +179,7 @@ void CreateIdmap(const StringPiece& target_apk_path, const StringPiece& overlay_ 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); + Idmap::FromApkAssets(*target_apk, *overlay_apk, fulfilled_policies, enforce_overlayable); *out_idmap = result ? std::move(*result) : nullptr; } @@ -195,7 +194,7 @@ TEST(IdmapTests, CreateIdmapFromApkAssets) { ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x76a20829); - ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x8635c2ed); + ASSERT_EQ(idmap->GetHeader()->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); @@ -480,9 +479,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 +495,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); 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..c243d745e568 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -38,9 +38,8 @@ 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; @@ -50,7 +49,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { ASSERT_NE(stream.str().find("00000000: 504d4449 magic\n"), std::string::npos); ASSERT_NE(stream.str().find("00000004: 00000001 version\n"), std::string::npos); ASSERT_NE(stream.str().find("00000008: 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("0000000c: c054fb26 overlay crc\n"), std::string::npos); ASSERT_NE(stream.str().find("0000021c: 00000000 0x7f010000 -> 0x7f010000 integer/int1\n"), std::string::npos); } diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp new file mode 100644 index 000000000000..1ef41de4410d --- /dev/null +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -0,0 +1,322 @@ +/* + * 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/ResourceMapping.h" + +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()); + } + + return ResourceMapping::FromApkAssets(*target_apk, *overlay_apk, overlay_info, fulfilled_policies, + enforce_overlayable); +} + +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, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000, + true /* rewrite */)); // integer/int1 + ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000, + true /* rewrite */)); // string/str1 + ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001, + true /* rewrite */)); // string/str3 + ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002, + true /* 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, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002, + true /* rewrite */)); // string/str1 -> string/str4 + ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000, + true /* rewrite */)); // string/str3 -> string/str1 + ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001, + true /* rewrite */)); // string/str4 -> string/str3 +} + +TEST(ResourceMappingTests, DoNotRewriteNonResourceMapping) { + 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, 0x01 /* Res_value::TYPE_REFERENCE */, 0x0104000a, + false /* rewrite */)); // string/str1 -> android:string/ok + ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_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, 0x03 /* Res_value::TYPE_STRING */, + overlay_string_pool_size + 0U, + false /* rewrite */)); // string/str1 -> "Hello World" + ASSERT_RESULT(MappingExists(res, 0x7f010000, 0x10 /* 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, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000, + true /* rewrite */)); // string/policy_public + ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001, + true /* rewrite */)); // string/policy_system + ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002, + true /* 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, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005, + true /* rewrite */)); // string/policy_public + ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007, + true /* rewrite */)); // string/policy_system + ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008, + true /* 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, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000, + true /* rewrite */)); // string/not_overlayable + ASSERT_RESULT(MappingExists(res, 0x7f020004, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001, + true /* rewrite */)); // string/other + ASSERT_RESULT(MappingExists(res, 0x7f020005, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002, + true /* rewrite */)); // string/policy_odm + ASSERT_RESULT(MappingExists(res, 0x7f020006, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010003, + true /* rewrite */)); // string/policy_oem + ASSERT_RESULT(MappingExists(res, 0x7f020007, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010004, + true /* rewrite */)); // string/policy_product + ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005, + true /* rewrite */)); // string/policy_public + ASSERT_RESULT(MappingExists(res, 0x7f020009, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010006, + true /* rewrite */)); // string/policy_signature + ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007, + true /* rewrite */)); // string/policy_system + ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008, + true /* 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, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000, + true /* rewrite */)); // integer/int1 + ASSERT_RESULT(MappingExists(res, 0x7f02000c, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020000, + true /* rewrite */)); // string/str1 + ASSERT_RESULT(MappingExists(res, 0x7f02000e, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020001, + true /* rewrite */)); // string/str3 + ASSERT_RESULT(MappingExists(res, 0x7f02000f, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f020002, + true /* 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, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010000, + true /* rewrite */)); // string/not_overlayable + ASSERT_RESULT(MappingExists(res, 0x7f020004, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010001, + true /* rewrite */)); // string/other + ASSERT_RESULT(MappingExists(res, 0x7f020005, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010002, + true /* rewrite */)); // string/policy_odm + ASSERT_RESULT(MappingExists(res, 0x7f020006, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010003, + true /* rewrite */)); // string/policy_oem + ASSERT_RESULT(MappingExists(res, 0x7f020007, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010004, + true /* rewrite */)); // string/policy_product + ASSERT_RESULT(MappingExists(res, 0x7f020008, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010005, + true /* rewrite */)); // string/policy_public + ASSERT_RESULT(MappingExists(res, 0x7f020009, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010006, + true /* rewrite */)); // string/policy_signature + ASSERT_RESULT(MappingExists(res, 0x7f02000a, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010007, + true /* rewrite */)); // string/policy_system + ASSERT_RESULT(MappingExists(res, 0x7f02000b, 0x01 /* Res_value::TYPE_REFERENCE */, 0x7f010008, + true /* 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..a7c2f284e7c5 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -123,7 +123,7 @@ 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 +136,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/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/statsd/Android.bp b/cmds/statsd/Android.bp index 8944dedd6c96..8af925aa7a63 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -51,11 +51,6 @@ cc_defaults { srcs: [ ":statsd_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", @@ -63,51 +58,59 @@ 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/GpuStatsPuller.cpp", "src/external/Perfetto.cpp", - "src/external/StatsPuller.cpp", + "src/external/PowerStatsPuller.cpp", + "src/external/puller_util.cpp", + "src/external/ResourceHealthManagerPuller.cpp", "src/external/StatsCallbackPuller.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/SurfaceflingerStatsPuller.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", ], local_include_dirs: [ @@ -120,26 +123,25 @@ cc_defaults { ], shared_libs: [ + "android.frameworks.stats@1.0", + "android.hardware.health@2.0", + "android.hardware.power.stats@1.0", + "android.hardware.power@1.0", + "android.hardware.power@1.1", "libbase", "libbinder", + "libcutils", "libgraphicsenv", + "libhidlbase", "libincident", "liblog", - "libutils", - "libservices", "libprotoutil", + "libservices", "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", + "libstatssocket", "libsysutils", - "libcutils", + "libtimestats_proto", + "libutils", ], } @@ -213,57 +215,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/StatsPuller_test.cpp", + "tests/external/SurfaceflingerStatsPuller_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: [ @@ -293,17 +295,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: { @@ -343,11 +345,11 @@ java_library { }, 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/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index 13f5c8ae5fd8..84a06070e431 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) { @@ -464,4 +461,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..0e033e06c203 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -392,6 +392,7 @@ 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); diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index af8b3af6ea61..5e156bb26caa 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -59,6 +59,16 @@ android::hash_t hashDimension(const HashableDimensionKey& value) { return JenkinsHashWhiten(hash); } +bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values, Value* output) { + for (const auto& value : values) { + if (value.mField.matches(matcherField)) { + (*output) = value.mValue; + return true; + } + } + return false; +} + bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values, HashableDimensionKey* output) { size_t num_matches = 0; diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index 6f4941f717ee..a12385057585 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -120,6 +120,13 @@ class MetricDimensionKey { 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, + Value* output); + +/** * Creating HashableDimensionKeys from FieldValues using matcher. * * This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL @@ -169,4 +176,4 @@ struct hash<MetricDimensionKey> { 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 ff7416c4b9e0..6c3dff24e5fe 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -16,24 +16,26 @@ #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 "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; @@ -218,6 +220,8 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { onIsolatedUidChangedEventLocked(*event); } + StateManager::getInstance().onLogEvent(*event); + if (mMetricsManagers.empty()) { return; } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index f0db1b0128a1..8292a3a9194a 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -248,19 +248,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,6 +262,10 @@ private: FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); + FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState); + FRIEND_TEST(CountMetricE2eTest, TestWithMappedState); + FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates); + FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 1c7180ffbde1..b665a8b99fbd 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -266,7 +266,9 @@ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* rep IResultReceiver::asInterface(data.readStrongBinder()); err = command(in, out, err, args, resultReceiver); - resultReceiver->send(err); + if (resultReceiver != nullptr) { + resultReceiver->send(err); + } return NO_ERROR; } default: { return BnStatsManager::onTransact(code, data, reply, flags); } @@ -411,13 +413,20 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args, return cmd_trigger_active_config_broadcast(out, args); } if (!args[0].compare(String8("data-subscribe"))) { - if (mShellSubscriber == nullptr) { - mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); + { + std::lock_guard<std::mutex> lock(mShellSubscriberMutex); + if (mShellSubscriber == nullptr) { + mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); + } } int timeoutSec = -1; if (argCount >= 2) { timeoutSec = atoi(args[1].c_str()); } + if (resultReceiver == nullptr) { + ALOGI("Null resultReceiver given, no subscription will be started"); + return UNEXPECTED_NULL; + } mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec); return NO_ERROR; } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 53b6ce989195..949094871936 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -432,6 +432,10 @@ private: sp<ShellSubscriber> mShellSubscriber; + /** + * Mutex for setting the shell subscriber + */ + mutable mutex mShellSubscriberMutex; std::shared_ptr<LogEventQueue> mEventQueue; FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 32d90b70c34e..6249de3d3bac 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -56,6 +56,8 @@ 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 @@ -295,6 +297,7 @@ 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"]; @@ -319,15 +322,25 @@ message Atom { 217 [(log_from_module) = "permissioncontroller"]; PermissionAppsFragmentViewed permission_apps_fragment_viewed = 218 [(log_from_module) = "permissioncontroller"]; + TextSelectionEvent text_selection_event = + 219 [(log_from_module) = "textclassifier"]; + TextLinkifyEvent text_linkify_event = + 220 [(log_from_module) = "textclassifier"]; + ConversationActionsEvent conversation_actions_event = + 221 [(log_from_module) = "textclassifier"]; + LanguageDetectionEvent language_detection_event = + 222 [(log_from_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]; } // Pulled events will start at field 10000. - // Next: 10062 + // Next: 10065 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001; @@ -365,7 +378,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; @@ -391,11 +403,16 @@ 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; } // 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; } /** @@ -2605,12 +2622,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; @@ -3052,9 +3071,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; @@ -3062,7 +3081,7 @@ message OverlayStateChanged { ENTERED = 1; EXITED = 2; } - optional State state = 4; + optional State state = 4 [(state_field_option).option = EXCLUSIVE]; } /* @@ -3955,7 +3974,7 @@ message ModemActivityInfo { // rx time in ms at power level 5 optional uint64 controller_rx_time_millis = 9; // product of current(mA), voltage(V) and time(ms) - optional uint64 energy_used = 10; + optional uint64 energy_used = 10 [deprecated=true]; } /** @@ -4001,8 +4020,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 @@ -4012,67 +4031,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 all managed processes (from ActivityManagerServie) + * and for selected native processes. * - * Pulled from StatsCompanionService 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 ProcessMemoryHighWaterMark atom instead. Always 0. - optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true]; + // 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]; - // 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]; @@ -4081,9 +4085,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; } /* @@ -6196,6 +6220,41 @@ message MediametricsNuPlayerReported { } /** + * 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 */ message DangerousPermissionState { @@ -6404,6 +6463,9 @@ 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; } /* @@ -6507,8 +6569,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; } /** @@ -6824,6 +6892,317 @@ 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 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 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 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; +} + +/** + * 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 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; +} + +/** + * 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: diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 60a4b236df11..52a1269798ca 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -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/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp index 0d3aca05e0e5..d38b87f046e0 100644 --- a/cmds/statsd/src/external/GpuStatsPuller.cpp +++ b/cmds/statsd/src/external/GpuStatsPuller.cpp @@ -92,10 +92,17 @@ 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; event->init(); data->emplace_back(event); } diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 69e6a11b9ad7..5a76d1f9c80d 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -17,12 +17,17 @@ #define DEBUG false #include "Log.h" +#include "StatsPullerManager.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,13 +37,11 @@ #include "ResourceHealthManagerPuller.h" #include "StatsCallbackPuller.h" #include "StatsCompanionServicePuller.h" -#include "StatsPullerManager.h" #include "SubsystemSleepStatePuller.h" +#include "SurfaceflingerStatsPuller.h" #include "TrainInfoPuller.h" #include "statslog.h" -#include <iostream> - using std::make_shared; using std::map; using std::shared_ptr; @@ -143,17 +146,15 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}}, // process_memory_state {android::util::PROCESS_MEMORY_STATE, - {.additiveFields = {4, 5, 6, 7, 8, 9}, + {.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 = + {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}}, + // process_memory_snapshot + {android::util::PROCESS_MEMORY_SNAPSHOT, + {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}}, // system_ion_heap_size {android::util::SYSTEM_ION_HEAP_SIZE, {.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}}, @@ -267,6 +268,10 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // App ops {android::util::APP_OPS, {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}}, + // SurfaceflingerStatsGlobalInfo + {android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, + {.puller = + new SurfaceflingerStatsPuller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp b/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp new file mode 100644 index 000000000000..23b2236f35f2 --- /dev/null +++ b/cmds/statsd/src/external/SurfaceflingerStatsPuller.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SurfaceflingerStatsPuller.h" + +#include <cutils/compiler.h> + +#include <numeric> + +#include "logd/LogEvent.h" +#include "stats_log_util.h" +#include "statslog.h" + +namespace android { +namespace os { +namespace statsd { + +SurfaceflingerStatsPuller::SurfaceflingerStatsPuller(const int tagId) : StatsPuller(tagId) { +} + +bool SurfaceflingerStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) { + switch (mTagId) { + case android::util::SURFACEFLINGER_STATS_GLOBAL_INFO: + return pullGlobalInfo(data); + default: + break; + } + + return false; +} + +static int64_t getTotalTime( + const google::protobuf::RepeatedPtrField<surfaceflinger::SFTimeStatsHistogramBucketProto>& + buckets) { + int64_t total = 0; + for (const auto& bucket : buckets) { + if (bucket.time_millis() == 1000) { + continue; + } + + total += bucket.time_millis() * bucket.frame_count(); + } + + return total; +} + +bool SurfaceflingerStatsPuller::pullGlobalInfo(std::vector<std::shared_ptr<LogEvent>>* data) { + std::string protoBytes; + if (CC_UNLIKELY(mStatsProvider)) { + protoBytes = mStatsProvider(); + } else { + std::unique_ptr<FILE, decltype(&pclose)> pipe(popen("dumpsys SurfaceFlinger --timestats -dump --proto", "r"), pclose); + if (!pipe.get()) { + return false; + } + char buf[1024]; + size_t bytesRead = 0; + do { + bytesRead = fread(buf, 1, sizeof(buf), pipe.get()); + protoBytes.append(buf, bytesRead); + } while (bytesRead > 0); + } + surfaceflinger::SFTimeStatsGlobalProto proto; + proto.ParseFromString(protoBytes); + + int64_t totalTime = getTotalTime(proto.present_to_present()); + + data->clear(); + data->reserve(1); + std::shared_ptr<LogEvent> event = + make_shared<LogEvent>(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, getWallClockNs(), + getElapsedRealtimeNs()); + if (!event->write(proto.total_frames())) return false; + if (!event->write(proto.missed_frames())) return false; + if (!event->write(proto.client_composition_frames())) return false; + if (!event->write(proto.display_on_time())) return false; + if (!event->write(totalTime)) return false; + event->init(); + data->emplace_back(event); + + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/SurfaceflingerStatsPuller.h b/cmds/statsd/src/external/SurfaceflingerStatsPuller.h new file mode 100644 index 000000000000..ed7153edf797 --- /dev/null +++ b/cmds/statsd/src/external/SurfaceflingerStatsPuller.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <timestatsproto/TimeStatsProtoHeader.h> + +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +/** + * Pull metrics from Surfaceflinger + */ +class SurfaceflingerStatsPuller : public StatsPuller { +public: + explicit SurfaceflingerStatsPuller(const int tagId); + + // StatsPuller interface + bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override; + +protected: + // Test-only, for injecting fake data + using StatsProvider = std::function<std::string()>; + StatsProvider mStatsProvider; + +private: + bool pullGlobalInfo(std::vector<std::shared_ptr<LogEvent>>* data); +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 0ade53118d77..fd19c9d61f87 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -332,6 +332,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, diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 531ce299beef..f1f45a2d3bc7 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -21,9 +21,9 @@ #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 <utils/Errors.h> #include <string> @@ -157,6 +157,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, 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..4a06387e357f 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,7 +51,6 @@ 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 @@ -65,11 +67,16 @@ 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 +89,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 +102,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric mConditionSliced = true; } - mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0); + // TODO(tsaichristine): b/142124705 handle metric state links flushIfNeededLocked(startTimeNs); // Adjust start for partial bucket @@ -114,6 +116,12 @@ CountMetricProducer::~CountMetricProducer() { VLOG("~CountMetricProducer() called"); } +void CountMetricProducer::onStateChanged(int atomId, const HashableDimensionKey& primaryKey, + int oldState, int newState) { + VLOG("CountMetric %lld onStateChanged State%d, key %s, %d -> %d", (long long)mMetricId, atomId, + primaryKey.toString().c_str(), oldState, newState); +} + void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { if (mCurrentSlicedCounter == nullptr || mCurrentSlicedCounter->size() == 0) { @@ -171,13 +179,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); @@ -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; } } diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index b4a910c6f410..61e0892d50a9 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,12 +41,20 @@ 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(int atomId, const HashableDimensionKey& primaryKey, int oldState, + int newState) override; + protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, @@ -102,6 +111,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..ab2a1c3bd65c 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,7 +48,6 @@ 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; @@ -63,14 +63,17 @@ 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 +103,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 +113,16 @@ 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 +159,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 +173,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; } @@ -249,178 +242,20 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio } } - -// 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); - } - } - } - } - } - } - } -} - 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); - } - } - } - } - } } void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, @@ -526,12 +361,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); @@ -666,6 +495,7 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { ALOGE("DurationMetric %lld dropping data for condition dimension key %s", (long long)mMetricId, newKey.getDimensionKeyInCondition().toString().c_str()); + StatsdStats::getInstance().noteHardDimensionLimitReached(mMetricId); return true; } } @@ -679,6 +509,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; } } @@ -790,52 +621,24 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, 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..7457d7fb2dd9 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(); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 96133bd0a38d..32eb077e1cf4 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; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 74e6bc845c04..dca37e8790a4 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -33,9 +33,14 @@ 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(); diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index a64bbc1056e0..d0f88a867da7 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -48,7 +48,6 @@ 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; @@ -73,8 +72,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 +119,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 +127,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 +138,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, @@ -209,12 +211,6 @@ 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); @@ -307,11 +303,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 +442,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; } } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index a612adf8a38b..640a02a9a8ab 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(); @@ -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; diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 92752b29ecda..2a700efe3d37 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -40,6 +40,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 +79,24 @@ 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); - } - 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); - } + onMatchedLogEventInternalLocked( + matcherIndex, metricKey, conditionKey, condition, event); } bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) { @@ -111,24 +124,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()) { diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 09ad2903fa4c..a72de22bd433 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -17,19 +17,19 @@ #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" namespace android { namespace os { @@ -69,29 +69,32 @@ enum DumpLatency { NO_TIME_CONSTRAINTS = 2 }; +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; +}; + // 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 PackageInfoListener, 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(){}; @@ -150,6 +153,9 @@ public: return mConditionSliced; }; + void onStateChanged(int 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 +174,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 +181,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 +195,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 +215,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 +259,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 +271,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) = 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,8 +340,6 @@ protected: return (endNs - mTimeBaseNs) / mBucketSizeNs - 1; } - virtual void dropDataLocked(const int64_t dropTimeNs) = 0; - const int64_t mMetricId; const ConfigKey mConfigKey; @@ -342,21 +360,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 +380,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 +391,16 @@ protected: bool mIsActive; + // The slice_by_state atom ids defined in statsd_config. + std::vector<int> mSlicedStateAtoms; + + // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>). + std::unordered_map<int, std::unordered_map<int, int64_t>> mStateGroupMap; + + FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState); + FRIEND_TEST(CountMetricE2eTest, TestWithMappedState); + FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates); + FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 963205ee56f4..7bae4b929b48 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 "condition/CombinationConditionTracker.h" @@ -25,12 +29,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; @@ -149,6 +151,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()"); } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 8efca1e10de5..d184121f5ba0 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -260,17 +260,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,6 +282,10 @@ private: TestActivationOnBootMultipleActivationsDifferentActivationTypes); FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); + FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState); + FRIEND_TEST(CountMetricE2eTest, TestWithMappedState); + FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates); + FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 0e33a0f9f29b..6fd03273a434 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -51,7 +51,6 @@ 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; @@ -82,8 +81,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 +113,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()) { @@ -127,10 +131,6 @@ ValueMetricProducer::ValueMetricProducer( 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; @@ -139,11 +139,10 @@ 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()); + mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()); int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs); mCurrentBucketNum += numBucketsForward; @@ -160,6 +159,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, mCondition); + } + // 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,16 +179,6 @@ 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); - } - // Now that activations are processed, start the condition timer if needed. - mConditionTimer.onConditionChanged(mIsActive && mCondition == ConditionState::kTrue, - mCurrentBucketStartTimeNs); -} - void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) { VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); @@ -243,12 +241,6 @@ 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); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 739f6ef07cc4..206e602dd1c7 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(); @@ -116,8 +121,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. diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 081e61ed21fa..6b5c2994a0c8 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), @@ -180,8 +179,6 @@ protected: const int64_t mBucketSizeNs; - const std::vector<Matcher>& mDimensionInCondition; - const bool mNested; int64_t mCurrentBucketStartTimeNs; @@ -196,7 +193,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..df66cb0c53e5 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -27,18 +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) { @@ -252,17 +248,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..d0371da096ea 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, diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index 956383a99eea..b0fd975b2328 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -26,20 +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) { @@ -227,17 +223,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 +251,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..43c48d5f536b 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, diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 5cfb1239d30e..33e162ec2d24 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -18,25 +18,26 @@ #include "Log.h" #include "metrics_manager_util.h" +#include "MetricProducer.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 "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 "statslog.h" -#include <inttypes.h> - using std::set; using std::string; using std::unordered_map; @@ -137,6 +138,97 @@ 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; +} + +// 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 +276,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 +334,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 +377,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 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 +453,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 +465,29 @@ 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; + } + } + + // TODO(tsaichristine): add check for unequal number of MetricStateLinks + // and slice_by_states + + 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 +555,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 +601,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 +667,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> valueProducer = new ValueMetricProducer( key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId, - timeBaseTimeNs, currentTimeNs, pullerManager); + timeBaseTimeNs, currentTimeNs, pullerManager, eventActivationMap, + eventDeactivationMap); allMetricProducers.push_back(valueProducer); } @@ -586,10 +762,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) { @@ -602,6 +787,13 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } 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 +899,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 +919,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 +933,16 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& ALOGE("initConditionTrackers failed"); return false; } - + if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) { + ALOGE("initStates failed"); + return false; + } if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap, - conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers, + conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps, + allConditionTrackers, allMetricProducers, conditionToMetricMap, trackerToMetricMap, metricProducerMap, - noReportMetricIds)) { + noReportMetricIds, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation)) { ALOGE("initMetricProducers failed"); return false; } @@ -824,14 +956,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/state/StateListener.h b/cmds/statsd/src/state/StateListener.h new file mode 100644 index 000000000000..a31690a102ed --- /dev/null +++ b/cmds/statsd/src/state/StateListener.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#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. + * + * [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(int 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..95b2c7691803 --- /dev/null +++ b/cmds/statsd/src/state/StateManager.cpp @@ -0,0 +1,91 @@ +/* + * 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::onLogEvent(const LogEvent& event) { + std::lock_guard<std::mutex> lock(mMutex); + if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) { + mStateTrackers[event.GetTagId()]->onLogEvent(event); + } +} + +bool StateManager::registerListener(int atomId, wp<StateListener> listener) { + std::lock_guard<std::mutex> lock(mMutex); + + // 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(int 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(); +} + +int StateManager::getStateValue(int atomId, const HashableDimensionKey& key) { + std::lock_guard<std::mutex> lock(mMutex); + if (mStateTrackers.find(atomId) != mStateTrackers.end()) { + return mStateTrackers[atomId]->getStateValue(key); + } + + return StateTracker::kStateUnknown; +} + +} // 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..89ee6c04b922 --- /dev/null +++ b/cmds/statsd/src/state/StateManager.h @@ -0,0 +1,78 @@ +/* + * 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 { + +class StateManager : public virtual RefBase { +public: + StateManager(){}; + + ~StateManager(){}; + + // Returns a pointer to the single, shared StateManager object. + static StateManager& getInstance(); + + // 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(int 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(int atomId, wp<StateListener> listener); + + // Queries the correct StateTracker for the original/un-mapped state value + // that is mapped to the given query key. + // If the StateTracker doesn't exist, returns StateTracker::kStateUnknown. + int getStateValue(int atomId, const HashableDimensionKey& queryKey); + + inline int getStateTrackersCount() { + std::lock_guard<std::mutex> lock(mMutex); + return mStateTrackers.size(); + } + + inline int getListenersCount(int atomId) { + std::lock_guard<std::mutex> lock(mMutex); + if (mStateTrackers.find(atomId) != mStateTrackers.end()) { + return mStateTrackers[atomId]->getListenersCount(); + } + return -1; + } + +private: + mutable std::mutex mMutex; + + // Maps state atom ids to StateTrackers + std::unordered_map<int, 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..323fc0e94ab2 --- /dev/null +++ b/cmds/statsd/src/state/StateTracker.cpp @@ -0,0 +1,159 @@ +/* + * 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 int atomId, + const util::StateAtomFieldOptions& stateAtomInfo) + : mAtomId(atomId), + mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) { + // create matcher for each primary field + // TODO(tsaichristine): b/142108433 handle when primary field is first uid in chain + for (const auto& primary : stateAtomInfo.primaryFields) { + Matcher matcher = getSimpleMatcher(atomId, primary); + mPrimaryFields.push_back(matcher); + } + + // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting +} + +void StateTracker::onLogEvent(const LogEvent& event) { + // 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(); + return; + } + } else { + // atom has no primary fields + primaryKey = DEFAULT_DIMENSION_KEY; + } + + // parse event for state value + Value state; + int32_t stateValue; + if (!filterValues(mStateField, event.getValues(), &state) || state.getType() != INT) { + ALOGE("StateTracker error extracting state from log event. Type: %d", state.getType()); + handlePartialReset(primaryKey); + return; + } + stateValue = state.int_value; + + if (stateValue == mResetState) { + VLOG("StateTracker Reset state: %s", state.toString().c_str()); + handleReset(); + } + + // track and update state + int32_t oldState = 0; + int32_t newState = 0; + updateState(primaryKey, stateValue, &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(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); +} + +int StateTracker::getStateValue(const HashableDimensionKey& queryKey) const { + if (queryKey.getValues().size() == mPrimaryFields.size()) { + auto it = mStateMap.find(queryKey); + if (it != mStateMap.end()) { + return it->second.state; + } + } 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"); + } + return mDefaultState; +} + +void StateTracker::handleReset() { + VLOG("StateTracker handle reset"); + for (const auto pair : mStateMap) { + for (auto l : mListeners) { + auto sl = l.promote(); + if (sl != nullptr) { + sl->onStateChanged(mAtomId, pair.first, pair.second.state, mDefaultState); + } + } + } + mStateMap.clear(); +} + +void StateTracker::handlePartialReset(const HashableDimensionKey& primaryKey) { + VLOG("StateTracker handle partial reset"); + if (mStateMap.find(primaryKey) != mStateMap.end()) { + 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..cfa9fd8f6272 --- /dev/null +++ b/cmds/statsd/src/state/StateTracker.h @@ -0,0 +1,95 @@ +/* + * 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 <statslog.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 int 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); + + // Returns the state value mapped to the given query key. + // If the key isn't mapped to a state or the key size doesn't match the + // primary key size, the default state is returned. + int getStateValue(const HashableDimensionKey& queryKey) 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; + + // 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(); + + // Reset only the state value mapped to primary key to default state + void handlePartialReset(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..e45e24fe49d4 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,13 @@ message ValueBucketInfo { message ValueMetricData { optional DimensionsValue dimensions_in_what = 1; - optional DimensionsValue dimensions_in_condition = 2; + optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; repeated ValueBucketInfo 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 GaugeBucketInfo { @@ -166,13 +177,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 { @@ -220,7 +231,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 +374,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 +494,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..c22e3cc07a3b 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -445,6 +445,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/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 79c06b98a82d..c98b2cf4b472 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -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 int64 state = 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,7 +290,7 @@ message ValueMetric { optional FieldMatcher dimensions_in_what = 5; - optional FieldMatcher dimensions_in_condition = 9; + optional FieldMatcher dimensions_in_condition = 9 [deprecated = true]; optional TimeUnit bucket = 6; @@ -438,6 +469,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/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..0743480bf4ee 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -582,7 +582,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 +621,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; diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index fe25a257aa67..460b9e0995c8 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -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..6591d69cdc1a --- /dev/null +++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp @@ -0,0 +1,197 @@ +/* + * 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 "tests/statsd_test_util.h" + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ + +TEST(CountMetricE2eTest, TestWithSimpleState) { + // 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::ONE_MINUTE); + countMetric->add_slice_by_state(state.id()); + + // Initialize StatsLogProcessor + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + // Check that StateTrackers were properly initialized. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, + StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // 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), android::util::SCREEN_STATE_CHANGED); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); +} + +TEST(CountMetricE2eTest, TestWithMappedState) { + // 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::ONE_MINUTE); + countMetric->add_slice_by_state(state.id()); + + // Initialize StatsLogProcessor + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + // Check that StateTrackers were properly initialized. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, + StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // 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), android::util::SCREEN_STATE_CHANGED); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + + StateMap map = state.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value], + group.group_id()); + } + } +} + +TEST(CountMetricE2eTest, TestWithMultipleStates) { + // 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 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 + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(syncStartMatcher.id()); + countMetric->set_bucket(TimeUnit::ONE_MINUTE); + countMetric->add_slice_by_state(state1.id()); + countMetric->add_slice_by_state(state2.id()); + + // Initialize StatsLogProcessor + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + // Check that StateTrackers were properly initialized. + EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, + StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED)); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount( + android::util::UID_PROCESS_STATE_CHANGED)); + + // 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), android::util::SCREEN_STATE_CHANGED); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), android::util::UID_PROCESS_STATE_CHANGED); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + + StateMap map = state1.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value], + group.group_id()); + } + } +} + +} // 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/external/GpuStatsPuller_test.cpp b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp index bdc52b0af34c..e91fb0d4b27c 100644 --- a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp +++ b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp @@ -56,8 +56,9 @@ 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 size_t NUMBER_OF_VALUES_GLOBAL = 13; -static const size_t NUMBER_OF_VALUES_APP = 6; +static const size_t NUMBER_OF_VALUES_APP = 7; // clang-format on class MockGpuStatsPuller : public GpuStatsPuller { @@ -150,6 +151,7 @@ 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)); event->init(); inData.emplace_back(event); MockGpuStatsPuller mockPuller(android::util::GPU_STATS_APP_INFO, &inData); @@ -168,6 +170,7 @@ 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); } } // namespace statsd diff --git a/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp b/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp new file mode 100644 index 000000000000..5c9636fa99cc --- /dev/null +++ b/cmds/statsd/tests/external/SurfaceflingerStatsPuller_test.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "SurfaceflingerStatsPuller_test" + +#include "src/external/SurfaceflingerStatsPuller.h" + +#include <gtest/gtest.h> +#include <log/log.h> + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +class TestableSurfaceflingerStatsPuller : public SurfaceflingerStatsPuller { +public: + TestableSurfaceflingerStatsPuller(const int tagId) : SurfaceflingerStatsPuller(tagId){}; + + void injectStats(const StatsProvider& statsProvider) { + mStatsProvider = statsProvider; + } +}; + +class SurfaceflingerStatsPullerTest : public ::testing::Test { +public: + SurfaceflingerStatsPullerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + ~SurfaceflingerStatsPullerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } +}; + +TEST_F(SurfaceflingerStatsPullerTest, pullGlobalStats) { + surfaceflinger::SFTimeStatsGlobalProto proto; + proto.set_total_frames(1); + proto.set_missed_frames(2); + proto.set_client_composition_frames(2); + proto.set_display_on_time(4); + + auto bucketOne = proto.add_present_to_present(); + bucketOne->set_time_millis(2); + bucketOne->set_frame_count(4); + auto bucketTwo = proto.add_present_to_present(); + bucketTwo->set_time_millis(4); + bucketTwo->set_frame_count(1); + auto bucketThree = proto.add_present_to_present(); + bucketThree->set_time_millis(1000); + bucketThree->set_frame_count(1); + static constexpr int64_t expectedAnimationMillis = 12; + TestableSurfaceflingerStatsPuller puller(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); + + puller.injectStats([&] { + return proto.SerializeAsString(); + }); + puller.ForceClearCache(); + vector<std::shared_ptr<LogEvent>> outData; + puller.Pull(&outData); + + ASSERT_EQ(1, outData.size()); + EXPECT_EQ(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, outData[0]->GetTagId()); + EXPECT_EQ(proto.total_frames(), outData[0]->getValues()[0].mValue.long_value); + EXPECT_EQ(proto.missed_frames(), outData[0]->getValues()[1].mValue.long_value); + EXPECT_EQ(proto.client_composition_frames(), outData[0]->getValues()[2].mValue.long_value); + EXPECT_EQ(proto.display_on_time(), outData[0]->getValues()[3].mValue.long_value); + EXPECT_EQ(expectedAnimationMillis, outData[0]->getValues()[4].mValue.long_value); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 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..b027e8e687db 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -79,8 +79,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 +124,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 +207,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 +298,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 +364,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 +424,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 +477,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 +484,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 +513,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 +521,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 +563,6 @@ TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); Alert alert; alert.set_id(101); @@ -692,7 +671,6 @@ TEST(GaugeMetricProducerTest, TestPullOnTrigger) { logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; @@ -777,7 +755,6 @@ TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - gaugeProducer.prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; 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..4b9d0c0ca718 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -105,7 +105,6 @@ class ValueMetricProducerTestHelper { kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); return valueProducer; } @@ -125,7 +124,6 @@ class ValueMetricProducerTestHelper { new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); valueProducer->mCondition = ConditionState::kFalse; return valueProducer; } @@ -169,7 +167,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 +196,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); @@ -381,7 +377,6 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer->prepareFirstBucket(); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -670,7 +665,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); @@ -728,7 +722,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 +772,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 +846,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); @@ -897,7 +888,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 +962,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); @@ -1269,7 +1258,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 +1302,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 +1348,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 +1398,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 +1443,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); @@ -1532,7 +1516,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); @@ -2081,7 +2064,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. @@ -2862,7 +2844,6 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); ProtoOutputStream output; std::set<string> strSet; @@ -2905,7 +2886,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(); @@ -2969,7 +2949,6 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); - valueProducer.prepareFirstBucket(); ProtoOutputStream output; std::set<string> strSet; diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h index 97c107228f9c..329e39fc4eff 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 { @@ -55,4 +53,4 @@ 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..8d380006a8ab --- /dev/null +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -0,0 +1,382 @@ +/* + * 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(int atomId, const HashableDimensionKey& primaryKey, int oldState, + int newState) { + updates.emplace_back(primaryKey, newState); + } +}; + +// 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 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)); +} +// 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.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(2, mgr.getStateValue(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(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey)); +} + +/** + * 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(1, mgr.getStateValue(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(); + 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); + + 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", 1); + std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2); + + mgr.onLogEvent(*event1); + mgr.onLogEvent(*event2); + mgr.onLogEvent(*event3); + mgr.onLogEvent(*event5); + mgr.onLogEvent(*event5); + mgr.onLogEvent(*event6); + mgr.onLogEvent(*event7); + + // Query for UidProcessState of uid 1001 + HashableDimensionKey queryKey1; + getUidProcessKey(1001, &queryKey1); + EXPECT_EQ(1003, mgr.getStateValue(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, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, + queryKey2)); // default state + + // Query for UidProcessState of uid 1001 - after change in state + mgr.onLogEvent(*event4); + EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); + + // Query for ScreenState + EXPECT_EQ(2, mgr.getStateValue(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(2, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey3)); +} + +} // 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..38c22ab5d253 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -251,6 +251,101 @@ Predicate CreateIsInBackgroundPredicate() { return predicate; } +State CreateScreenState() { + State state; + state.set_id(StringToId("ScreenState")); + state.set_atom_id(29); + return state; +} + +State CreateUidProcessState() { + State state; + state.set_id(StringToId("UidProcessState")); + state.set_atom_id(27); + return state; +} + +State CreateOverlayState() { + State state; + state.set_id(StringToId("OverlayState")); + state.set_atom_id(59); + return state; +} + +State CreateScreenStateWithOnOffMap() { + State state; + state.set_id(StringToId("ScreenStateOnOff")); + state.set_atom_id(29); + + auto map = CreateScreenStateOnOffMap(); + *state.mutable_map() = map; + + return state; +} + +State CreateScreenStateWithInDozeMap() { + State state; + state.set_id(StringToId("ScreenStateInDoze")); + state.set_atom_id(29); + + 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()); diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 635c5835333a..c02610540cf0 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -104,6 +104,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); @@ -319,4 +350,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()); - } -} |