Merge "Tracks isolated uid's and their parent uid."
diff --git a/Android.mk b/Android.mk
index c4f222e..817aa80 100644
--- a/Android.mk
+++ b/Android.mk
@@ -137,6 +137,7 @@
../../system/bt/binder/android/bluetooth/IBluetoothPbap.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothPbapClient.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothSap.aidl \
+ ../../system/bt/binder/android/bluetooth/IBluetoothSocketManager.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothStateChangeCallback.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHeadsetClient.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHidDevice.aidl \
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 87d318b..d99136f 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -38,7 +38,7 @@
src/matchers/CombinationLogMatchingTracker.cpp \
src/matchers/matcher_util.cpp \
src/matchers/SimpleLogMatchingTracker.cpp \
- src/metrics/CountAnomalyTracker.cpp \
+ src/anomaly/DiscreteAnomalyTracker.cpp \
src/metrics/MetricProducer.cpp \
src/metrics/EventMetricProducer.cpp \
src/metrics/CountMetricProducer.cpp \
@@ -150,6 +150,7 @@
LOCAL_SRC_FILES := \
$(statsd_common_src) \
tests/AnomalyMonitor_test.cpp \
+ tests/anomaly/AnomalyTracker_test.cpp \
tests/ConditionTracker_test.cpp \
tests/ConfigManager_test.cpp \
tests/indexed_priority_queue_test.cpp \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index f0689d2..7ff42b6 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -18,7 +18,6 @@
#include "statslog.h"
#include "StatsLogProcessor.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "metrics/CountMetricProducer.h"
#include "stats_util.h"
@@ -26,6 +25,13 @@
#include <utils/Errors.h>
using namespace android;
+using android::util::FIELD_TYPE_BOOL;
+using android::util::FIELD_TYPE_FLOAT;
+using android::util::FIELD_TYPE_INT32;
+using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
+using android::util::ProtoOutputStream;
using std::make_unique;
using std::unique_ptr;
using std::vector;
@@ -34,6 +40,14 @@
namespace os {
namespace statsd {
+// for ConfigMetricsReport
+const int FIELD_ID_CONFIG_KEY = 1;
+const int FIELD_ID_METRICS = 2;
+const int FIELD_ID_UID_MAP = 3;
+// for ConfigKey
+const int FIELD_ID_UID = 1;
+const int FIELD_ID_NAME = 1;
+
StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
const std::function<void(const vector<uint8_t>&)>& pushLog)
: mUidMap(uidMap), mPushLog(pushLog) {
@@ -87,27 +101,46 @@
}
}
-ConfigMetricsReport StatsLogProcessor::onDumpReport(const ConfigKey& key) {
- ConfigMetricsReport report;
-
+vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) {
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end()) {
ALOGW("Config source %s does not exist", key.ToString().c_str());
- return report;
+ return vector<uint8_t>();
}
- auto set_key = report.mutable_config_key();
- set_key->set_uid(key.GetUid());
- set_key->set_name(key.GetName());
- for (auto m : it->second->onDumpReport()) {
- // Transfer the vector of StatsLogReport into a field
- // TODO: perhaps we just have bytes being returned from onDumpReport and transfer bytes
- auto dest = report.add_metrics();
- *dest = m;
+ ProtoOutputStream proto;
+
+ // Fill in ConfigKey.
+ long long configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
+ proto.write(FIELD_TYPE_STRING | FIELD_ID_NAME, key.GetName());
+ proto.end(configKeyToken);
+
+ // Fill in StatsLogReport's.
+ for (auto& m : it->second->onDumpReport()) {
+ // Add each vector of StatsLogReport into a repeated field.
+ proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_METRICS, reinterpret_cast<char*>(m.get()->data()),
+ m.get()->size());
}
- auto temp = mUidMap->getOutput(key);
- report.mutable_uid_map()->Swap(&temp);
- return report;
+
+ // Fill in UidMap.
+ auto uidMap = mUidMap->getOutput(key);
+ const int uidMapSize = uidMap.ByteSize();
+ char uidMapBuffer[uidMapSize];
+ uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
+ proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
+
+ vector<uint8_t> buffer(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&buffer[pos], iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
+
+ return buffer;
}
void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 0083827..f38d715 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -41,8 +41,7 @@
void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config);
void OnConfigRemoved(const ConfigKey& key);
- // TODO: Once we have the ProtoOutputStream in c++, we can just return byte array.
- ConfigMetricsReport onDumpReport(const ConfigKey& key);
+ vector<uint8_t> onDumpReport(const ConfigKey& key);
/* Request a flush through a binder call. */
void flush();
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h
index e2ac623..d9207e9 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.h
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h
@@ -55,6 +55,7 @@
};
};
+// TODO: Rename this file to AnomalyAlarmMonitor.
/**
* Manages alarms for Anomaly Detection.
*/
@@ -95,6 +96,15 @@
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> popSoonerThan(
uint32_t timestampSec);
+ // TODO: Function that uses popSoonerThan to get all alarms that have fired, and then
+ // iterates over all DurationAnomalyTracker, looking for those alarms. When they're found,
+ // have them declareAnomaly on those alarms. This means that DurationAnomalyTracker
+ // must be thread-safe (since this is being called on a different thread). There is no
+ // worry about missing the alarms (due to them being cancelled after this function being called)
+ // because DurationAnomalyTracker guarantees that it checks for anaomlies when it cancels
+ // alarms anyway.
+ // void declareAnomalies(uint32_t timestampSec);
+
/**
* Returns the projected alarm timestamp that is registered with
* StatsCompanionService. This may not be equal to the soonest alarm,
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
new file mode 100644
index 0000000..9c9bde9
--- /dev/null
+++ b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "DiscreteAnomalyTracker.h"
+
+#include <time.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+DiscreteAnomalyTracker::DiscreteAnomalyTracker(const Alert& alert) : mAlert(alert) {
+ VLOG("DiscreteAnomalyTracker() called");
+ if (mAlert.number_of_buckets() <= 0) {
+ ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets",
+ (long long)mAlert.number_of_buckets());
+ return;
+ }
+ mPastBuckets.resize(mAlert.number_of_buckets());
+ reset(); // initialization
+}
+
+DiscreteAnomalyTracker::~DiscreteAnomalyTracker() {
+ VLOG("~DiscreteAnomalyTracker() called");
+}
+
+void DiscreteAnomalyTracker::reset() {
+ VLOG("reset() called.");
+ mPastBuckets.clear();
+ mPastBuckets.resize(mAlert.number_of_buckets());
+ mSumOverPastBuckets.clear();
+ mCurrentBucketIndex = -1;
+ mLastAlarmAtBucketIndex = -1;
+ mAnomalyDeclared = 0;
+}
+
+size_t DiscreteAnomalyTracker::index(int64_t bucketNum) {
+ return bucketNum % mAlert.number_of_buckets();
+}
+
+void DiscreteAnomalyTracker::addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues,
+ int64_t bucketIndex) {
+ VLOG("addPastBucket() called.");
+ if (bucketIndex <= mCurrentBucketIndex - mAlert.number_of_buckets()) {
+ ALOGE("Cannot add a past bucket %lld units in past", (long long)bucketIndex);
+ return;
+ }
+
+ // Empty out old mPastBuckets[i] values and update mSumOverPastBuckets.
+ if (bucketIndex - mCurrentBucketIndex >= mAlert.number_of_buckets()) {
+ mPastBuckets.clear();
+ mPastBuckets.resize(mAlert.number_of_buckets());
+ mSumOverPastBuckets.clear();
+ } else {
+ for (int64_t i = std::max(
+ 0LL, (long long)(mCurrentBucketIndex - mAlert.number_of_buckets() + 1));
+ i < bucketIndex - mAlert.number_of_buckets(); i++) {
+ const int idx = index(i);
+ subtractBucketFromSum(mPastBuckets[idx]);
+ mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket.
+ }
+ }
+ subtractBucketFromSum(mPastBuckets[index(bucketIndex)]);
+ mPastBuckets[index(bucketIndex)] = nullptr; // release (but not clear) the old bucket.
+
+ // Replace the oldest bucket with the new bucket we are adding.
+ mPastBuckets[index(bucketIndex)] = BucketValues;
+ addBucketToSum(BucketValues);
+
+ mCurrentBucketIndex = std::max(mCurrentBucketIndex, bucketIndex);
+}
+
+void DiscreteAnomalyTracker::subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket) {
+ if (bucket == nullptr) {
+ return;
+ }
+ // For each dimension present in the bucket, subtract its value from its corresponding sum.
+ for (const auto& keyValuePair : *bucket) {
+ auto itr = mSumOverPastBuckets.find(keyValuePair.first);
+ if (itr == mSumOverPastBuckets.end()) {
+ continue;
+ }
+ itr->second -= keyValuePair.second;
+ // TODO: No need to look up the object twice like this. Use a var.
+ if (itr->second == 0) {
+ mSumOverPastBuckets.erase(itr);
+ }
+ }
+}
+
+void DiscreteAnomalyTracker::addBucketToSum(const shared_ptr<const DimToValMap>& bucket) {
+ if (bucket == nullptr) {
+ return;
+ }
+ // For each dimension present in the bucket, add its value to its corresponding sum.
+ for (const auto& keyValuePair : *bucket) {
+ mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
+ }
+}
+
+bool DiscreteAnomalyTracker::detectAnomaly() {
+ for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
+ if (mAlert.has_trigger_if_sum_gt() && itr->second > mAlert.trigger_if_sum_gt()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void DiscreteAnomalyTracker::declareAndDeclareAnomaly() {
+ if (detectAnomaly()) {
+ declareAnomaly();
+ }
+}
+
+void DiscreteAnomalyTracker::declareAnomaly() {
+ if (mLastAlarmAtBucketIndex >= 0 && mCurrentBucketIndex - mLastAlarmAtBucketIndex <=
+ (long long)mAlert.refractory_period_in_buckets()) {
+ VLOG("Skipping anomaly check since within refractory period");
+ return;
+ }
+ mAnomalyDeclared++;
+ // TODO(guardrail): Consider guarding against too short refractory periods.
+ mLastAlarmAtBucketIndex = mCurrentBucketIndex;
+
+ if (mAlert.has_incidentd_details()) {
+ const Alert_IncidentdDetails& incident = mAlert.incidentd_details();
+ if (incident.has_alert_name()) {
+ ALOGW("An anomaly (%s) has occurred! Informing incidentd.",
+ incident.alert_name().c_str());
+ } else {
+ // TODO: Can construct a name based on the criteria (and/or relay the criteria).
+ ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
+ }
+ // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
+ } else {
+ ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
new file mode 100644
index 0000000..ed7d5d7
--- /dev/null
+++ b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gtest/gtest_prod.h>
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+#include "stats_util.h" // HashableDimensionKey and DimToValMap
+
+#include <memory> // unique_ptr
+#include <stdlib.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::unordered_map;
+using std::shared_ptr;
+
+// This anomaly track assmues that all values are non-negative.
+class DiscreteAnomalyTracker {
+ public:
+ DiscreteAnomalyTracker(const Alert& alert);
+
+ virtual ~DiscreteAnomalyTracker();
+
+ // Adds a new bucket or updates an existing bucket.
+ // Bucket index starts from 0.
+ void addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues, int64_t bucketIndex);
+
+ // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
+ bool detectAnomaly();
+
+ // Informs incidentd about the detected alert.
+ void declareAnomaly();
+
+ // Detects the alert and informs the incidentd when applicable.
+ void declareAndDeclareAnomaly();
+
+private:
+ // statsd_config.proto Alert message that defines this tracker.
+ const Alert mAlert;
+
+ // The exisiting bucket list.
+ std::vector<shared_ptr<const DimToValMap>> mPastBuckets;
+
+ // Sum over all existing buckets cached in mPastBuckets.
+ DimToValMap mSumOverPastBuckets;
+
+ // Current bucket index of the current anomaly detection window. Bucket index starts from 0.
+ int64_t mCurrentBucketIndex = -1;
+
+ // The bucket index when the last anomaly was declared.
+ int64_t mLastAlarmAtBucketIndex = -1;
+
+ // The total number of declared anomalies.
+ int64_t mAnomalyDeclared = 0;
+
+ // Add the information in the given bucket to mSumOverPastBuckets.
+ void addBucketToSum(const shared_ptr<const DimToValMap>& bucket);
+
+ // Subtract the information in the given bucket from mSumOverPastBuckets
+ // and remove any items with value 0.
+ void subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket);
+
+ // Calculates the corresponding bucket index within the circular array.
+ size_t index(int64_t bucketNum);
+
+ // Resets all data. For use when all the data gets stale.
+ void reset();
+
+ FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
+ FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 40d41be..2618a21 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -25,7 +25,6 @@
#include <unordered_map>
#include "../matchers/matcher_util.h"
#include "ConditionTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
index 47e245e..4167bf9 100644
--- a/cmds/statsd/src/condition/condition_util.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -19,7 +19,6 @@
#include <vector>
#include "../matchers/matcher_util.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h
index a58766d..19ccfcf 100644
--- a/cmds/statsd/src/config/ConfigListener.h
+++ b/cmds/statsd/src/config/ConfigListener.h
@@ -16,7 +16,6 @@
#pragma once
-#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
#include "config/ConfigKey.h"
#include <utils/RefBase.h>
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 02e6903..94566ff 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -165,6 +165,12 @@
KeyMatcher* keyMatcher = metric->add_dimension();
keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
+ // Anomaly threshold for background count.
+ alert = metric->add_alerts();
+ alert->set_number_of_buckets(4);
+ alert->set_trigger_if_sum_gt(30);
+ alert->set_refractory_period_secs(20);
+
// Count process state changes, slice by uid, while SCREEN_IS_OFF
metric = config.add_count_metric();
metric->set_metric_id(3);
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
index 006d74c..adb691e 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
@@ -22,7 +22,6 @@
#include <unordered_map>
#include <vector>
#include "LogMatchingTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index d82da3b..ffbf248 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -17,7 +17,6 @@
#ifndef LOG_MATCHING_TRACKER_H
#define LOG_MATCHING_TRACKER_H
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "logd/LogEvent.h"
#include "matchers/matcher_util.h"
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
index e110ec8..5dca55e 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -23,7 +23,6 @@
#include <unordered_map>
#include <vector>
#include "LogMatchingTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 6aa2211..cccc9b3 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -16,7 +16,6 @@
#include "Log.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/LogMatchingTracker.h"
#include "matchers/matcher_util.h"
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
deleted file mode 100644
index 7aa748f..0000000
--- a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG true // STOPSHIP if true
-#include "Log.h"
-
-#include "CountAnomalyTracker.h"
-
-#include <time.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-CountAnomalyTracker::CountAnomalyTracker(const Alert& alert)
- : mAlert(alert),
- mNumPastBuckets(alert.number_of_buckets() > 0 ? alert.number_of_buckets() - 1 : 0),
- mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr) {
-
- VLOG("CountAnomalyTracker() called");
- if (alert.number_of_buckets() < 1) {
- ALOGE("Cannot create CountAnomalyTracker with %d buckets", alert.number_of_buckets());
- }
- reset(); // initialization
-}
-
-CountAnomalyTracker::~CountAnomalyTracker() {
- VLOG("~CountAnomalyTracker() called");
-}
-
-void CountAnomalyTracker::addPastBucket(int pastBucketCount,
- time_t numberOfBucketsAgo) {
- VLOG("addPastBucket() called.");
- if (numberOfBucketsAgo < 1) {
- ALOGE("Cannot add a past bucket %ld units in past", numberOfBucketsAgo);
- return;
- }
- // If past bucket was ancient, just empty out all past info.
- // This always applies if mNumPastBuckets == 0 (i.e. store no past buckets).
- if (numberOfBucketsAgo > (time_t) mNumPastBuckets) {
- reset();
- return;
- }
-
- // Empty out old mPastBuckets[i] values and update mSumPastCounters.
- for (size_t i = mOldestBucketIndex;
- i < mOldestBucketIndex + numberOfBucketsAgo; i++) {
- mSumPastCounters -= mPastBuckets[index(i)];
- mPastBuckets[index(i)] = 0;
- }
-
- // Replace the oldest bucket with the new bucket we are adding.
- mPastBuckets[mOldestBucketIndex] = pastBucketCount;
- mSumPastCounters += pastBucketCount;
-
- // Advance the oldest bucket index by numberOfBucketsAgo units.
- mOldestBucketIndex = index(mOldestBucketIndex + numberOfBucketsAgo);
-
- // TODO: Once dimensions are added to mSumPastCounters:
- // iterate through mSumPastCounters and remove any entries that are 0.
-}
-
-void CountAnomalyTracker::reset() {
- VLOG("reset() called.");
- for (size_t i = 0; i < mNumPastBuckets; i++) {
- mPastBuckets[i] = 0;
- }
- mSumPastCounters = 0;
- mOldestBucketIndex = 0;
-}
-
-void CountAnomalyTracker::checkAnomaly(int currentCount) {
- // Skip the check if in refractory period.
- if (time(nullptr) < mRefractoryPeriodEndsSec) {
- VLOG("Skipping anomaly check since within refractory period");
- return;
- }
-
- // TODO: Remove these extremely verbose debugging log.
- VLOG("Checking whether %d + %d > %lld",
- mSumPastCounters, currentCount, mAlert.trigger_if_sum_gt());
-
- // Note that this works even if mNumPastBuckets < 1 (since then
- // mSumPastCounters = 0 so the comparison is based only on currentCount).
- if (mAlert.has_trigger_if_sum_gt() &&
- mSumPastCounters + currentCount > mAlert.trigger_if_sum_gt()) {
- declareAnomaly();
- }
-}
-
-void CountAnomalyTracker::declareAnomaly() {
- // TODO(guardrail): Consider guarding against too short refractory periods.
- time_t currTime = time(nullptr);
- mRefractoryPeriodEndsSec = currTime + mAlert.refractory_period_secs();
-
- // TODO: If we had access to the bucket_size_millis, consider calling reset()
- // if (mAlert.refractory_period_secs() > mNumPastBuckets * bucket_size_millis * 1000).
-
- if (mAlert.has_incidentd_details()) {
- const Alert_IncidentdDetails& incident = mAlert.incidentd_details();
- if (incident.has_alert_name()) {
- ALOGW("An anomaly (%s) has occurred! Informing incidentd.",
- incident.alert_name().c_str());
- } else {
- // TODO: Can construct a name based on the criteria (and/or relay the criteria).
- ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
- }
- // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
- } else {
- ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.h b/cmds/statsd/src/metrics/CountAnomalyTracker.h
deleted file mode 100644
index 79c47d2a..0000000
--- a/cmds/statsd/src/metrics/CountAnomalyTracker.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef COUNT_ANOMALY_TRACKER_H
-#define COUNT_ANOMALY_TRACKER_H
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
-
-#include <memory> // unique_ptr
-#include <stdlib.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// TODO: Can probably be used for Count, Value, and Gauge. If so, rename to ValueAnomalyTracker.
-// (caveat: currently, the value cannot be negative. Probably fine for P.)
-class CountAnomalyTracker {
-public:
- CountAnomalyTracker(const Alert& alert);
-
- virtual ~CountAnomalyTracker();
-
-
- // Adds a new past bucket, holding pastBucketCount, and then advances the
- // present by numberOfBucketsAgo buckets (filling any intervening buckets
- // with 0s).
- // Thus, the newly added bucket (which holds pastBucketCount) is stored
- // numberOfBucketsAgo buckets ago.
- void addPastBucket(int pastBucketCount, time_t numberOfBucketsAgo);
-
- // Informs the anomaly tracker of the current bucket's count, so that it can
- // determine whether an anomaly has occurred. This value is not stored.
- void checkAnomaly(int currentCount);
-
-private:
- // statsd_config.proto Alert message that defines this tracker.
- const Alert mAlert;
-
- // Number of past buckets. One less than the total number of buckets needed
- // for the anomaly detection (since the current bucket is not in the past).
- const size_t mNumPastBuckets;
-
- // Count values for each of the past mNumPastBuckets buckets.
- // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
- std::unique_ptr<int[]> mPastBuckets;
-
- // Sum over all of mPastBuckets (cached).
- // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
- // At that point, mSumPastCounters must never contain entries of 0.
- int mSumPastCounters;
-
- // Index of the oldest bucket (i.e. the next bucket to be overwritten).
- size_t mOldestBucketIndex = 0;
-
- // Timestamp that the refractory period (if this anomaly was declared) ends, in seconds.
- // If an anomaly was never declared, set to 0.
- time_t mRefractoryPeriodEndsSec = 0;
-
- void declareAnomaly();
-
- // Calculates the corresponding index within the circular array.
- size_t index(size_t unsafeIndex) {
- return unsafeIndex % mNumPastBuckets;
- }
-
- // Resets all data. For use when all the data gets stale.
- void reset();
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // COUNT_ANOMALY_TRACKER_H
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 94f4adf..0c9e522 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -17,7 +17,7 @@
#define DEBUG true // STOPSHIP if true
#include "Log.h"
-#include "CountAnomalyTracker.h"
+#include "../anomaly/DiscreteAnomalyTracker.h"
#include "CountMetricProducer.h"
#include "stats_util.h"
@@ -61,6 +61,7 @@
const int FIELD_ID_COUNT = 3;
// TODO: add back AnomalyTracker.
+
CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& wizard,
const uint64_t startTimeNs)
@@ -76,7 +77,7 @@
for (int i = 0; i < metric.alerts_size(); i++) {
const Alert& alert = metric.alerts(i);
if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) {
- mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert));
+ mAnomalyTrackers.push_back(std::make_unique<DiscreteAnomalyTracker>(alert));
} else {
ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d",
alert.trigger_if_sum_gt(), alert.number_of_buckets());
@@ -116,7 +117,7 @@
VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
}
-StatsLogReport CountMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NS_PER_SEC;
// Dump current bucket if it's stale.
@@ -171,15 +172,13 @@
(long long)mCurrentBucketStartTimeNs);
VLOG("metric %lld dump report now...", mMetric.metric_id());
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
mPastBuckets.clear();
mByteSize = 0;
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
@@ -201,25 +200,19 @@
return;
}
- auto it = mCurrentSlicedCounter.find(eventKey);
+ auto it = mCurrentSlicedCounter->find(eventKey);
- if (it == mCurrentSlicedCounter.end()) {
+ if (it == mCurrentSlicedCounter->end()) {
// create a counter for the new key
- mCurrentSlicedCounter[eventKey] = 1;
-
+ (*mCurrentSlicedCounter)[eventKey] = 1;
} else {
// increment the existing value
auto& count = it->second;
count++;
}
- // TODO: Re-add anomaly detection (similar to):
- // for (auto& tracker : mAnomalyTrackers) {
- // tracker->checkAnomaly(mCounter);
- // }
-
VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
- mCurrentSlicedCounter[eventKey]);
+ (*mCurrentSlicedCounter)[eventKey]);
}
// When a new matched event comes in, we check if event falls into the current
@@ -230,12 +223,13 @@
}
// adjust the bucket start time
- int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ // TODO: This (and addPastBucket to which it goes) doesn't really need to be an int64.
+ uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
CountBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
- for (const auto& counter : mCurrentSlicedCounter) {
+ for (const auto& counter : *mCurrentSlicedCounter) {
info.mCount = counter.second;
auto& bucketList = mPastBuckets[counter.first];
bucketList.push_back(info);
@@ -244,15 +238,16 @@
mByteSize += sizeof(info);
}
- // TODO: Re-add anomaly detection (similar to):
- // for (auto& tracker : mAnomalyTrackers) {
- // tracker->addPastBucket(mCounter, numBucketsForward);
- //}
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->addOrUpdateBucket(mCurrentSlicedCounter, mCurrentBucketNum);
+ tracker->declareAndDeclareAnomaly();
+ }
- // Reset counters
- mCurrentSlicedCounter.clear();
+ // Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers).
+ mCurrentSlicedCounter = std::make_shared<DimToValMap>();
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
+ mCurrentBucketNum += numBucketsForward;
VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
(long long)mCurrentBucketStartTimeNs);
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index c3af006..b7e480c 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -23,14 +23,11 @@
#include <gtest/gtest_prod.h>
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
-#include "CountAnomalyTracker.h"
+#include "../anomaly/DiscreteAnomalyTracker.h"
#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
-using namespace std;
-
namespace android {
namespace os {
namespace statsd {
@@ -54,7 +51,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
void onSlicedConditionMayChange(const uint64_t eventTime) override;
@@ -82,9 +79,9 @@
size_t mByteSize;
// The current bucket.
- std::unordered_map<HashableDimensionKey, int> mCurrentSlicedCounter;
+ std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
- vector<unique_ptr<CountAnomalyTracker>> mAnomalyTrackers;
+ vector<std::unique_ptr<DiscreteAnomalyTracker>> mAnomalyTrackers;
void flushCounterIfNeeded(const uint64_t newEventTime);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index c0a0d98..2783bcea 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -159,7 +159,7 @@
}
}
-StatsLogReport DurationMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NS_PER_SEC;
// Dump current bucket if it's stale.
@@ -214,14 +214,12 @@
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
mPastBuckets.clear();
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
}
void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 8fdd0d4..eea00454 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -26,7 +26,6 @@
#include "duration_helper/DurationTracker.h"
#include "duration_helper/MaxDurationTracker.h"
#include "duration_helper/OringDurationTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -50,7 +49,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
void onSlicedConditionMayChange(const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index ee0bfde..f516cc7 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -84,20 +84,18 @@
void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
}
-StatsLogReport EventMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NS_PER_SEC;
mProto->end(mProtoToken);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
size_t bufferSize = mProto->size();
VLOG("metric %lld dump report now... proto size: %zu ", mMetric.metric_id(), bufferSize);
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
}
void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 2ca8181..0dccdf4 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -24,7 +24,6 @@
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -50,7 +49,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
void onSlicedConditionMayChange(const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index dcfcc19..285d29b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -106,7 +106,7 @@
void GaugeMetricProducer::finish() {
}
-StatsLogReport GaugeMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
VLOG("gauge metric %lld dump report now...", mMetric.metric_id());
// Dump current bucket if it's stale.
@@ -159,15 +159,13 @@
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
mPastBuckets.clear();
mByteSize = 0;
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 3757174..d80672d 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -24,7 +24,6 @@
#include "../external/StatsPullerManager.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -60,7 +59,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
size_t byteSize() override;
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 5ca83b2..3c4dd90 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -64,16 +64,16 @@
scheduledPull);
}
-std::unique_ptr<uint8_t[]> MetricProducer::serializeProto() {
+std::unique_ptr<std::vector<uint8_t>> MetricProducer::serializeProto() {
size_t bufferSize = mProto->size();
- std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]);
+ std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize));
size_t pos = 0;
auto it = mProto->data();
while (it.readBuffer() != NULL) {
size_t toRead = it.currentToRead();
- std::memcpy(&buffer[pos], it.readBuffer(), toRead);
+ std::memcpy(&buffer.get()[pos], it.readBuffer(), toRead);
pos += toRead;
it.rp()->move(toRead);
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 7ac97a8..c0930e3 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -23,7 +23,6 @@
#include <log/logprint.h>
#include <utils/RefBase.h>
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
namespace android {
namespace os {
@@ -39,6 +38,7 @@
const sp<ConditionWizard>& wizard)
: mStartTimeNs(startTimeNs),
mCurrentBucketStartTimeNs(startTimeNs),
+ mCurrentBucketNum(0),
mCondition(conditionIndex >= 0 ? false : true),
mConditionSliced(false),
mWizard(wizard),
@@ -61,7 +61,7 @@
// TODO: Pass a timestamp as a parameter in onDumpReport and update all its
// implementations.
- virtual StatsLogReport onDumpReport() = 0;
+ virtual std::unique_ptr<std::vector<uint8_t>> onDumpReport() = 0;
virtual bool isConditionSliced() const {
return mConditionSliced;
@@ -74,6 +74,8 @@
uint64_t mCurrentBucketStartTimeNs;
+ uint64_t mCurrentBucketNum;
+
int64_t mBucketSizeNs;
bool mCondition;
@@ -118,7 +120,7 @@
virtual void startNewProtoOutputStream(long long timestamp) = 0;
- std::unique_ptr<uint8_t[]> serializeProto();
+ std::unique_ptr<std::vector<uint8_t>> serializeProto();
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 80b325f..19317ee 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -56,10 +56,10 @@
}
}
-vector<StatsLogReport> MetricsManager::onDumpReport() {
+vector<std::unique_ptr<vector<uint8_t>>> MetricsManager::onDumpReport() {
VLOG("=========================Metric Reports Start==========================");
// one StatsLogReport per MetricProduer
- vector<StatsLogReport> reportList;
+ vector<std::unique_ptr<vector<uint8_t>>> reportList;
for (auto& metric : mAllMetricProducers) {
reportList.push_back(metric->onDumpReport());
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 63e2c33..39c79f9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -44,7 +44,7 @@
void finish();
// Config source owner can call onDumpReport() to get all the metrics collected.
- std::vector<StatsLogReport> onDumpReport();
+ std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
size_t byteSize();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 41b7a5d..2a63073 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -108,7 +108,7 @@
VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
}
-StatsLogReport ValueMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
VLOG("metric %lld dump report now...", mMetric.metric_id());
for (const auto& pair : mPastBuckets) {
@@ -156,15 +156,13 @@
(long long)mCurrentBucketStartTimeNs);
VLOG("metric %lld dump report now...", mMetric.metric_id());
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
mPastBuckets.clear();
mByteSize = 0;
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 8437665..ef9868b 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -21,9 +21,7 @@
#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
-#include "CountAnomalyTracker.h"
#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
@@ -49,7 +47,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
void onSlicedConditionMayChange(const uint64_t eventTime);
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index aeb234d..5c76d0e 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -20,8 +20,6 @@
#include "condition/ConditionWizard.h"
#include "stats_util.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index de53d62..b095884 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -19,8 +19,6 @@
#include "DurationTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index a9507bf..e1d0aceb 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef STATS_UTIL_H
-#define STATS_UTIL_H
+
+#pragma once
#include "logd/LogReader.h"
#include "storage/DropboxWriter.h"
@@ -22,6 +22,8 @@
#include <log/logprint.h>
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include <unordered_map>
+
namespace android {
namespace os {
namespace statsd {
@@ -33,6 +35,10 @@
typedef std::map<std::string, HashableDimensionKey> ConditionKey;
+// TODO: For P, change int to int64_t.
+// TODO: Should HashableDimensionKey be marked here as const?
+typedef std::unordered_map<HashableDimensionKey, int> DimToValMap;
+
EventMetricData parse(log_msg msg);
int getTagId(log_msg msg);
@@ -41,5 +47,3 @@
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // STATS_UTIL_H
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index f3e6894..3b8eeaf 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -117,6 +117,8 @@
optional int32 refractory_period_secs = 4;
optional int64 trigger_if_sum_gt = 5;
+
+ optional int32 refractory_period_in_buckets = 6;
}
message EventMetric {
@@ -168,7 +170,6 @@
repeated Alert alerts = 7;
repeated EventConditionLink links = 8;
-
}
message GaugeMetric {
@@ -235,4 +236,6 @@
repeated LogEntryMatcher log_entry_matcher = 7;
repeated Condition condition = 8;
+
+ repeated Alert alerts = 9;
}
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
new file mode 100644
index 0000000..b8150d0
--- /dev/null
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -0,0 +1,239 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/anomaly/DiscreteAnomalyTracker.h"
+
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void AddValueToBucket(const std::vector<std::pair<string, long>>& key_value_pair_list,
+ std::shared_ptr<DimToValMap> bucket) {
+ for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
+ (*bucket)[itr->first] += itr->second;
+ }
+}
+
+std::shared_ptr<DimToValMap> MockeBucket(
+ const std::vector<std::pair<string, long>>& key_value_pair_list) {
+ std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
+ AddValueToBucket(key_value_pair_list, bucket);
+ return bucket;
+}
+
+TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
+ Alert alert;
+ alert.set_number_of_buckets(3);
+ alert.set_refractory_period_in_buckets(3);
+ alert.set_trigger_if_sum_gt(2);
+
+ DiscreteAnomalyTracker anomaly_tracker(alert);
+
+ std::shared_ptr<DimToValMap> bucket0 = MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+ // Adds bucket #0
+ anomaly_tracker.addOrUpdateBucket(bucket0, 0);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+
+ // Adds bucket #0 again. The sum does not change.
+ anomaly_tracker.addOrUpdateBucket(bucket0, 0);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 0L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+
+ // Adds bucket #1.
+ std::shared_ptr<DimToValMap> bucket1 = MockeBucket({{"b", 2}});
+ anomaly_tracker.addOrUpdateBucket(bucket1, 1);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ // Alarm.
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+ // Adds bucket #1 again. The sum does not change.
+ anomaly_tracker.addOrUpdateBucket(bucket1, 1);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ // Alarm.
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+ // Adds bucket #2.
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 2);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 2L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Within refractory period.
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+ // Adds bucket #3.
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 3);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 3L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+
+ // Adds bucket #3.
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 2}}), 4);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 4L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Within refractory period.
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 5);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 5L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Within refractory period.
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 5L);
+}
+
+TEST(AnomalyTrackerTest, TestSparseBuckets) {
+ Alert alert;
+ alert.set_number_of_buckets(3);
+ alert.set_refractory_period_in_buckets(3);
+ alert.set_trigger_if_sum_gt(2);
+
+ DiscreteAnomalyTracker anomaly_tracker(alert);
+
+ // Add bucket #9
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}}), 9);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 9L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+
+ // Add bucket #16
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 4}}), 16);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 16L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+
+ // Add bucket #18
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ // Within refractory period.
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+
+ // Add bucket #18 again.
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+
+ // Add bucket #20
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 3}, {"d", 1}}), 20);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 20L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+
+ // Add bucket #25
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"d", 1}}), 25);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 25L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1L);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+
+ // Add bucket #28
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"e", 5}}), 28);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 28L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("e")->second, 5L);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 3L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 28L);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index dfd3bbf..26efda1 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -2105,6 +2105,7 @@
break;
case DENSITY_DPI_NONE:
parts.add("nodpi");
+ break;
default:
parts.add(config.densityDpi + "dpi");
break;
diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl
index 5857aae..6bbd99e 100644
--- a/core/java/android/os/ParcelFileDescriptor.aidl
+++ b/core/java/android/os/ParcelFileDescriptor.aidl
@@ -17,4 +17,4 @@
package android.os;
-parcelable ParcelFileDescriptor;
+parcelable ParcelFileDescriptor cpp_header "android/os/parcel_file_descriptor.h";
diff --git a/core/java/android/os/ParcelUuid.aidl b/core/java/android/os/ParcelUuid.aidl
index f7e080a..6f36297 100644
--- a/core/java/android/os/ParcelUuid.aidl
+++ b/core/java/android/os/ParcelUuid.aidl
@@ -16,4 +16,4 @@
package android.os;
-parcelable ParcelUuid;
+parcelable ParcelUuid cpp_header "android/os/parcel_uuid.h";
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index d5820b6..02c82d7 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -16,11 +16,11 @@
package android.os;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityThread;
-import android.app.ApplicationErrorReport;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -75,6 +75,8 @@
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -163,12 +165,14 @@
private static final String CLEARTEXT_PROPERTY = "persist.sys.strictmode.clear";
/**
- * Quick feature-flag that can be used to disable the defaults provided by
- * {@link #initThreadDefaults(ApplicationInfo)} and
- * {@link #initVmDefaults(ApplicationInfo)}.
+ * Quick feature-flag that can be used to disable the defaults provided by {@link
+ * #initThreadDefaults(ApplicationInfo)} and {@link #initVmDefaults(ApplicationInfo)}.
*/
private static final boolean DISABLE = false;
+ // Only apply VM penalties for the same violation at this interval.
+ private static final long MIN_VM_INTERVAL_MS = 1000;
+
// Only log a duplicate stack trace to the logs every second.
private static final long MIN_LOG_INTERVAL_MS = 1000;
@@ -374,6 +378,32 @@
private static volatile ViolationLogger sLogger = LOGCAT_LOGGER;
+ private static final ThreadLocal<OnThreadViolationListener> sThreadViolationListener =
+ new ThreadLocal<>();
+ private static final ThreadLocal<Executor> sThreadViolationExecutor = new ThreadLocal<>();
+
+ /**
+ * When #{@link ThreadPolicy.Builder#penaltyListener} is enabled, the listener is called on the
+ * provided executor when a Thread violation occurs.
+ *
+ * @hide
+ */
+ public interface OnThreadViolationListener {
+ /** Called on a thread policy violation. */
+ void onThreadViolation(Violation v);
+ }
+
+ /**
+ * When #{@link VmPolicy.Builder#penaltyListener} is enabled, the listener is called on the
+ * provided executor when a VM violation occurs.
+ *
+ * @hide
+ */
+ public interface OnVmViolationListener {
+ /** Called on a VM policy violation. */
+ void onVmViolation(Violation v);
+ }
+
/** {@hide} */
@TestApi
public static void setViolationLogger(ViolationLogger listener) {
@@ -403,12 +433,16 @@
*/
public static final class ThreadPolicy {
/** The default, lax policy which doesn't catch anything. */
- public static final ThreadPolicy LAX = new ThreadPolicy(0);
+ public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null);
final int mask;
+ final OnThreadViolationListener mListener;
+ final Executor mCallbackExecutor;
- private ThreadPolicy(int mask) {
+ private ThreadPolicy(int mask, OnThreadViolationListener listener, Executor executor) {
this.mask = mask;
+ mListener = listener;
+ mCallbackExecutor = executor;
}
@Override
@@ -436,6 +470,8 @@
*/
public static final class Builder {
private int mMask = 0;
+ private OnThreadViolationListener mListener;
+ private Executor mExecutor;
/**
* Create a Builder that detects nothing and has no violations. (but note that {@link
@@ -601,6 +637,22 @@
return enable(PENALTY_DROPBOX);
}
+ /**
+ * Call #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified
+ * executor every violation.
+ *
+ * @hide
+ */
+ public Builder penaltyListener(
+ @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+ if (executor == null) {
+ throw new NullPointerException("executor must not be null");
+ }
+ mListener = listener;
+ mExecutor = executor;
+ return this;
+ }
+
private Builder enable(int bit) {
mMask |= bit;
return this;
@@ -620,7 +672,8 @@
public ThreadPolicy build() {
// If there are detection bits set but no violation bits
// set, enable simple logging.
- if (mMask != 0
+ if (mListener == null
+ && mMask != 0
&& (mMask
& (PENALTY_DEATH
| PENALTY_LOG
@@ -629,7 +682,7 @@
== 0) {
penaltyLog();
}
- return new ThreadPolicy(mMask);
+ return new ThreadPolicy(mMask, mListener, mExecutor);
}
}
}
@@ -641,19 +694,27 @@
*/
public static final class VmPolicy {
/** The default, lax policy which doesn't catch anything. */
- public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP);
+ public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null);
final int mask;
+ final OnVmViolationListener mListener;
+ final Executor mCallbackExecutor;
// Map from class to max number of allowed instances in memory.
final HashMap<Class, Integer> classInstanceLimit;
- private VmPolicy(int mask, HashMap<Class, Integer> classInstanceLimit) {
+ private VmPolicy(
+ int mask,
+ HashMap<Class, Integer> classInstanceLimit,
+ OnVmViolationListener listener,
+ Executor executor) {
if (classInstanceLimit == null) {
throw new NullPointerException("classInstanceLimit == null");
}
this.mask = mask;
this.classInstanceLimit = classInstanceLimit;
+ mListener = listener;
+ mCallbackExecutor = executor;
}
@Override
@@ -681,6 +742,8 @@
*/
public static final class Builder {
private int mMask;
+ private OnVmViolationListener mListener;
+ private Executor mExecutor;
private HashMap<Class, Integer> mClassInstanceLimit; // null until needed
private boolean mClassInstanceLimitNeedCow = false; // need copy-on-write
@@ -694,6 +757,8 @@
mMask = base.mask;
mClassInstanceLimitNeedCow = true;
mClassInstanceLimit = base.classInstanceLimit;
+ mListener = base.mListener;
+ mExecutor = base.mCallbackExecutor;
}
/**
@@ -910,6 +975,21 @@
return enable(PENALTY_DROPBOX);
}
+ /**
+ * Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
+ *
+ * @hide
+ */
+ public Builder penaltyListener(
+ @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+ if (executor == null) {
+ throw new NullPointerException("executor must not be null");
+ }
+ mListener = listener;
+ mExecutor = executor;
+ return this;
+ }
+
private Builder enable(int bit) {
mMask |= bit;
return this;
@@ -929,7 +1009,8 @@
public VmPolicy build() {
// If there are detection bits set but no violation bits
// set, enable simple logging.
- if (mMask != 0
+ if (mListener == null
+ && mMask != 0
&& (mMask
& (PENALTY_DEATH
| PENALTY_LOG
@@ -940,7 +1021,9 @@
}
return new VmPolicy(
mMask,
- mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP);
+ mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP,
+ mListener,
+ mExecutor);
}
}
}
@@ -973,6 +1056,8 @@
*/
public static void setThreadPolicy(final ThreadPolicy policy) {
setThreadPolicyMask(policy.mask);
+ sThreadViolationListener.set(policy.mListener);
+ sThreadViolationExecutor.set(policy.mCallbackExecutor);
}
/** @hide */
@@ -1029,7 +1114,10 @@
// introduce VmPolicy cleanly) but this isn't particularly
// optimal for users who might call this method often. This
// should be in a thread-local and not allocate on each call.
- return new ThreadPolicy(getThreadPolicyMask());
+ return new ThreadPolicy(
+ getThreadPolicyMask(),
+ sThreadViolationListener.get(),
+ sThreadViolationExecutor.get());
}
/**
@@ -1042,7 +1130,10 @@
* end of a block
*/
public static ThreadPolicy allowThreadDiskWrites() {
- return new ThreadPolicy(allowThreadDiskWritesMask());
+ return new ThreadPolicy(
+ allowThreadDiskWritesMask(),
+ sThreadViolationListener.get(),
+ sThreadViolationExecutor.get());
}
/** @hide */
@@ -1063,7 +1154,10 @@
* @return the old policy, to be passed to setThreadPolicy to restore the policy.
*/
public static ThreadPolicy allowThreadDiskReads() {
- return new ThreadPolicy(allowThreadDiskReadsMask());
+ return new ThreadPolicy(
+ allowThreadDiskReadsMask(),
+ sThreadViolationListener.get(),
+ sThreadViolationExecutor.get());
}
/** @hide */
@@ -1076,16 +1170,27 @@
return oldPolicyMask;
}
+ private static ThreadPolicy allowThreadViolations() {
+ ThreadPolicy oldPolicy = getThreadPolicy();
+ setThreadPolicyMask(0);
+ return oldPolicy;
+ }
+
+ private static VmPolicy allowVmViolations() {
+ VmPolicy oldPolicy = getVmPolicy();
+ sVmPolicy = VmPolicy.LAX;
+ return oldPolicy;
+ }
+
/**
- * Determine if the given app is "bundled" as part of the system image.
- * These bundled apps are developed in lock-step with the OS, and they
- * aren't updated outside of an OTA, so we want to chase any
- * {@link StrictMode} regressions by enabling detection when running on
- * {@link Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds.
- * <p>
- * Unbundled apps included in the system image are expected to detect and
- * triage their own {@link StrictMode} issues separate from the OS release
- * process, which is why we don't enable them here.
+ * Determine if the given app is "bundled" as part of the system image. These bundled apps are
+ * developed in lock-step with the OS, and they aren't updated outside of an OTA, so we want to
+ * chase any {@link StrictMode} regressions by enabling detection when running on {@link
+ * Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds.
+ *
+ * <p>Unbundled apps included in the system image are expected to detect and triage their own
+ * {@link StrictMode} issues separate from the OS release process, which is why we don't enable
+ * them here.
*
* @hide
*/
@@ -1122,8 +1227,8 @@
*/
public static void initThreadDefaults(ApplicationInfo ai) {
final ThreadPolicy.Builder builder = new ThreadPolicy.Builder();
- final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion
- : Build.VERSION_CODES.CUR_DEVELOPMENT;
+ final int targetSdkVersion =
+ (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
// Starting in HC, we don't allow network usage on the main thread
if (targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
@@ -1162,8 +1267,8 @@
*/
public static void initVmDefaults(ApplicationInfo ai) {
final VmPolicy.Builder builder = new VmPolicy.Builder();
- final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion
- : Build.VERSION_CODES.CUR_DEVELOPMENT;
+ final int targetSdkVersion =
+ (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
// Starting in N, we don't allow file:// Uri exposure
if (targetSdkVersion >= Build.VERSION_CODES.N) {
@@ -1204,7 +1309,9 @@
sVmPolicy.mask
| DETECT_VM_FILE_URI_EXPOSURE
| PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
- sVmPolicy.classInstanceLimit);
+ sVmPolicy.classInstanceLimit,
+ sVmPolicy.mListener,
+ sVmPolicy.mCallbackExecutor);
}
/**
@@ -1219,7 +1326,9 @@
sVmPolicy.mask
& ~(DETECT_VM_FILE_URI_EXPOSURE
| PENALTY_DEATH_ON_FILE_URI_EXPOSURE),
- sVmPolicy.classInstanceLimit);
+ sVmPolicy.classInstanceLimit,
+ sVmPolicy.mListener,
+ sVmPolicy.mCallbackExecutor);
}
/**
@@ -1409,7 +1518,7 @@
// go into this immediate mode?
if (looper == null || (info.mPolicy & THREAD_PENALTY_MASK) == PENALTY_DEATH) {
info.durationMillis = -1; // unknown (redundant, already set)
- handleViolation(info);
+ onThreadPolicyViolation(info);
return;
}
@@ -1447,30 +1556,28 @@
THREAD_HANDLER
.get()
.postAtFrontOfQueue(
- new Runnable() {
- public void run() {
- long loopFinishTime = SystemClock.uptimeMillis();
+ () -> {
+ long loopFinishTime = SystemClock.uptimeMillis();
- // Note: we do this early, before handling the
- // violation below, as handling the violation
- // may include PENALTY_DEATH and we don't want
- // to keep the red border on.
- if (windowManager != null) {
- try {
- windowManager.showStrictModeViolation(false);
- } catch (RemoteException unused) {
- }
+ // Note: we do this early, before handling the
+ // violation below, as handling the violation
+ // may include PENALTY_DEATH and we don't want
+ // to keep the red border on.
+ if (windowManager != null) {
+ try {
+ windowManager.showStrictModeViolation(false);
+ } catch (RemoteException unused) {
}
-
- for (int n = 0; n < records.size(); ++n) {
- ViolationInfo v = records.get(n);
- v.violationNumThisLoop = n + 1;
- v.durationMillis =
- (int) (loopFinishTime - v.violationUptimeMillis);
- handleViolation(v);
- }
- records.clear();
}
+
+ for (int n = 0; n < records.size(); ++n) {
+ ViolationInfo v = records.get(n);
+ v.violationNumThisLoop = n + 1;
+ v.durationMillis =
+ (int) (loopFinishTime - v.violationUptimeMillis);
+ onThreadPolicyViolation(v);
+ }
+ records.clear();
});
}
@@ -1479,13 +1586,13 @@
// violation fired and now (after the violating code ran) due
// to people who push/pop temporary policy in regions of code,
// hence the policy being passed around.
- void handleViolation(final ViolationInfo info) {
- if (LOG_V) Log.d(TAG, "handleViolation; policy=" + info.mPolicy);
+ void onThreadPolicyViolation(final ViolationInfo info) {
+ if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; policy=" + info.mPolicy);
if (info.penaltyEnabled(PENALTY_GATHER)) {
ArrayList<ViolationInfo> violations = gatheredViolations.get();
if (violations == null) {
- violations = new ArrayList<ViolationInfo>(1);
+ violations = new ArrayList<>(1);
gatheredViolations.set(violations);
}
for (ViolationInfo previous : violations) {
@@ -1519,6 +1626,8 @@
sLogger.log(info);
}
+ final Violation violation = info.mViolation;
+
// The violationMaskSubset, passed to ActivityManager, is a
// subset of the original StrictMode policy bitmask, with
// only the bit violated and penalty bits to be executed
@@ -1552,15 +1661,32 @@
}
if ((info.getPolicyMask() & PENALTY_DEATH) != 0) {
- executeDeathPenalty(info);
+ throw new RuntimeException("StrictMode ThreadPolicy violation", violation);
+ }
+
+ // penaltyDeath will cause penaltyCallback to no-op since we cannot guarantee the
+ // executor finishes before crashing.
+ final OnThreadViolationListener listener = sThreadViolationListener.get();
+ final Executor executor = sThreadViolationExecutor.get();
+ if (listener != null && executor != null) {
+ try {
+ executor.execute(
+ () -> {
+ // Lift violated policy to prevent infinite recursion.
+ ThreadPolicy oldPolicy = allowThreadViolations();
+ try {
+ listener.onThreadViolation(violation);
+ } finally {
+ setThreadPolicy(oldPolicy);
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ Log.e(TAG, "ThreadPolicy penaltyCallback failed", e);
+ }
}
}
}
- private static void executeDeathPenalty(ViolationInfo info) {
- throw new RuntimeException("StrictMode death penalty", info.mViolation);
- }
-
/**
* In the common case, as set by conditionallyEnableDebugLogging, we're just dropboxing any
* violations but not showing a dialog, not loggging, and not killing the process. In these
@@ -1736,9 +1862,8 @@
* #setThreadPolicy}.
*/
public static void enableDefaults() {
- StrictMode.setThreadPolicy(
- new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
+ setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+ setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}
/** @hide */
@@ -1857,11 +1982,11 @@
}
/** @hide */
- public static void onVmPolicyViolation(Violation originStack, boolean forceDeath) {
+ public static void onVmPolicyViolation(Violation violation, boolean forceDeath) {
final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0;
final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath;
final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0;
- final ViolationInfo info = new ViolationInfo(originStack, sVmPolicy.mask);
+ final ViolationInfo info = new ViolationInfo(violation, sVmPolicy.mask);
// Erase stuff not relevant for process-wide violations
info.numAnimationsRunning = 0;
@@ -1870,37 +1995,37 @@
final Integer fingerprint = info.hashCode();
final long now = SystemClock.uptimeMillis();
- long lastViolationTime = 0;
+ long lastViolationTime;
long timeSinceLastViolationMillis = Long.MAX_VALUE;
synchronized (sLastVmViolationTime) {
if (sLastVmViolationTime.containsKey(fingerprint)) {
lastViolationTime = sLastVmViolationTime.get(fingerprint);
timeSinceLastViolationMillis = now - lastViolationTime;
}
- if (timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
+ if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
sLastVmViolationTime.put(fingerprint, now);
}
}
-
- if (penaltyLog && sLogger != null) {
- sLogger.log(info);
+ if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) {
+ // Rate limit all penalties.
+ return;
}
- if (penaltyLog && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
+
+ if (penaltyLog && sLogger != null && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
sLogger.log(info);
}
int violationMaskSubset = PENALTY_DROPBOX | (ALL_VM_DETECT_BITS & sVmPolicy.mask);
- if (penaltyDropbox && !penaltyDeath) {
- // Common case for userdebug/eng builds. If no death and
- // just dropboxing, we can do the ActivityManager call
- // asynchronously.
- dropboxViolationAsync(violationMaskSubset, info);
- return;
- }
-
- if (penaltyDropbox && lastViolationTime == 0) {
- handleApplicationStrictModeViolation(violationMaskSubset, info);
+ if (penaltyDropbox) {
+ if (penaltyDeath) {
+ handleApplicationStrictModeViolation(violationMaskSubset, info);
+ } else {
+ // Common case for userdebug/eng builds. If no death and
+ // just dropboxing, we can do the ActivityManager call
+ // asynchronously.
+ dropboxViolationAsync(violationMaskSubset, info);
+ }
}
if (penaltyDeath) {
@@ -1908,6 +2033,26 @@
Process.killProcess(Process.myPid());
System.exit(10);
}
+
+ // If penaltyDeath, we can't guarantee this callback finishes before the process dies for
+ // all executors. penaltyDeath supersedes penaltyCallback.
+ if (sVmPolicy.mListener != null && sVmPolicy.mCallbackExecutor != null) {
+ final OnVmViolationListener listener = sVmPolicy.mListener;
+ try {
+ sVmPolicy.mCallbackExecutor.execute(
+ () -> {
+ // Lift violated policy to prevent infinite recursion.
+ VmPolicy oldPolicy = allowVmViolations();
+ try {
+ listener.onVmViolation(violation);
+ } finally {
+ setVmPolicy(oldPolicy);
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ Log.e(TAG, "VmPolicy penaltyCallback failed", e);
+ }
+ }
}
/** Called from Parcel.writeNoException() */
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 0c2e4b7..cd233b8 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -97,6 +97,10 @@
public static final String ACTION_RESOLVE_NO_PRIVILEGES =
"android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
+ /** Ask the user to input carrier confirmation code. */
+ public static final String ACTION_RESOLVE_CONFIRMATION_CODE =
+ "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
+
/** Intent extra set for resolution requests containing the package name of the calling app. */
public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
"android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
@@ -105,6 +109,8 @@
public static final int RESULT_OK = 0;
/** Result code indicating that an active SIM must be deactivated to perform the operation. */
public static final int RESULT_MUST_DEACTIVATE_SIM = -1;
+ /** Result code indicating that the user must input a carrier confirmation code. */
+ public static final int RESULT_NEED_CONFIRMATION_CODE = -2;
// New predefined codes should have negative values.
/** Start of implementation-specific error results. */
@@ -119,10 +125,13 @@
RESOLUTION_ACTIONS = new ArraySet<>();
RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
+ RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE);
}
/** Boolean extra for resolution actions indicating whether the user granted consent. */
public static final String RESOLUTION_EXTRA_CONSENT = "consent";
+ /** String extra for resolution actions indicating the carrier confirmation code. */
+ public static final String RESOLUTION_EXTRA_CONFIRMATION_CODE = "confirmation_code";
private final IEuiccService.Stub mStubWrapper;
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 3e992ec..080482b 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.Service;
import android.content.ComponentName;
@@ -56,6 +57,8 @@
* </meta-data>
* </service></pre>
*
+ * <p> Condition providers cannot be bound by the system on
+ * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
*/
public abstract class ConditionProviderService extends Service {
private final String TAG = ConditionProviderService.class.getSimpleName()
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 08d3118..dac663e7 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -21,6 +21,7 @@
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.Notification.Builder;
@@ -82,6 +83,8 @@
* method is the <i>only</i> one that is safe to call before {@link #onListenerConnected()}
* or after {@link #onListenerDisconnected()}.
* </p>
+ * <p> Notification listeners cannot get notification access or be bound by the system on
+ * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
*/
public abstract class NotificationListenerService extends Service {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c043dca..e12c0b0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14218,6 +14218,7 @@
*/
public void setScaleX(float scaleX) {
if (scaleX != getScaleX()) {
+ requireIsFinite(scaleX, "scaleX");
invalidateViewProperty(true, false);
mRenderNode.setScaleX(scaleX);
invalidateViewProperty(false, true);
@@ -14254,6 +14255,7 @@
*/
public void setScaleY(float scaleY) {
if (scaleY != getScaleY()) {
+ requireIsFinite(scaleY, "scaleY");
invalidateViewProperty(true, false);
mRenderNode.setScaleY(scaleY);
invalidateViewProperty(false, true);
@@ -14803,6 +14805,15 @@
}
}
+ private static void requireIsFinite(float transform, String propertyName) {
+ if (Float.isNaN(transform)) {
+ throw new IllegalArgumentException("Cannot set '" + propertyName + "' to Float.NaN");
+ }
+ if (Float.isInfinite(transform)) {
+ throw new IllegalArgumentException("Cannot set '" + propertyName + "' to infinity");
+ }
+ }
+
/**
* The visual x position of this view, in pixels. This is equivalent to the
* {@link #setTranslationX(float) translationX} property plus the current
@@ -14889,6 +14900,7 @@
*/
public void setElevation(float elevation) {
if (elevation != getElevation()) {
+ requireIsFinite(elevation, "elevation");
invalidateViewProperty(true, false);
mRenderNode.setElevation(elevation);
invalidateViewProperty(false, true);
@@ -14981,6 +14993,7 @@
*/
public void setTranslationZ(float translationZ) {
if (translationZ != getTranslationZ()) {
+ requireIsFinite(translationZ, "translationZ");
invalidateViewProperty(true, false);
mRenderNode.setTranslationZ(translationZ);
invalidateViewProperty(false, true);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f7e0b46..161a0c2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -258,6 +258,11 @@
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.settings.intelligence">
+ <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.sharedstoragebackup">
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 5484cf0..251b2e7 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -98,6 +98,9 @@
enabled: true,
},
},
+ sanitize: {
+ blacklist: "libandroidfw_blacklist.txt",
+ },
}
common_test_libs = [
diff --git a/libs/androidfw/libandroidfw_blacklist.txt b/libs/androidfw/libandroidfw_blacklist.txt
new file mode 100644
index 0000000..dd17e4d
--- /dev/null
+++ b/libs/androidfw/libandroidfw_blacklist.txt
@@ -0,0 +1 @@
+src:*/ResourceTypes.cpp
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index b7f1fb2..530e82e 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -31,11 +31,7 @@
namespace uirenderer {
Extensions::Extensions() {
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- // Extensions class is used only by OpenGL and SkiaGL pipelines
- // The code below will crash for SkiaVulkan, because OpenGL is not initialized
- // TODO: instantiate Extensions class only for OpenGL pipeline
- // TODO: remove the only usage of Extensions by SkiaGL in SkiaOpenGLReadback::copyImageInto
+ if (Properties::isSkiaEnabled()) {
return;
}
const char* version = (const char*)glGetString(GL_VERSION);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 6db57ca..288d039 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -66,14 +66,8 @@
break;
}
- /* Ideally, we would call grContext->caps()->isConfigRenderable(...). We
- * currently can't do that since some devices (i.e. SwiftShader) supports all
- * the appropriate half float extensions, but only allow the buffer to be read
- * back as full floats. We can relax this extension if Skia implements support
- * for reading back float buffers (skbug.com/6945).
- */
if (pixelConfig == kRGBA_half_GrPixelConfig &&
- !DeviceInfo::get()->extensions().hasRenderableFloatTextures()) {
+ !grContext->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false)) {
ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
return CopyResult::DestinationInvalid;
}
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index fa50943..478297f 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -80,6 +80,9 @@
// The minimum allowed max packet size is 255 according to the OBEX specification
public static final int LOWER_LIMIT_MAX_PACKET_SIZE = 255;
+ // The length of OBEX Byte Sequency Header Id according to the OBEX specification
+ public static final int OBEX_BYTE_SEQ_HEADER_LEN = 0x03;
+
/**
* Temporary workaround to be able to push files to Windows 7.
* TODO: Should be removed as soon as Microsoft updates their driver.
@@ -205,12 +208,15 @@
case 0x40:
boolean trimTail = true;
index++;
- length = 0xFF & headerArray[index];
- length = length << 8;
- index++;
- length += 0xFF & headerArray[index];
- length -= 3;
- index++;
+ length = ((0xFF & headerArray[index]) << 8) +
+ (0xFF & headerArray[index + 1]);
+ index += 2;
+ if (length <= OBEX_BYTE_SEQ_HEADER_LEN) {
+ Log.e(TAG, "Remote sent an OBEX packet with " +
+ "incorrect header length = " + length);
+ break;
+ }
+ length -= OBEX_BYTE_SEQ_HEADER_LEN;
value = new byte[length];
System.arraycopy(headerArray, index, value, 0, length);
if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d3dcf5a..8e93d19 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4680,6 +4680,16 @@
// OS: P
DIALOG_FIRMWARE_VERSION = 1247;
+ // OPEN: Settings > Network & internet > Menu > Private DNS
+ // CATEGORY: SETTINGS
+ // OS: P
+ DIALOG_PRIVATE_DNS = 1248;
+
+ // ACTION: A private dns mode been selected by user
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACTION_PRIVATE_DNS_MODE = 1249;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e609bd7..831c9cb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1356,31 +1356,6 @@
}
}
- public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
- if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) {
- return;
- }
-
- synchronized (mRecords) {
- for (Record r : mRecords) {
- if (VDBG) {
- log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId);
- }
- if ((r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) &&
- ((r.subId == subId) ||
- (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) {
- try {
- r.callback.onOemHookRawEvent(rawData);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
- }
- }
- handleRemoveListLocked();
- }
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1673,11 +1648,6 @@
android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
}
-
- if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
- }
}
private void handleRemoveListLocked() {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index caf85b0..7837940 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -8084,28 +8084,22 @@
private int updateLightNavigationBarLw(int vis, WindowState opaque,
WindowState opaqueOrDimming) {
final WindowState imeWin = mWindowManagerFuncs.getInputMethodWindowLw();
- final boolean isImeShownWithBottomNavBar =
- imeWin != null && imeWin.isVisibleLw() && mNavigationBarPosition == NAV_BAR_BOTTOM;
- if (isImeShownWithBottomNavBar) {
- final int winFlags = PolicyControl.getWindowFlags(imeWin, null);
- if ((winFlags & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
- // If the IME window is visible and explicitly requesting custom nav bar background
- // rendering, respect its light flag.
- vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- vis |= PolicyControl.getSystemUiVisibility(imeWin, null)
- & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- return vis;
- }
+
+ final WindowState navColorWin;
+ if (imeWin != null && imeWin.isVisibleLw() && mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ navColorWin = imeWin;
+ } else {
+ navColorWin = opaqueOrDimming;
}
- if (opaqueOrDimming != null) {
- if (opaqueOrDimming == opaque) {
- // If the top fullscreen-or-dimming window is also the top fullscreen window,
- // respect its light flag.
+ if (navColorWin != null) {
+ if (navColorWin == opaque) {
+ // If the top fullscreen-or-dimming window is also the top fullscreen, respect
+ // its light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- vis |= PolicyControl.getSystemUiVisibility(opaqueOrDimming, null)
+ vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
& View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- } else if (opaqueOrDimming.isDimming() || isImeShownWithBottomNavBar) {
+ } else if (navColorWin.isDimming() || navColorWin == imeWin) {
// Otherwise if it's dimming or it's the IME window, clear the light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 061e55a..0030ab6 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1650,6 +1650,26 @@
public static final String KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL =
"identify_high_definition_calls_in_call_log_bool";
+ /**
+ * Flag specifying whether to use the {@link ServiceState} roaming status, which can be
+ * affected by other carrier configs (e.g.
+ * {@link #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY}), when setting the SPN display.
+ * <p>
+ * If {@code true}, the SPN display uses {@link ServiceState#getRoaming}.
+ * If {@code false} the SPN display checks if the current MCC/MNC is different from the
+ * SIM card's MCC/MNC.
+ *
+ * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_NON_ROAMING_OPERATOR_STRING_ARRAY
+ * @see KEY_ROAMING_OPERATOR_STRING_ARRAY
+ * @see KEY_FORCE_HOME_NETWORK_BOOL
+ *
+ * @hide
+ */
+ public static final String KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL =
+ "spn_display_rule_use_roaming_from_service_state_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1928,6 +1948,7 @@
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
+ sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index afff6d5..9ccfa94 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -204,16 +204,6 @@
public static final int LISTEN_VOLTE_STATE = 0x00004000;
/**
- * Listen for OEM hook raw event
- *
- * @see #onOemHookRawEvent
- * @hide
- * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
- */
- @Deprecated
- public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
-
- /**
* Listen for carrier network changes indicated by a carrier app.
*
* @see #onCarrierNetworkRequest
@@ -359,9 +349,6 @@
case LISTEN_DATA_ACTIVATION_STATE:
PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
break;
- case LISTEN_OEM_HOOK_RAW_EVENT:
- PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
- break;
case LISTEN_CARRIER_NETWORK_CHANGE:
PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
break;
@@ -556,16 +543,6 @@
}
/**
- * Callback invoked when OEM hook raw event is received. Requires
- * the READ_PRIVILEGED_PHONE_STATE permission.
- * @param rawData is the byte array of the OEM hook raw data.
- * @hide
- */
- public void onOemHookRawEvent(byte[] rawData) {
- // default implementation empty
- }
-
- /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -677,10 +654,6 @@
send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
}
- public void onOemHookRawEvent(byte[] rawData) {
- send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
- }
-
public void onCarrierNetworkChange(boolean active) {
send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 42c3de5..4ffb3c3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5709,29 +5709,6 @@
return retVal;
}
- /**
- * Returns the result and response from RIL for oem request
- *
- * @param oemReq the data is sent to ril.
- * @param oemResp the respose data from RIL.
- * @return negative value request was not handled or get error
- * 0 request was handled succesfully, but no response data
- * positive value success, data length of response
- * @hide
- * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
- */
- @Deprecated
- public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.invokeOemRilRequestRaw(oemReq, oemResp);
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- }
- return -1;
- }
-
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index b5484e34..01041c8 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -53,6 +53,8 @@
@Nullable
public final String encodedActivationCode;
+ @Nullable private String confirmationCode;
+
// see getCarrierName and setCarrierName
@Nullable
private String carrierName;
@@ -66,6 +68,7 @@
private DownloadableSubscription(Parcel in) {
encodedActivationCode = in.readString();
+ confirmationCode = in.readString();
carrierName = in.readString();
accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
}
@@ -83,6 +86,21 @@
}
/**
+ * Sets the confirmation code.
+ */
+ public void setConfirmationCode(String confirmationCode) {
+ this.confirmationCode = confirmationCode;
+ }
+
+ /**
+ * Returns the confirmation code.
+ */
+ @Nullable
+ public String getConfirmationCode() {
+ return confirmationCode;
+ }
+
+ /**
* Set the user-visible carrier name.
* @hide
*
@@ -134,6 +152,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(encodedActivationCode);
+ dest.writeString(confirmationCode);
dest.writeString(carrierName);
dest.writeTypedArray(accessRules, flags);
}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index e9c5461..ac16139 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -45,7 +45,6 @@
void onVoLteServiceStateChanged(in VoLteServiceState lteState);
void onVoiceActivationStateChanged(int activationState);
void onDataActivationStateChanged(int activationState);
- void onOemHookRawEvent(in byte[] rawData);
void onCarrierNetworkChange(in boolean active);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2ac11b5..3cc9bde 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1000,17 +1000,6 @@
in List<String> cdmaNonRoamingList);
/**
- * Returns the result and response from RIL for oem request
- *
- * @param oemReq the data is sent to ril.
- * @param oemResp the respose data from RIL.
- * @return negative value request was not handled or get error
- * 0 request was handled succesfully, but no response data
- * positive value success, data length of response
- */
- int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
-
- /**
* Check if any mobile Radios need to be shutdown.
*
* @return true is any mobile radio needs to be shutdown
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 2c2206c..75d8f3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -67,7 +67,6 @@
void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
int activationState, int activationType);
- void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
void notifySubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
}