diff options
Diffstat (limited to 'cmds')
55 files changed, 2014 insertions, 540 deletions
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp index f92502370566..8be95e4659b4 100644 --- a/cmds/app_process/Android.bp +++ b/cmds/app_process/Android.bp @@ -22,7 +22,6 @@ cc_binary { "libcutils", "libdl", "libhidlbase", - "libhwbinder", "liblog", "libnativeloader", "libutils", diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 76b905d8985f..ed717c491467 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -125,6 +125,11 @@ public class Bmgr { return; } + if ("autorestore".equals(op)) { + doAutoRestore(userId); + return; + } + if ("enabled".equals(op)) { doEnabled(userId); return; @@ -213,6 +218,26 @@ public class Bmgr { 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"; } @@ -918,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."); @@ -992,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 d4d587108a54..4c77ba402595 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -69,6 +69,7 @@ cc_library { static_libs: [ "libandroidfw", "libbase", + "libcutils", "libutils", "libziparchive", ], @@ -121,6 +122,7 @@ cc_test { static_libs: [ "libandroidfw", "libbase", + "libcutils", "libidmap2", "liblog", "libutils", @@ -163,6 +165,7 @@ cc_binary { static_libs: [ "libandroidfw", "libbase", + "libcutils", "libidmap2", "liblog", "libutils", diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index bb8d92737563..f482191b09a8 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") 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/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index 0b349e10a118..d0530f2d344e 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -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) { @@ -176,6 +178,17 @@ Result<Unit> Scan(const std::vector<std::string>& args) { continue; } + // Note that conditional property enablement/exclusion only applies if + // the attribute is present. In its absence, all overlays are presumed enabled. + if (!overlay_info->requiredSystemPropertyName.empty() + && !overlay_info->requiredSystemPropertyValue.empty()) { + // if property set & equal to value, then include overlay - otherwise skip + if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "") + != overlay_info->requiredSystemPropertyValue) { + continue; + } + } + std::vector<std::string> fulfilled_policies; if (!override_policies.empty()) { fulfilled_policies = override_policies; 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/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/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h index 8797a788dd1d..9a0c2abced5a 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -30,6 +30,8 @@ namespace android::idmap2::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) bool is_static; // NOLINT(misc-non-private-member-variables-in-classes) int priority = -1; // NOLINT(misc-non-private-member-variables-in-classes) }; diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp index 71ba3f0f1ac2..dce83e35978d 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -103,6 +103,16 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, info.priority = std::stoi(iter->second); } + iter = tag->find("requiredSystemPropertyName"); + if (iter != tag->end()) { + info.requiredSystemPropertyName = iter->second; + } + + iter = tag->find("requiredSystemPropertyValue"); + if (iter != tag->end()) { + info.requiredSystemPropertyValue = iter->second; + } + return info; } 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/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/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 index c1cba5f7f22d..56f5cc034f05 100644 --- a/cmds/locksettings/TEST_MAPPING +++ b/cmds/locksettings/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit": [ + "presubmit-devicepolicy": [ { "name": "CtsDevicePolicyManagerTestCases", "options": [ diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 19fa64073183..cb2732561b56 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,23 +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", - "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", + "libstatssocket", "libsysutils", - "libcutils", + "libtimestats_proto", + "libutils", ], } @@ -204,56 +209,62 @@ cc_test { ], srcs: [ + // atom_field_options.proto needs field_options.proto, but that is + // 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", "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/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/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: [ @@ -262,11 +273,11 @@ cc_test { ], proto: { - type: "full", + type: "lite", include_dirs: ["external/protobuf/src"], }, - shared_libs: ["libprotobuf-cpp-full"], + shared_libs: ["libprotobuf-cpp-lite"], } @@ -279,21 +290,25 @@ cc_benchmark { defaults: ["statsd_defaults"], srcs: [ - "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", + // atom_field_options.proto needs field_options.proto, but that is + // not included in libprotobuf-cpp-lite, so compile it here. + ":libprotobuf-internal-protos", + + "benchmark/duration_metric_benchmark.cpp", "benchmark/filter_value_benchmark.cpp", "benchmark/get_dimensions_for_condition_benchmark.cpp", + "benchmark/hello_world_benchmark.cpp", + "benchmark/log_event_benchmark.cpp", + "benchmark/main.cpp", "benchmark/metric_util.cpp", - "benchmark/duration_metric_benchmark.cpp", + "benchmark/stats_write_benchmark.cpp", + "src/atom_field_options.proto", + "src/atoms.proto", + "src/stats_log.proto", ], proto: { - type: "full", + type: "lite", include_dirs: ["external/protobuf/src"], }, @@ -315,7 +330,7 @@ cc_benchmark { shared_libs: [ "libgtest_prod", "libstatslog", - "libprotobuf-cpp-full", + "libprotobuf-cpp-lite", ], } @@ -329,11 +344,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/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index 1185127ab805..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; } @@ -153,7 +138,7 @@ 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; + int uidField = it->second; // uidField is the field number in proto return field.getDepth() == 0 && field.getPosAtDepth(0) == uidField && value.getType() == INT; } 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.h b/cmds/statsd/src/StatsLogProcessor.h index f0db1b0128a1..3d002d2efdd0 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); 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/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index d1dcb5df7838..7ace44eef564 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -24,6 +24,7 @@ #include "subscriber/IncidentdReporter.h" #include "subscriber/SubscriberReporter.h" +#include <inttypes.h> #include <statslog.h> #include <time.h> @@ -224,7 +225,7 @@ void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId } if (!mSubscriptions.empty()) { - ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.", + ALOGI("An anomaly (%" PRId64 ") %s has occurred! Informing subscribers.", mAlert.id(), key.toString().c_str()); informSubscribers(key, metricId, metricValue); } else { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index b01b0a8e083c..6249de3d3bac 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -334,10 +334,13 @@ message Atom { 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; @@ -375,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; @@ -401,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; } /** @@ -2615,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; @@ -3062,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; @@ -3072,7 +3081,7 @@ message OverlayStateChanged { ENTERED = 1; EXITED = 2; } - optional State state = 4; + optional State state = 4 [(state_field_option).option = EXCLUSIVE]; } /* @@ -3965,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]; } /** @@ -4011,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 @@ -4022,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 rss_high_water_mark_in_kilobytes instead. This field is + // computed by converting kilobytes to bytes. + optional int64 rss_high_water_mark_in_bytes = 3 [deprecated = true]; - // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0. - optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true]; - - // Elapsed real time when the process started. - // Value is read from /proc/PID/stat, field 22. - optional int64 start_time_nanos = 7; - - // SWAP - // Value read from /proc/PID/status, field VmSwap. - optional int64 swap_in_bytes = 8; - - // Anonymous page size plus swap size. Values are read from /proc/PID/status. - optional int32 anon_rss_and_swap_in_kilobytes = 9; + // RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in + // /proc/PID/status. + optional int32 rss_high_water_mark_in_kilobytes = 4; } /* - * Logs the memory high-water mark for a process. + * Logs the memory stats for a process. * - * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie) + * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService) * and for selected native processes. - * - * Pulling this atom resets high-water mark counters for all processes. */ -message ProcessMemoryHighWaterMark { +message ProcessMemorySnapshot { // The uid if available. -1 means not available. optional int32 uid = 1 [(is_uid) = true]; @@ -4091,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; } /* @@ -5937,7 +5951,8 @@ message BubbleUIChanged { optional bool is_ongoing = 10; // Whether the bubble is produced by an app running in foreground. - optional bool is_foreground = 11; + // This is deprecated and the value should be ignored. + optional bool is_foreground = 11 [deprecated = true]; } /** @@ -7087,3 +7102,138 @@ message UpdateEngineSuccessfulUpdateReported { // 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: + * frameworks/base/core/java/android/app/AppCompatCallbacks.java and + * frameworks/base/services/core/java/com/android/server/compat/PlatformCompat.java + */ +message AppCompatibilityChangeReported { + // The UID of the app being affected by the compatibilty change. + optional int32 uid = 1 [(is_uid) = true]; + + // The ID of the change affecting the app. + optional int64 change_id = 2; + + enum State { + UNKNOWN_STATE = 0; + ENABLED = 1; + DISABLED = 2; + LOGGED = 3; + } + + // The state of the change - if logged from gating whether it was enabled or disabled, or just + // logged otherwise. + optional State state = 3; + + enum Source { + UNKNOWN_SOURCE = 0; + APP_PROCESS = 1; + SYSTEM_SERVER = 2; + } + + // Where it was logged from. + optional Source source = 4; + +} diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateConditionTracker.cpp index 18c7178dd7d7..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,7 +176,7 @@ void StateTracker::evaluateCondition(const LogEvent& event, return; } -void StateTracker::isConditionMet( +void StateConditionTracker::isConditionMet( const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, const bool isPartialLink, vector<ConditionState>& conditionCache) const { diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateConditionTracker.h index 5ae4441713cd..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: @@ -109,7 +109,7 @@ 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 diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp index bbdb5405ca05..d38b87f046e0 100644 --- a/cmds/statsd/src/external/GpuStatsPuller.cpp +++ b/cmds/statsd/src/external/GpuStatsPuller.cpp @@ -92,9 +92,15 @@ 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(); diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 475f18a9b0b8..43e33f59f612 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; @@ -142,17 +145,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)}}, @@ -266,6 +267,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/main.cpp b/cmds/statsd/src/main.cpp index 68082c2dc4d2..7d446a9a1ed6 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -53,6 +53,8 @@ void sigHandler(int sig) { if (gStatsService != nullptr) { gStatsService->Terminate(); } + ALOGW("statsd terminated on receiving signal %d.", sig); + exit(1); } void registerSigHandler() @@ -78,7 +80,7 @@ int main(int /*argc*/, char** /*argv*/) { ps->giveThreadPoolName(); IPCThreadState::self()->disableBackgroundScheduling(true); - ::android::hardware::configureRpcThreadpool(1 /*threads*/, false /*willJoin*/); + ::android::hardware::configureRpcThreadpool(4 /*threads*/, false /*willJoin*/); std::shared_ptr<LogEventQueue> eventQueue = std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/); diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index 10ac4a182f87..476fae37899d 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -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/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 94f833b20814..fdbdc83fb66e 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -83,9 +83,9 @@ public: mCurrentBucketStartTimeNs(timeBaseNs), mCurrentBucketNum(0), mCondition(initialCondition(conditionIndex)), + mConditionTrackerIndex(conditionIndex), mConditionSliced(false), mWizard(wizard), - mConditionTrackerIndex(conditionIndex), mContainANYPositionInDimensionsInWhat(false), mSliceByPositionALL(false), mHasLinksToAllConditionDimensionsInTracker(false), @@ -167,11 +167,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 { @@ -179,34 +174,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. @@ -218,9 +188,14 @@ public: dropDataLocked(dropTimeNs); } - // For test only. - inline int64_t getCurrentBucketNum() const { - return mCurrentBucketNum; + void prepareFirstBucket() { + std::lock_guard<std::mutex> lock(mMutex); + prepareFirstBucketLocked(); + } + + 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) { @@ -238,44 +213,41 @@ public: return isActiveLocked(); } + void flushIfExpire(int64_t elapsedTimestampNs); + void addActivation(int activationTrackerIndex, const ActivationType& activationType, int64_t ttl_seconds, int deactivationTrackerIndex = -1); - void prepareFirstBucket() { - std::lock_guard<std::mutex> lock(mMutex); - prepareFirstBucketLocked(); - } - - void flushIfExpire(int64_t elapsedTimestampNs); - void writeActiveMetricToProtoOutputStream( int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); -protected: - virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0; - virtual void onSlicedConditionMayChangeLocked(bool overallCondition, - const int64_t eventTime) = 0; - virtual void onDumpReportLocked(const int64_t dumpTimeNs, - const bool include_current_partial_bucket, - const bool erase_data, - const DumpLatency dumpLatency, - std::set<string> *str_set, - android::util::ProtoOutputStream* protoOutput) = 0; - virtual void clearPastBucketsLocked(const int64_t dumpTimeNs) = 0; - virtual size_t byteSizeLocked() const = 0; - virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0; - bool evaluateActiveStateLocked(int64_t elapsedTimestampNs); - - void activateLocked(int activationTrackerIndex, int64_t elapsedTimestampNs); - void cancelEventActivationLocked(int deactivationTrackerIndex); + // Start: getters/setters + inline const int64_t& getMetricId() const { + return mMetricId; + } - inline bool isActiveLocked() const { - return mIsActive; + // For test only. + inline int64_t getCurrentBucketNum() const { + return mCurrentBucketNum; } - void loadActiveMetricLocked(const ActiveMetric& activeMetric, int64_t currentTimeNs); + int64_t getBucketSizeInNs() const { + std::lock_guard<std::mutex> lock(mMutex); + return mBucketSizeNs; + } - 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. @@ -283,14 +255,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 @@ -303,12 +267,66 @@ 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; + virtual void prepareFirstBucketLocked() {}; + 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 { @@ -319,8 +337,6 @@ protected: return (endNs - mTimeBaseNs) / mBucketSizeNs - 1; } - virtual void dropDataLocked(const int64_t dropTimeNs) = 0; - const int64_t mMetricId; const ConfigKey mConfigKey; @@ -341,17 +357,18 @@ protected: ConditionState mCondition; + int mConditionTrackerIndex; + bool mConditionSliced; sp<ConditionWizard> mWizard; - int mConditionTrackerIndex; - - vector<Matcher> mDimensionsInWhat; // The dimensions_in_what defined in statsd_config - bool mContainANYPositionInDimensionsInWhat; + bool mSliceByPositionALL; + 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. bool mHasLinksToAllConditionDimensionsInTracker; @@ -360,29 +377,6 @@ 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 { @@ -397,6 +391,7 @@ protected: 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; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index dd32c08faba3..40484f4fb86b 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -21,7 +21,7 @@ #include "../condition/CombinationConditionTracker.h" #include "../condition/SimpleConditionTracker.h" -#include "../condition/StateTracker.h" +#include "../condition/StateConditionTracker.h" #include "../external/StatsPullerManager.h" #include "../matchers/CombinationLogMatchingTracker.h" #include "../matchers/SimpleLogMatchingTracker.h" @@ -35,6 +35,8 @@ #include "stats_util.h" #include "statslog.h" +#include <inttypes.h> + using std::set; using std::string; using std::unordered_map; @@ -182,13 +184,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( @@ -240,8 +242,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 { @@ -593,7 +595,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t for (int i = 0; i < config.no_report_metric_size(); ++i) { const auto no_report_metric = config.no_report_metric(i); if (metricMap.find(no_report_metric) == metricMap.end()) { - ALOGW("no_report_metric %lld not exist", no_report_metric); + ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric); return false; } noReportMetricIds.insert(no_report_metric); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 028231ff908c..3704969039c4 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -113,7 +113,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..a3059c5b52ac --- /dev/null +++ b/cmds/statsd/src/state/StateManager.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include "StateManager.h" + +namespace android { +namespace os { +namespace statsd { + +StateManager& StateManager::getInstance() { + static StateManager sStateManager; + return sStateManager; +} + +void StateManager::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 stateAtomId, wp<StateListener> listener) { + std::lock_guard<std::mutex> lock(mMutex); + + // Check if state tracker already exists + if (mStateTrackers.find(stateAtomId) == mStateTrackers.end()) { + // Create a new state tracker iff atom is a state atom + auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(stateAtomId); + if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) { + mStateTrackers[stateAtomId] = new StateTracker(stateAtomId, it->second); + } else { + ALOGE("StateManager cannot register listener, Atom %d is not a state atom", + stateAtomId); + return false; + } + } + mStateTrackers[stateAtomId]->registerListener(listener); + return true; +} + +void StateManager::unregisterListener(int stateAtomId, 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(stateAtomId); + 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", + stateAtomId); + } + lock.unlock(); +} + +int StateManager::getState(int stateAtomId, const HashableDimensionKey& key) { + std::lock_guard<std::mutex> lock(mMutex); + if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) { + return mStateTrackers[stateAtomId]->getState(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..ce60f1482be7 --- /dev/null +++ b/cmds/statsd/src/state/StateManager.h @@ -0,0 +1,77 @@ +/* + * 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/Log.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 stateAtomId is the id of a state atom and notifies the + // correct StateTracker to register the listener. If the correct + // StateTracker does not exist, a new StateTracker is created. + bool registerListener(int stateAtomId, 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 stateAtomId, wp<StateListener> listener); + + // Queries the correct StateTracker for the state that is mapped to the given + // query key. + // If the StateTracker doesn't exist, returns StateTracker::kStateUnknown. + int getState(int stateAtomId, const HashableDimensionKey& queryKey); + + inline int getStateTrackersCount() { + std::lock_guard<std::mutex> lock(mMutex); + return mStateTrackers.size(); + } + + inline int getListenersCount(int stateAtomId) { + std::lock_guard<std::mutex> lock(mMutex); + if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) { + return mStateTrackers[stateAtomId]->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..5a91950b9f8b --- /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): 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): 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::getState(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..f22706c8418d --- /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 getState(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 d9c04f248af0..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,12 +75,14 @@ message CountBucketInfo { message CountMetricData { optional DimensionsValue dimensions_in_what = 1; - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; + repeated StateValue slice_by_state = 6; repeated CountBucketInfo bucket_info = 3; repeated DimensionsValue dimension_leaf_values_in_what = 4; + optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; + repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; } diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 1b7f39806608..c107397b0273 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -150,6 +150,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 +176,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 +208,15 @@ message CountMetric { optional FieldMatcher dimensions_in_what = 4; - optional FieldMatcher dimensions_in_condition = 7 [deprecated = true]; + 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 { @@ -438,6 +468,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 70f0f6f75a59..441d3c896467 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -330,6 +330,7 @@ TEST(AtomMatcherTest, TestUidFieldMatcher) { 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 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/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/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/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp new file mode 100644 index 000000000000..c89ffea85709 --- /dev/null +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -0,0 +1,356 @@ +/* + * 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 stateAtomId, 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; +} + +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; +} +// 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()); + 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 + 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 + 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 + 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.getState(android::util::SCREEN_STATE_CHANGED, queryKey)); +} + +/** + * Test StateManager's onLogEvent and StateListener's onStateChanged correctly + * updates listener for states with primary keys. + */ +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, + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + 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, &queryKey); + EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey)); +} + +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, "package1", 1); // state: ENTERED + mgr.onLogEvent(*event); + + // check listener update + 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); +} + +/** + * 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> event = + buildIncorrectOverlayEvent(1000, "package1", 1); // state: ENTERED + mgr.onLogEvent(*event); + + // check listener update + 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); // state value: + 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.getState(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.getState(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.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); + + // Query for ScreenState + EXPECT_EQ(2, mgr.getState(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.getState(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/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index e0f7d862e70a..c9f069d62003 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -65,11 +65,21 @@ public final class Telecom extends BaseCommand { private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account"; private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer"; private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer"; + /** + * Change the system dialer package name if a package name was specified, + * Example: adb shell telecom set-system-dialer <PACKAGE> + * + * Restore it to the default if if argument is "default" or no argument is passed. + * Example: adb shell telecom set-system-dialer default + */ + private static final String COMMAND_SET_SYSTEM_DIALER = "set-system-dialer"; private static final String COMMAND_GET_SYSTEM_DIALER = "get-system-dialer"; private static final String COMMAND_WAIT_ON_HANDLERS = "wait-on-handlers"; private static final String COMMAND_SET_SIM_COUNT = "set-sim-count"; private static final String COMMAND_GET_SIM_CONFIG = "get-sim-config"; private static final String COMMAND_GET_MAX_PHONES = "get-max-phones"; + private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER = + "set-test-emergency-phone-account-package-filter"; private ComponentName mComponent; private String mAccountId; @@ -83,7 +93,10 @@ public final class Telecom extends BaseCommand { + "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" + "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" + "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" - + "usage: telecom set-user-selected-outgoing-phone-account <COMPONENT> <ID> " + + "usage: telecom register-sim-phone-account [-e] <COMPONENT> <ID> <USER_SN>" + + " <LABEL>: registers a PhoneAccount with CAPABILITY_SIM_SUBSCRIPTION" + + " and optionally CAPABILITY_PLACE_EMERGENCY_CALLS if \"-e\" is provided\n" + + "usage: telecom set-user-selected-outgoing-phone-account [-e] <COMPONENT> <ID> " + "<USER_SN>\n" + "usage: telecom set-test-call-redirection-app <PACKAGE>\n" + "usage: telecom set-test-call-screening-app <PACKAGE>\n" @@ -100,6 +113,7 @@ public final class Telecom extends BaseCommand { + "usage: telecom set-sim-count <COUNT>\n" + "usage: telecom get-sim-config\n" + "usage: telecom get-max-phones\n" + + "usage: telecom set-emer-phone-account-filter <PACKAGE>\n" + "\n" + "telecom set-phone-account-enabled: Enables the given phone account, if it has" + " already been registered with Telecom.\n" @@ -113,6 +127,8 @@ public final class Telecom extends BaseCommand { + "telecom get-default-dialer: Displays the current default dialer.\n" + "\n" + "telecom get-system-dialer: Displays the current system dialer.\n" + + "telecom set-system-dialer: Set the override system dialer to the given" + + " component. To remove the override, send \"default\"\n" + "\n" + "telecom wait-on-handlers: Wait until all handlers finish their work.\n" + "\n" @@ -123,6 +139,10 @@ public final class Telecom extends BaseCommand { + " or \"\" for single SIM\n" + "\n" + "telecom get-max-phones: Get the max supported phones from the modem.\n" + + "telecom set-test-emergency-phone-account-package-filter <PACKAGE>: sets a" + + " package name that will be used for test emergency calls. To clear," + + " send an empty package name. Real emergency calls will still be placed" + + " over Telephony.\n" ); } @@ -193,6 +213,9 @@ public final class Telecom extends BaseCommand { case COMMAND_GET_DEFAULT_DIALER: runGetDefaultDialer(); break; + case COMMAND_SET_SYSTEM_DIALER: + runSetSystemDialer(); + break; case COMMAND_GET_SYSTEM_DIALER: runGetSystemDialer(); break; @@ -208,6 +231,9 @@ public final class Telecom extends BaseCommand { case COMMAND_GET_MAX_PHONES: runGetMaxPhones(); break; + case COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER: + runSetEmergencyPhoneAccountPackageFilter(); + break; default: Log.w(this, "onRun: unknown command: %s", command); throw new IllegalArgumentException ("unknown command '" + command + "'"); @@ -234,19 +260,31 @@ public final class Telecom extends BaseCommand { } private void runRegisterSimPhoneAccount() throws RemoteException { + boolean isEmergencyAccount = false; + String opt; + while ((opt = nextOption()) != null) { + switch (opt) { + case "-e": { + isEmergencyAccount = true; + break; + } + } + } final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs(); final String label = nextArgRequired(); final String address = nextArgRequired(); + int capabilities = PhoneAccount.CAPABILITY_CALL_PROVIDER + | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION + | (isEmergencyAccount ? PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS : 0); PhoneAccount account = PhoneAccount.builder( handle, label) - .setAddress(Uri.parse(address)) - .setSubscriptionAddress(Uri.parse(address)) - .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | - PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) - .setShortDescription(label) - .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) - .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) - .build(); + .setAddress(Uri.parse(address)) + .setSubscriptionAddress(Uri.parse(address)) + .setCapabilities(capabilities) + .setShortDescription(label) + .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) + .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) + .build(); mTelecomService.registerPhoneAccount(account); System.out.println("Success - " + handle + " registered."); } @@ -297,6 +335,14 @@ public final class Telecom extends BaseCommand { System.out.println("Success - " + packageName + " set as override default dialer."); } + private void runSetSystemDialer() throws RemoteException { + final String flatComponentName = nextArg(); + final ComponentName componentName = (flatComponentName.equals("default") + ? null : parseComponentName(flatComponentName)); + mTelecomService.setSystemDialer(componentName); + System.out.println("Success - " + componentName + " set as override system dialer."); + } + private void runGetDefaultDialer() throws RemoteException { System.out.println(mTelecomService.getDefaultDialerPackage()); } @@ -338,6 +384,18 @@ public final class Telecom extends BaseCommand { } } + private void runSetEmergencyPhoneAccountPackageFilter() throws RemoteException { + String packageName = mArgs.getNextArg(); + if (TextUtils.isEmpty(packageName)) { + mTelecomService.setTestEmergencyPhoneAccountPackageNameFilter(null); + System.out.println("Success - filter cleared"); + } else { + mTelecomService.setTestEmergencyPhoneAccountPackageNameFilter(packageName); + System.out.println("Success = filter set to " + packageName); + } + + } + private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException { if (TextUtils.isEmpty(mArgs.peekNextArg())) { return null; |