Merge "Add command to clear puller cache"
diff --git a/api/current.txt b/api/current.txt
index bbe3c49..4fb9e0a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -21946,6 +21946,7 @@
field public static final int ENCODING_DTS = 7; // 0x7
field public static final int ENCODING_DTS_HD = 8; // 0x8
field public static final int ENCODING_E_AC3 = 6; // 0x6
+ field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
field public static final int ENCODING_IEC61937 = 13; // 0xd
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_MP3 = 9; // 0x9
@@ -22150,6 +22151,20 @@
field public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
}
+ public final class AudioPresentation {
+ method public java.util.Map<java.util.Locale, java.lang.String> getLabels();
+ method public java.util.Locale getLocale();
+ method public int getMasteringIndication();
+ method public boolean hasAudioDescription();
+ method public boolean hasDialogueEnhancement();
+ method public boolean hasSpokenSubtitles();
+ field public static final int MASTERED_FOR_3D = 3; // 0x3
+ field public static final int MASTERED_FOR_HEADPHONE = 4; // 0x4
+ field public static final int MASTERED_FOR_STEREO = 1; // 0x1
+ field public static final int MASTERED_FOR_SURROUND = 2; // 0x2
+ field public static final int MASTERING_NOT_INDICATED = 0; // 0x0
+ }
+
public class AudioRecord implements android.media.AudioRouting {
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -22316,6 +22331,7 @@
method public int setPlaybackRate(int);
method public int setPositionNotificationPeriod(int);
method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
+ method public int setPresentation(android.media.AudioPresentation);
method protected deprecated void setState(int);
method public deprecated int setStereoVolume(float, float);
method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
@@ -23472,6 +23488,7 @@
ctor public MediaExtractor();
method public boolean advance();
method protected void finalize();
+ method public java.util.List<android.media.AudioPresentation> getAudioPresentations(int);
method public long getCachedDuration();
method public android.media.MediaExtractor.CasInfo getCasInfo(int);
method public android.media.DrmInitData getDrmInitData();
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index f75678b..6e0bd3a 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -26,6 +26,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -34,6 +35,7 @@
import libcore.io.Streams;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -583,7 +585,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
- Streams.copy(new FileInputStream(fd.getFileDescriptor()), System.out);
+ FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
}
}
}
@@ -596,7 +598,7 @@
@Override
public void onExecute(IContentProvider provider) throws Exception {
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
- Streams.copy(System.in, new FileOutputStream(fd.getFileDescriptor()));
+ FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
}
}
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index cc18227..b0019ac 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -38,6 +38,7 @@
src/external/StatsPuller.cpp \
src/external/StatsCompanionServicePuller.cpp \
src/external/SubsystemSleepStatePuller.cpp \
+ src/external/ResourceHealthManagerPuller.cpp \
src/external/CpuTimePerUidPuller.cpp \
src/external/CpuTimePerUidFreqPuller.cpp \
src/external/KernelUidCpuActiveTimeReader.cpp \
@@ -77,7 +78,8 @@
$(LOCAL_PATH)/../../core/java
statsd_common_static_libraries := \
- libplatformprotos
+ libhealthhalutils \
+ libplatformprotos \
statsd_common_shared_libraries := \
libbase \
@@ -95,6 +97,7 @@
libhidlbase \
libhidltransport \
libhwbinder \
+ android.hardware.health@2.0 \
android.hardware.power@1.0 \
android.hardware.power@1.1 \
libmemunreachable
@@ -167,6 +170,7 @@
LOCAL_SRC_FILES := \
$(statsd_common_src) \
+ tests/dimension_test.cpp \
tests/AnomalyMonitor_test.cpp \
tests/anomaly/AnomalyTracker_test.cpp \
tests/ConfigManager_test.cpp \
@@ -192,7 +196,8 @@
tests/e2e/WakelockDuration_e2e_test.cpp \
tests/e2e/MetricConditionLink_e2e_test.cpp \
tests/e2e/Attribution_e2e_test.cpp \
- tests/e2e/GaugeMetric_e2e_test.cpp
+ tests/e2e/GaugeMetric_e2e_test.cpp \
+ tests/e2e/DimensionInCondition_e2e_test.cpp
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 857a6dd..f0eaeff 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -67,12 +67,16 @@
return hashDimensionsValue(0, value);
}
+android::hash_t hashMetricDimensionKey(int64_t seed, const MetricDimensionKey& dimensionKey) {
+ android::hash_t hash = seed;
+ hash = android::JenkinsHashMix(hash, std::hash<MetricDimensionKey>{}(dimensionKey));
+ return JenkinsHashWhiten(hash);
+}
+
using std::string;
string HashableDimensionKey::toString() const {
- string flattened;
- DimensionsValueToString(getDimensionsValue(), &flattened);
- return flattened;
+ return DimensionsValueToString(getDimensionsValue());
}
bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
@@ -162,6 +166,22 @@
return LessThan(getDimensionsValue(), that.getDimensionsValue());
};
+string MetricDimensionKey::toString() const {
+ string flattened = mDimensionKeyInWhat.toString();
+ flattened += mDimensionKeyInCondition.toString();
+ return flattened;
+}
+
+bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
+ return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
+ mDimensionKeyInCondition == that.getDimensionKeyInCondition();
+};
+
+bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
+ return toString().compare(that.toString()) < 0;
+};
+
+
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 85c317f..a31d7a6 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -41,6 +41,10 @@
return mDimensionsValue;
}
+ inline DimensionsValue* getMutableDimensionsValue() {
+ return &mDimensionsValue;
+ }
+
bool operator==(const HashableDimensionKey& that) const;
bool operator<(const HashableDimensionKey& that) const;
@@ -53,8 +57,52 @@
DimensionsValue mDimensionsValue;
};
+class MetricDimensionKey {
+ public:
+ explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat,
+ const HashableDimensionKey& dimensionKeyInCondition)
+ : mDimensionKeyInWhat(dimensionKeyInWhat),
+ mDimensionKeyInCondition(dimensionKeyInCondition) {};
+
+ MetricDimensionKey(){};
+
+ MetricDimensionKey(const MetricDimensionKey& that)
+ : mDimensionKeyInWhat(that.getDimensionKeyInWhat()),
+ mDimensionKeyInCondition(that.getDimensionKeyInCondition()) {};
+
+ MetricDimensionKey& operator=(const MetricDimensionKey& from) = default;
+
+ std::string toString() const;
+
+ inline const HashableDimensionKey& getDimensionKeyInWhat() const {
+ return mDimensionKeyInWhat;
+ }
+
+ inline const HashableDimensionKey& getDimensionKeyInCondition() const {
+ return mDimensionKeyInCondition;
+ }
+
+ bool hasDimensionKeyInCondition() const {
+ return mDimensionKeyInCondition.getDimensionsValue().has_field();
+ }
+
+ bool operator==(const MetricDimensionKey& that) const;
+
+ bool operator<(const MetricDimensionKey& that) const;
+
+ inline const char* c_str() const {
+ return toString().c_str();
+ }
+ private:
+ HashableDimensionKey mDimensionKeyInWhat;
+ HashableDimensionKey mDimensionKeyInCondition;
+};
+
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2);
+
android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value);
android::hash_t hashDimensionsValue(const DimensionsValue& value);
+android::hash_t hashMetricDimensionKey(int64_t see, const MetricDimensionKey& dimensionKey);
} // namespace statsd
} // namespace os
@@ -63,6 +111,7 @@
namespace std {
using android::os::statsd::HashableDimensionKey;
+using android::os::statsd::MetricDimensionKey;
template <>
struct hash<HashableDimensionKey> {
@@ -71,4 +120,14 @@
}
};
-} // namespace std
+template <>
+struct hash<MetricDimensionKey> {
+ std::size_t operator()(const MetricDimensionKey& key) const {
+ android::hash_t hash = hashDimensionsValue(
+ key.getDimensionKeyInWhat().getDimensionsValue());
+ hash = android::JenkinsHashMix(hash,
+ hashDimensionsValue(key.getDimensionKeyInCondition().getDimensionsValue()));
+ return android::JenkinsHashWhiten(hash);
+ }
+};
+} // namespace std
\ No newline at end of file
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index c19ff63..7642aafa 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -104,7 +104,10 @@
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
-
+ FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
};
} // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index ded6c4c..c84a5b4 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -96,7 +96,7 @@
}
}
-void AnomalyTracker::addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
const int64_t& bucketNum) {
flushPastBuckets(bucketNum);
@@ -147,7 +147,7 @@
}
}
-int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key,
+int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
const int64_t& bucketNum) const {
const auto& bucket = mPastBuckets[index(bucketNum)];
if (bucket == nullptr) {
@@ -157,7 +157,7 @@
return itr == bucket->end() ? 0 : itr->second;
}
-int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) const {
+int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const {
const auto& itr = mSumOverPastBuckets.find(key);
if (itr != mSumOverPastBuckets.end()) {
return itr->second;
@@ -165,7 +165,7 @@
return 0;
}
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key,
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const MetricDimensionKey& key,
const int64_t& currentBucketValue) {
if (currentBucketNum > mMostRecentBucketNum + 1) {
// TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this.
@@ -175,7 +175,7 @@
&& getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
}
-void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key) {
+void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key) {
// TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now.
if (isInRefractoryPeriod(timestampNs, key)) {
VLOG("Skipping anomaly declaration since within refractory period");
@@ -199,14 +199,14 @@
StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
- // TODO: This should also take in the const HashableDimensionKey& key?
+ // TODO: This should also take in the const MetricDimensionKey& key?
android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
mConfigKey.GetId(), mAlert.id());
}
void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs,
const int64_t& currBucketNum,
- const HashableDimensionKey& key,
+ const MetricDimensionKey& key,
const int64_t& currentBucketValue) {
if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
declareAnomaly(timestampNs, key);
@@ -214,7 +214,7 @@
}
bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
- const HashableDimensionKey& key) {
+ const MetricDimensionKey& key) {
const auto& it = mRefractoryPeriodEndsSec.find(key);
if (it != mRefractoryPeriodEndsSec.end()) {
if ((timestampNs / NS_PER_SEC) <= it->second) {
@@ -226,7 +226,7 @@
return false;
}
-void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
+void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
VLOG("informSubscribers called.");
if (mSubscriptions.empty()) {
ALOGE("Attempt to call with no subscribers.");
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 472c02c..f01a97f 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -48,19 +48,19 @@
// Adds a bucket.
// Bucket index starts from 0.
void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum);
- void addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+ void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
const int64_t& bucketNum);
// Returns true if detected anomaly for the existing buckets on one or more dimension keys.
- bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key,
+ bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
const int64_t& currentBucketValue);
// Informs incidentd about the detected alert.
- void declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key);
+ void declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key);
// Detects the alert and informs the incidentd when applicable.
void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum,
- const HashableDimensionKey& key,
+ const MetricDimensionKey& key,
const int64_t& currentBucketValue);
// Init the AnomalyMonitor which is shared across anomaly trackers.
@@ -69,10 +69,10 @@
}
// Helper function to return the sum value of past buckets at given dimension.
- int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const;
+ int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
// Helper function to return the value for a past bucket.
- int64_t getPastBucketValue(const HashableDimensionKey& key, const int64_t& bucketNum) const;
+ int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
// Returns the anomaly threshold.
inline int64_t getAnomalyThreshold() const {
@@ -81,7 +81,7 @@
// Returns the refractory period timestamp (in seconds) for the given key.
// If there is no stored refractory period ending timestamp, returns 0.
- uint32_t getRefractoryPeriodEndsSec(const HashableDimensionKey& key) const {
+ uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const {
const auto& it = mRefractoryPeriodEndsSec.find(key);
return it != mRefractoryPeriodEndsSec.end() ? it->second : 0;
}
@@ -124,7 +124,7 @@
// declared for that dimension) ends, in seconds. Only anomalies that occur after this period
// ends will be declared.
// Entries may be, but are not guaranteed to be, removed after the period is finished.
- unordered_map<HashableDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
+ unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
void flushPastBuckets(const int64_t& currBucketNum);
@@ -135,7 +135,7 @@
// and remove any items with value 0.
void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
- bool isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key);
+ bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key);
// Calculates the corresponding bucket index within the circular array.
size_t index(int64_t bucketNum) const;
@@ -144,7 +144,7 @@
virtual void resetStorage();
// Informs the subscribers that an anomaly has occurred.
- void informSubscribers(const HashableDimensionKey& key);
+ void informSubscribers(const MetricDimensionKey& key);
FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 7576a38..bbee9fa 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -37,8 +37,8 @@
if (!mAlarms.empty()) VLOG("AnomalyTracker.resetStorage() called but mAlarms is NOT empty!");
}
-void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
- const uint64_t& timestampNs) {
+void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
+ const uint64_t& timestampNs) {
auto itr = mAlarms.find(dimensionKey);
if (itr == mAlarms.end()) {
return;
@@ -51,7 +51,7 @@
}
}
-void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey,
+void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
const uint64_t& timestampNs) {
uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC);
@@ -66,7 +66,7 @@
}
}
-void DurationAnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) {
+void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey) {
auto itr = mAlarms.find(dimensionKey);
if (itr != mAlarms.end()) {
mAlarms.erase(dimensionKey);
@@ -77,7 +77,7 @@
}
void DurationAnomalyTracker::stopAllAlarms() {
- std::set<HashableDimensionKey> keys;
+ std::set<MetricDimensionKey> keys;
for (auto itr = mAlarms.begin(); itr != mAlarms.end(); ++itr) {
keys.insert(itr->first);
}
@@ -95,7 +95,7 @@
// seldomly called. The alternative would be having AnomalyAlarms store information about the
// DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that is
// rarely ever called.
- unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
+ unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
for (const auto& kv : mAlarms) {
if (firedAlarms.count(kv.second) > 0) {
matchedAlarms.insert({kv.first, kv.second});
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
index 33e55ab..052fdf57 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -32,10 +32,10 @@
virtual ~DurationAnomalyTracker();
// Starts the alarm at the given timestamp.
- void startAlarm(const HashableDimensionKey& dimensionKey, const uint64_t& eventTime);
+ void startAlarm(const MetricDimensionKey& dimensionKey, const uint64_t& eventTime);
// Stops the alarm.
- void stopAlarm(const HashableDimensionKey& dimensionKey);
+ void stopAlarm(const MetricDimensionKey& dimensionKey);
// Stop all the alarms owned by this tracker.
void stopAllAlarms();
@@ -46,7 +46,7 @@
}
// Declares the anomaly when the alarm expired given the current timestamp.
- void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+ void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
const uint64_t& timestampNs);
// Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
@@ -59,7 +59,7 @@
protected:
// The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
// are still active.
- std::unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> mAlarms;
+ std::unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> mAlarms;
// Anomaly alarm monitor.
sp<AnomalyMonitor> mAnomalyMonitor;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4e570a6..b156d8d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -99,7 +99,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10019
+ // Next: 10021
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -120,6 +120,8 @@
CpuActiveTime cpu_active_time = 10016;
CpuClusterTime cpu_cluster_time = 10017;
DiskSpace disk_space = 10018;
+ RemainingBatteryCapacity remaining_battery_capacity = 10019;
+ FullBatteryCapacity full_battery_capacity = 10020;
}
}
@@ -757,8 +759,8 @@
// The tag used with the is_default for resetting sets of settings. This is generally null.
optional string tag = 5;
- // 1 indicates that this setting with tag should be resettable.
- optional int32 is_default = 6;
+ // True if this setting with tag should be resettable.
+ optional bool is_default = 6;
// The user ID associated. Defined in android/os/UserHandle.java
optional int32 user = 7;
@@ -1105,9 +1107,12 @@
optional int32 isolated_uid = 2;
- // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to
- // be removed before if it's used for another parent uid.
- optional int32 is_create = 3;
+ // We expect an isolated uid to be removed before if it's used for another parent uid.
+ enum Event {
+ REMOVED = 0;
+ CREATED = 1;
+ }
+ optional Event event = 3;
}
/**
@@ -1410,3 +1415,19 @@
// available bytes in download cache or temp directories
optional uint64 temp_available_bytes = 3;
}
+
+/**
+ * Pulls battery coulomb counter, which is the remaining battery charge in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message RemainingBatteryCapacity {
+ optional int32 charge_uAh = 1;
+}
+
+/**
+ * Pulls battery capacity, which is the battery capacity when full in uAh.
+ * Logged from: frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message FullBatteryCapacity {
+ optional int32 capacity_uAh = 1;
+}
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index ea6586c..4c20ccb 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -78,6 +78,7 @@
return false;
}
+
bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
conditionIdIndexMap, stack);
@@ -88,8 +89,10 @@
ALOGW("Child initialization success %lld ", (long long)child);
}
+ if (allConditionTrackers[childIndex]->isSliced()) {
+ setSliced(true);
+ }
mChildren.push_back(childIndex);
-
mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
childTracker->getLogTrackerIndex().end());
}
@@ -105,11 +108,15 @@
void CombinationConditionTracker::isConditionMet(
const ConditionKey& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions,
- vector<ConditionState>& conditionCache) const {
+ const FieldMatcher& dimensionFields,
+ vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ // So far, this is fine as there is at most one child having sliced output.
for (const int childIndex : mChildren) {
if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
- conditionCache);
+ dimensionFields, conditionCache,
+ dimensionsKeySet);
}
}
conditionCache[mIndex] =
@@ -127,6 +134,7 @@
}
for (const int childIndex : mChildren) {
+ // So far, this is fine as there is at most one child having sliced output.
if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
const sp<ConditionTracker>& child = mAllConditions[childIndex];
child->evaluateCondition(event, eventMatcherValues, mAllConditions,
@@ -159,6 +167,24 @@
}
}
+ConditionState CombinationConditionTracker::getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
+ // So far, this is fine as there is at most one child having sliced output.
+ for (const int childIndex : mChildren) {
+ conditionCache[childIndex] = conditionCache[childIndex] |
+ allConditions[childIndex]->getMetConditionDimension(
+ allConditions, dimensionFields, dimensionsKeySet);
+ }
+ evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
+ if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
+ dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
+ }
+ return conditionCache[mIndex];
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index dfd3837..0b7f949 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -41,12 +41,20 @@
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache) override;
- void isConditionMet(const ConditionKey& conditionParameters,
- const std::vector<sp<ConditionTracker>>& allConditions,
- std::vector<ConditionState>& conditionCache) const override;
+ void isConditionMet(
+ const ConditionKey& conditionParameters,
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+ ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
private:
LogicalOperation mLogicalOperation;
+
// Store index of the children Predicates.
// We don't store string name of the Children, because we want to get rid of the hash map to
// map the name to object. We don't want to store smart pointers to children, because it
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 773860f..81abbdb 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -24,6 +24,7 @@
#include <log/logprint.h>
#include <utils/RefBase.h>
+#include <unordered_set>
#include <unordered_map>
namespace android {
@@ -84,10 +85,19 @@
// [allConditions]: all condition trackers. This is needed because the condition evaluation is
// done recursively
// [conditionCache]: the cache holding the condition evaluation values.
+ // [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination
+ // condition, it assumes that only one child predicate is sliced.
virtual void isConditionMet(
const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- std::vector<ConditionState>& conditionCache) const = 0;
+ const FieldMatcher& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
+
+ virtual ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
// return the list of LogMatchingTracker index that this ConditionTracker uses.
virtual const std::set<int>& getLogTrackerIndex() const {
@@ -98,6 +108,10 @@
mSliced = mSliced | sliced;
}
+ bool isSliced() const {
+ return mSliced;
+ }
+
protected:
const int64_t mConditionId;
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index d99c2cc..0427700 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include "ConditionWizard.h"
+#include <unordered_set>
namespace android {
namespace os {
@@ -23,14 +24,26 @@
using std::string;
using std::vector;
-ConditionState ConditionWizard::query(const int index,
- const ConditionKey& parameters) {
+ConditionState ConditionWizard::query(
+ const int index, const ConditionKey& parameters,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionKeySet) {
+
vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
- mAllConditions[index]->isConditionMet(parameters, mAllConditions, cache);
+ mAllConditions[index]->isConditionMet(
+ parameters, mAllConditions, dimensionFields, cache, *dimensionKeySet);
return cache[index];
}
+ConditionState ConditionWizard::getMetConditionDimension(
+ const int index, const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const {
+
+ return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
+ *dimensionsKeySet);
+}
+
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 4ff5c07..b38b59f 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -41,7 +41,14 @@
// the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
virtual ConditionState query(
const int conditionIndex,
- const ConditionKey& conditionParameters);
+ const ConditionKey& conditionParameters,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionKeySet);
+
+ virtual ConditionState getMetConditionDimension(
+ const int index,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const;
private:
std::vector<sp<ConditionTracker>> mAllConditions;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 5cfc349..25265d5 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -104,6 +104,9 @@
vector<bool>& stack) {
// SimpleConditionTracker does not have dependency on other conditions, thus we just return
// if the initialization was successful.
+ if (mOutputDimensions.has_field() || mOutputDimensions.child_size() > 0) {
+ setSliced(true);
+ }
return mInitialized;
}
@@ -234,11 +237,12 @@
conditionChangedCache[mIndex] == true);
}
-void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
- const vector<MatchingState>& eventMatcherValues,
- const vector<sp<ConditionTracker>>& mAllConditions,
- vector<ConditionState>& conditionCache,
- vector<bool>& conditionChangedCache) {
+void SimpleConditionTracker::evaluateCondition(
+ const LogEvent& event,
+ const vector<MatchingState>& eventMatcherValues,
+ const vector<sp<ConditionTracker>>& mAllConditions,
+ vector<ConditionState>& conditionCache,
+ vector<bool>& conditionChangedCache) {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
VLOG("Yes, already evaluated, %lld %d",
@@ -271,7 +275,7 @@
if (mSliced) {
// if the condition result is sliced. metrics won't directly get value from the
// cache, so just set any value other than kNotEvaluated.
- conditionCache[mIndex] = ConditionState::kUnknown;
+ conditionCache[mIndex] = mInitialValue;
} else {
const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
if (itr == mSlicedConditionState.end()) {
@@ -310,10 +314,8 @@
vector<ConditionState> dimensionalConditionCache(conditionCache.size(),
ConditionState::kNotEvaluated);
vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false);
-
handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1,
dimensionalConditionCache, dimensionalConditionChangedCache);
-
OrConditionState(dimensionalConditionCache, &conditionCache);
OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache);
}
@@ -323,42 +325,73 @@
void SimpleConditionTracker::isConditionMet(
const ConditionKey& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions,
- vector<ConditionState>& conditionCache) const {
- const auto pair = conditionParameters.find(mConditionId);
-
- if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) {
- ALOGE("Predicate %lld output has dimension, but it's not specified in the query!",
- (long long)mConditionId);
- conditionCache[mIndex] = mInitialValue;
+ const FieldMatcher& dimensionFields,
+ vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+ // it has been evaluated.
+ VLOG("Yes, already evaluated, %lld %d",
+ (long long)mConditionId, conditionCache[mIndex]);
return;
}
- std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY};
+ const auto pair = conditionParameters.find(mConditionId);
+
+ if (pair == conditionParameters.end()) {
+ ConditionState conditionState = ConditionState::kNotEvaluated;
+ if (dimensionFields.has_field() && dimensionFields.child_size() > 0 &&
+ dimensionFields.field() == mOutputDimensions.field()) {
+ conditionState = conditionState | getMetConditionDimension(
+ allConditions, dimensionFields, dimensionsKeySet);
+ } else {
+ conditionState = conditionState | mInitialValue;
+ if (!mSliced) {
+ const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+ if (itr != mSlicedConditionState.end()) {
+ ConditionState sliceState =
+ itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
+ }
+ }
+ }
+ conditionCache[mIndex] = conditionState;
+ return;
+ }
+ std::vector<HashableDimensionKey> defaultKeys = { DEFAULT_DIMENSION_KEY };
const std::vector<HashableDimensionKey> &keys =
(pair == conditionParameters.end()) ? defaultKeys : pair->second;
ConditionState conditionState = ConditionState::kNotEvaluated;
- for (const auto& key : keys) {
+ for (size_t i = 0; i < keys.size(); ++i) {
+ const HashableDimensionKey& key = keys[i];
auto startedCountIt = mSlicedConditionState.find(key);
if (startedCountIt != mSlicedConditionState.end()) {
- conditionState = conditionState |
- (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+ ConditionState sliceState =
+ startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
+ if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+ HashableDimensionKey dimensionKey;
+ if (getSubDimension(startedCountIt->first.getDimensionsValue(), dimensionFields,
+ dimensionKey.getMutableDimensionsValue())) {
+ dimensionsKeySet.insert(dimensionKey);
+ }
+ }
} else {
// For unseen key, check whether the require dimensions are subset of sliced condition
// output.
- bool seenDimension = false;
+ conditionState = conditionState | mInitialValue;
for (const auto& slice : mSlicedConditionState) {
- if (IsSubDimension(slice.first.getDimensionsValue(),
- key.getDimensionsValue())) {
- seenDimension = true;
- conditionState = conditionState |
- (slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+ ConditionState sliceState =
+ slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ if (IsSubDimension(slice.first.getDimensionsValue(), key.getDimensionsValue())) {
+ conditionState = conditionState | sliceState;
+ if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+ HashableDimensionKey dimensionKey;
+ if (getSubDimension(slice.first.getDimensionsValue(),
+ dimensionFields, dimensionKey.getMutableDimensionsValue())) {
+ dimensionsKeySet.insert(dimensionKey);
+ }
+ }
}
- if (conditionState == ConditionState::kTrue) {
- break;
- }
- }
- if (!seenDimension) {
- conditionState = conditionState | mInitialValue;
}
}
}
@@ -366,6 +399,38 @@
VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
}
+ConditionState SimpleConditionTracker::getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+ ConditionState conditionState = mInitialValue;
+ if (!dimensionFields.has_field() ||
+ !mOutputDimensions.has_field() ||
+ dimensionFields.field() != mOutputDimensions.field()) {
+ const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+ if (itr != mSlicedConditionState.end()) {
+ ConditionState sliceState =
+ itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ conditionState = conditionState | sliceState;
+ }
+ return conditionState;
+ }
+
+ for (const auto& slice : mSlicedConditionState) {
+ ConditionState sliceState =
+ slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ DimensionsValue dimensionsValue;
+ conditionState = conditionState | sliceState;
+ HashableDimensionKey dimensionKey;
+ if (sliceState == ConditionState::kTrue &&
+ getSubDimension(slice.first.getDimensionsValue(), dimensionFields,
+ dimensionKey.getMutableDimensionsValue())) {
+ dimensionsKeySet.insert(dimensionKey);
+ }
+ }
+ return conditionState;
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 815b445..ce9a02d 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -48,7 +48,14 @@
void isConditionMet(const ConditionKey& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
- std::vector<ConditionState>& conditionCache) const override;
+ const FieldMatcher& dimensionFields,
+ std::vector<ConditionState>& conditionCache,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+
+ ConditionState getMetConditionDimension(
+ const std::vector<sp<ConditionTracker>>& allConditions,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
private:
const ConfigKey mConfigKey;
@@ -73,7 +80,8 @@
void handleStopAll(std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache);
- void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+ void handleConditionEvent(const HashableDimensionKey& outputKey,
+ bool matchStart,
std::vector<ConditionState>& conditionCache,
std::vector<bool>& changedCache);
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 3b2d480..0ab33cf 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -118,6 +118,9 @@
void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField,
std::vector<Field> *allFields) {
+ if (matcher.has_position()) {
+ leafField->set_position_index(0);
+ }
if (matcher.child_size() == 0) {
allFields->push_back(*rootField);
return;
diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp
index 04445ca..8a2e871 100644
--- a/cmds/statsd/src/dimension.cpp
+++ b/cmds/statsd/src/dimension.cpp
@@ -253,6 +253,9 @@
}
void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {
+ if (!value.has_field()) {
+ return;
+ }
*flattened += std::to_string(value.field());
*flattened += ":";
switch (value.value_case()) {
@@ -352,6 +355,46 @@
}
}
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+ DimensionsValue* subDimension) {
+ if (!matcher.has_field()) {
+ return false;
+ }
+ if (matcher.field() != dimension.field()) {
+ return false;
+ }
+ if (matcher.child_size() <= 0) {
+ if (dimension.value_case() == DimensionsValue::ValueCase::kValueTuple ||
+ dimension.value_case() == DimensionsValue::ValueCase::VALUE_NOT_SET) {
+ return false;
+ }
+ *subDimension = dimension;
+ return true;
+ } else {
+ if (dimension.value_case() != DimensionsValue::ValueCase::kValueTuple) {
+ return false;
+ }
+ bool found_value = true;
+ auto value_tuple = dimension.value_tuple();
+ subDimension->set_field(dimension.field());
+ for (int i = 0; found_value && i < matcher.child_size(); ++i) {
+ int j = 0;
+ for (; j < value_tuple.dimensions_value_size(); ++j) {
+ if (value_tuple.dimensions_value(j).field() == matcher.child(i).field()) {
+ break;
+ }
+ }
+ if (j < value_tuple.dimensions_value_size()) {
+ found_value &= getSubDimension(value_tuple.dimensions_value(j), matcher.child(i),
+ subDimension->mutable_value_tuple()->add_dimensions_value());
+ } else {
+ found_value = false;
+ }
+ }
+ return found_value;
+ }
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
index e900c5e..138c6e9 100644
--- a/cmds/statsd/src/dimension.h
+++ b/cmds/statsd/src/dimension.h
@@ -63,6 +63,9 @@
// Helper function to get long value from the DimensionsValue proto.
long getLongFromDimenValue(const DimensionsValue& dimensionValue);
+
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+ DimensionsValue* subDimension);
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
new file mode 100644
index 0000000..72fb5ff
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 <android/hardware/health/2.0/IHealth.h>
+#include <healthhalutils/HealthHalUtils.h>
+#include "external/ResourceHealthManagerPuller.h"
+#include "external/StatsPuller.h"
+
+#include "ResourceHealthManagerPuller.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::health::V2_0::get_health_service;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using std::make_shared;
+using std::shared_ptr;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+sp<android::hardware::health::V2_0::IHealth> gHealthHal = nullptr;
+
+bool getHealthHal() {
+ if (gHealthHal == nullptr) {
+ gHealthHal = get_health_service();
+
+ }
+ return gHealthHal != nullptr;
+}
+
+ResourceHealthManagerPuller::ResourceHealthManagerPuller(int tagId) : StatsPuller(tagId) {
+}
+
+// TODO: add other health atoms (eg. Temperature).
+bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+ if (!getHealthHal()) {
+ ALOGE("Health Hal not loaded");
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+
+ data->clear();
+ bool result_success = true;
+
+ Return<void> ret = gHealthHal->getHealthInfo([&](Result r, HealthInfo v) {
+ if (r != Result::SUCCESS) {
+ result_success = false;
+ return;
+ }
+ if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) {
+ auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY, timestamp);
+ ptr->write(v.legacy.batteryChargeCounter);
+ ptr->init();
+ data->push_back(ptr);
+ } else if (mTagId == android::util::FULL_BATTERY_CAPACITY) {
+ auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY, timestamp);
+ ptr->write(v.legacy.batteryFullCharge);
+ ptr->init();
+ data->push_back(ptr);
+ } else {
+ ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
+ }
+ });
+ if (!result_success || !ret.isOk()) {
+ ALOGE("getHealthHal() failed: health HAL service not available. Description: %s",
+ ret.description().c_str());
+ if (!ret.isOk() && ret.isDeadObject()) {
+ gHealthHal = nullptr;
+ }
+ return false;
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.h b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
new file mode 100644
index 0000000..9b238ea
--- /dev/null
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads Ihealth.hal
+ */
+class ResourceHealthManagerPuller : public StatsPuller {
+public:
+ ResourceHealthManagerPuller(int tagId);
+ bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index b4b04be..71b0abe 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -23,10 +23,11 @@
#include <climits>
#include "CpuTimePerUidFreqPuller.h"
#include "CpuTimePerUidPuller.h"
-#include "SubsystemSleepStatePuller.h"
+#include "ResourceHealthManagerPuller.h"
#include "StatsCompanionServicePuller.h"
#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
+#include "SubsystemSleepStatePuller.h"
#include "logd/LogEvent.h"
#include "statslog.h"
@@ -83,7 +84,10 @@
make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
-
+ mPullers.insert({android::util::REMAINING_BATTERY_CAPACITY,
+ make_shared<ResourceHealthManagerPuller>(android::util::REMAINING_BATTERY_CAPACITY)});
+ mPullers.insert({android::util::FULL_BATTERY_CAPACITY,
+ make_shared<ResourceHealthManagerPuller>(android::util::FULL_BATTERY_CAPACITY)});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 0455f6a..ae4df3e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -21,6 +21,7 @@
#include "guardrail/StatsdStats.h"
#include "stats_util.h"
#include "stats_log_util.h"
+#include "dimension.h"
#include <limits.h>
#include <stdlib.h>
@@ -71,13 +72,15 @@
}
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -99,7 +102,10 @@
auto count_metrics = report->mutable_count_metrics();
for (const auto& counter : mPastBuckets) {
CountMetricData* metricData = count_metrics->add_data();
- *metricData->mutable_dimensions_in_what() = counter.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() =
+ counter.first.getDimensionKeyInWhat().getDimensionsValue();
+ *metricData->mutable_dimensions_in_condition() =
+ counter.first.getDimensionKeyInCondition().getDimensionsValue();
for (const auto& bucket : counter.second) {
CountBucketInfo* bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -123,17 +129,26 @@
VLOG("metric %lld dump report now...",(long long)mMetricId);
for (const auto& counter : mPastBuckets) {
- const HashableDimensionKey& hashableKey = counter.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ const MetricDimensionKey& dimensionKey = counter.first;
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
- long long dimensionToken = protoOutput->start(
+ long long dimensionInWhatToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
- protoOutput->end(dimensionToken);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInWhatToken);
+
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
@@ -166,7 +181,7 @@
mCondition = conditionMet;
}
-bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
return false;
}
@@ -187,7 +202,7 @@
}
void CountMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 061b7a3..8659d47 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -50,7 +50,7 @@
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
@@ -74,14 +74,14 @@
void flushIfNeededLocked(const uint64_t& newEventTime);
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
// The current bucket.
std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
static const size_t kBucketSize = sizeof(CountBucket{});
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 000874c..efbdae1 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -21,6 +21,7 @@
#include "guardrail/StatsdStats.h"
#include "stats_util.h"
#include "stats_log_util.h"
+#include "dimension.h"
#include <limits.h>
#include <stdlib.h>
@@ -81,13 +82,15 @@
}
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -113,15 +116,17 @@
}
unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
- const HashableDimensionKey& eventKey) const {
+ const MetricDimensionKey& eventKey) const {
switch (mAggregationType) {
case DurationMetric_AggregationType_SUM:
return make_unique<OringDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+ mDimensionsInCondition, mNested,
mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
case DurationMetric_AggregationType_MAX_SPARSE:
return make_unique<MaxDurationTracker>(
- mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+ mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+ mDimensionsInCondition, mNested,
mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
}
}
@@ -129,10 +134,34 @@
void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
flushIfNeededLocked(eventTime);
+
// Now for each of the on-going event, check if the condition has changed for them.
- for (auto& pair : mCurrentSlicedDuration) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
pair.second->onSlicedConditionMayChange(eventTime);
}
+
+
+ std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
+ ConditionState conditionState = mWizard->getMetConditionDimension(
+ mConditionTrackerIndex, mDimensionsInCondition, &conditionDimensionsKeySet);
+
+ bool condition = (conditionState == ConditionState::kTrue);
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
+ conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition());
+ }
+ std::unordered_set<MetricDimensionKey> newKeys;
+ for (const auto& conditionDimensionsKey : conditionDimensionsKeySet) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
+ auto newKey =
+ MetricDimensionKey(pair.first.getDimensionKeyInWhat(), conditionDimensionsKey);
+ if (newKeys.find(newKey) == newKeys.end()) {
+ mCurrentSlicedDurationTrackerMap[newKey] = pair.second->clone(eventTime);
+ mCurrentSlicedDurationTrackerMap[newKey]->setEventKey(newKey);
+ mCurrentSlicedDurationTrackerMap[newKey]->onSlicedConditionMayChange(eventTime);
+ }
+ newKeys.insert(newKey);
+ }
+ }
}
void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
@@ -142,7 +171,7 @@
flushIfNeededLocked(eventTime);
// TODO: need to populate the condition change time from the event which triggers the condition
// change, instead of using current time.
- for (auto& pair : mCurrentSlicedDuration) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
pair.second->onConditionChanged(conditionMet, eventTime);
}
}
@@ -155,7 +184,10 @@
auto duration_metrics = report->mutable_duration_metrics();
for (const auto& pair : mPastBuckets) {
DurationMetricData* metricData = duration_metrics->add_data();
- *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() =
+ pair.first.getDimensionKeyInWhat().getDimensionsValue();
+ *metricData->mutable_dimensions_in_condition() =
+ pair.first.getDimensionKeyInCondition().getDimensionsValue();
for (const auto& bucket : pair.second) {
auto bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -179,8 +211,8 @@
VLOG("metric %lld dump report now...", (long long)mMetricId);
for (const auto& pair : mPastBuckets) {
- const HashableDimensionKey& hashableKey = pair.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ const MetricDimensionKey& dimensionKey = pair.first;
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
@@ -188,9 +220,18 @@
// First fill dimension.
long long dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
+
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
long long bucketInfoToken = protoOutput->start(
@@ -219,10 +260,11 @@
return;
}
VLOG("flushing...........");
- for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
+ for (auto it = mCurrentSlicedDurationTrackerMap.begin();
+ it != mCurrentSlicedDurationTrackerMap.end();) {
if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) {
VLOG("erase bucket for key %s", it->first.c_str());
- it = mCurrentSlicedDuration.erase(it);
+ it = mCurrentSlicedDurationTrackerMap.erase(it);
} else {
++it;
}
@@ -234,28 +276,28 @@
}
void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
- if (mCurrentSlicedDuration.size() == 0) {
+ if (mCurrentSlicedDurationTrackerMap.size() == 0) {
return;
}
fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
- (unsigned long)mCurrentSlicedDuration.size());
+ (unsigned long)mCurrentSlicedDurationTrackerMap.size());
if (verbose) {
- for (const auto& slice : mCurrentSlicedDuration) {
+ for (const auto& slice : mCurrentSlicedDurationTrackerMap) {
fprintf(out, "\t%s\n", slice.first.c_str());
slice.second->dumpStates(out, verbose);
}
}
}
-bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
// the key is not new, we are good.
- if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
+ if (mCurrentSlicedDurationTrackerMap.find(newKey) != mCurrentSlicedDurationTrackerMap.end()) {
return false;
}
// 1. Report the tuple count if the tuple count > soft limit
- if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
- size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
+ if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+ size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -268,27 +310,26 @@
}
void DurationMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition,
const LogEvent& event) {
flushIfNeededLocked(event.GetTimestampNs());
if (matcherIndex == mStopAllIndex) {
- for (auto& pair : mCurrentSlicedDuration) {
+ for (auto& pair : mCurrentSlicedDurationTrackerMap) {
pair.second->noteStopAll(event.GetTimestampNs());
}
return;
}
-
- if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+ if (mCurrentSlicedDurationTrackerMap.find(eventKey) == mCurrentSlicedDurationTrackerMap.end()) {
if (hitGuardRailLocked(eventKey)) {
return;
}
- mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey);
+ mCurrentSlicedDurationTrackerMap[eventKey] = createDurationTracker(eventKey);
}
- auto it = mCurrentSlicedDuration.find(eventKey);
+ auto it = mCurrentSlicedDurationTrackerMap.find(eventKey);
std::vector<DimensionsValue> values;
getDimensionKeys(event, mInternalDimensions, &values);
@@ -302,10 +343,11 @@
} else {
for (const DimensionsValue& value : values) {
if (matcherIndex == mStartIndex) {
- it->second->noteStart(HashableDimensionKey(value), condition,
- event.GetTimestampNs(), conditionKeys);
+ it->second->noteStart(
+ HashableDimensionKey(value), condition, event.GetTimestampNs(), conditionKeys);
} else if (matcherIndex == mStopIndex) {
- it->second->noteStop(HashableDimensionKey(value), event.GetTimestampNs(), false);
+ it->second->noteStop(
+ HashableDimensionKey(value), event.GetTimestampNs(), false);
}
}
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index d8cab92..152e570 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -50,7 +50,7 @@
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKeys, bool condition,
const LogEvent& event) override;
@@ -92,21 +92,21 @@
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
// The current bucket.
- std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
- mCurrentSlicedDuration;
+ std::unordered_map<MetricDimensionKey, std::unique_ptr<DurationTracker>>
+ mCurrentSlicedDurationTrackerMap;
// Helper function to create a duration tracker given the metric aggregation type.
std::unique_ptr<DurationTracker> createDurationTracker(
- const HashableDimensionKey& eventKey) const;
+ const MetricDimensionKey& eventKey) const;
// This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(DurationBucket{});
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 25c86d0..820d591 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -128,7 +128,7 @@
}
void EventMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
if (!condition) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 9da0dd0..935f206 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -45,7 +45,7 @@
private:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 1072c5a..d6cb189 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -82,13 +82,15 @@
mFieldFilter = metric.gauge_fields_filter();
// TODO: use UidMap if uid->pkg_name is required
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
// Kicks off the puller immediately.
if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
@@ -136,18 +138,27 @@
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
for (const auto& pair : mPastBuckets) {
- const HashableDimensionKey& hashableKey = pair.first;
+ const MetricDimensionKey& dimensionKey = pair.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
long long dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
+
// Then fill bucket_info (GaugeBucketInfo).
for (const auto& bucket : pair.second) {
long long bucketInfoToken = protoOutput->start(
@@ -248,7 +259,7 @@
}
}
-bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
return false;
}
@@ -268,7 +279,7 @@
}
void GaugeMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
if (condition == false) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6c01347..86d0ccd 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -44,7 +44,7 @@
uint64_t mBucketNum;
};
-typedef std::unordered_map<HashableDimensionKey, std::vector<GaugeAtom>>
+typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
DimToGaugeAtomsMap;
// This gauge metric producer first register the puller to automatically pull the gauge at the
@@ -64,7 +64,7 @@
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
@@ -99,7 +99,7 @@
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
// The current bucket.
std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket;
@@ -119,7 +119,7 @@
std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event);
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(GaugeBucket{});
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index e74924a..85e655b 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -15,6 +15,8 @@
*/
#include "MetricProducer.h"
+#include "dimension.h"
+
namespace android {
namespace os {
namespace statsd {
@@ -30,29 +32,51 @@
bool condition;
ConditionKey conditionKey;
+
+ std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
if (mConditionSliced) {
for (const auto& link : mConditionLinks) {
getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]);
}
- if (mWizard->query(mConditionTrackerIndex, conditionKey) != ConditionState::kTrue) {
- condition = false;
- } else {
- condition = true;
- }
+ auto conditionState =
+ mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
+ &dimensionKeysInCondition);
+ condition = (conditionState == ConditionState::kTrue);
} else {
condition = mCondition;
}
- if (mDimensions.has_field() && mDimensions.child_size() > 0) {
- vector<DimensionsValue> dimensionValues;
- getDimensionKeys(event, mDimensions, &dimensionValues);
- for (const DimensionsValue& dimensionValue : dimensionValues) {
+ vector<DimensionsValue> dimensionInWhatValues;
+ if (mDimensionsInWhat.has_field() && mDimensionsInWhat.child_size() > 0) {
+ getDimensionKeys(event, mDimensionsInWhat, &dimensionInWhatValues);
+ }
+
+ if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event);
+ } else if (dimensionKeysInCondition.empty()) {
+ for (const DimensionsValue& whatValue : dimensionInWhatValues) {
onMatchedLogEventInternalLocked(
- matcherIndex, HashableDimensionKey(dimensionValue), conditionKey, condition, event);
+ matcherIndex,
+ MetricDimensionKey(HashableDimensionKey(whatValue), DEFAULT_DIMENSION_KEY),
+ conditionKey, condition, event);
+ }
+ } else if (dimensionInWhatValues.empty()) {
+ for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex,
+ MetricDimensionKey(DEFAULT_DIMENSION_KEY, conditionDimensionKey),
+ conditionKey, condition, event);
}
} else {
- onMatchedLogEventInternalLocked(
- matcherIndex, DEFAULT_DIMENSION_KEY, conditionKey, condition, event);
+ for (const DimensionsValue& whatValue : dimensionInWhatValues) {
+ for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
+ onMatchedLogEventInternalLocked(
+ matcherIndex,
+ MetricDimensionKey(HashableDimensionKey(whatValue), conditionDimensionKey),
+ conditionKey, condition, event);
+ }
+ }
}
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 6f33073..3b1498f 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -91,6 +91,7 @@
std::lock_guard<std::mutex> lock(mMutex);
return onDumpReportLocked(dumpTimeNs, protoOutput);
}
+
void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) {
std::lock_guard<std::mutex> lock(mMutex);
return onDumpReportLocked(dumpTimeNs, report);
@@ -156,7 +157,8 @@
int mConditionTrackerIndex;
- FieldMatcher mDimensions; // The dimension defined in statsd_config
+ FieldMatcher mDimensionsInWhat; // The dimensions_in_what defined in statsd_config
+ FieldMatcher mDimensionsInCondition; // The dimensions_in_condition defined in statsd_config
std::vector<MetricConditionLink> mConditionLinks;
@@ -178,7 +180,7 @@
* [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
*/
virtual void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 9cdbafc..d4b9102 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -143,6 +143,10 @@
FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
+ FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index ae0c673..c9cc7bb 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -81,13 +81,15 @@
}
mBucketSizeNs = bucketSizeMills * 1000000;
- mDimensions = metric.dimensions_in_what();
+ mDimensionsInWhat = metric.dimensions_in_what();
+ mDimensionsInCondition = metric.dimensions_in_condition();
if (metric.links().size() > 0) {
mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
metric.links().end());
- mConditionSliced = true;
}
+ mConditionSliced = (metric.links().size() > 0)||
+ (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
if (!metric.has_condition() && mPullTagId != -1) {
VLOG("Setting up periodic pulling for %d", mPullTagId);
@@ -124,7 +126,10 @@
auto value_metrics = report->mutable_value_metrics();
for (const auto& pair : mPastBuckets) {
ValueMetricData* metricData = value_metrics->add_data();
- *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+ *metricData->mutable_dimensions_in_what() =
+ pair.first.getDimensionKeyInWhat().getDimensionsValue();
+ *metricData->mutable_dimensions_in_condition() =
+ pair.first.getDimensionKeyInCondition().getDimensionsValue();
for (const auto& bucket : pair.second) {
ValueBucketInfo* bucketInfo = metricData->add_bucket_info();
bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -146,16 +151,24 @@
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
for (const auto& pair : mPastBuckets) {
- const HashableDimensionKey& hashableKey = pair.first;
- VLOG(" dimension key %s", hashableKey.c_str());
+ const MetricDimensionKey& dimensionKey = pair.first;
+ VLOG(" dimension key %s", dimensionKey.c_str());
long long wrapperToken =
protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension.
long long dimensionToken = protoOutput->start(
FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
- writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
protoOutput->end(dimensionToken);
+ if (dimensionKey.hasDimensionKeyInCondition()) {
+ long long dimensionInConditionToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+ writeDimensionsValueProtoToStream(
+ dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+ protoOutput->end(dimensionInConditionToken);
+ }
// Then fill bucket_info (ValueBucketInfo).
for (const auto& bucket : pair.second) {
@@ -239,7 +252,7 @@
}
}
-bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
@@ -260,7 +273,7 @@
}
void ValueMetricProducer::onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) {
uint64_t eventTimeNs = event.GetTimestampNs();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 9f750cf..121ec7d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -49,7 +49,7 @@
protected:
void onMatchedLogEventInternalLocked(
- const size_t matcherIndex, const HashableDimensionKey& eventKey,
+ const size_t matcherIndex, const MetricDimensionKey& eventKey,
const ConditionKey& conditionKey, bool condition,
const LogEvent& event) override;
@@ -99,16 +99,16 @@
long sum;
} Interval;
- std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
+ std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket;
// Save the past buckets and we can clear when the StatsLogReport is dumped.
// TODO: Add a lock to mPastBuckets.
- std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+ std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event);
// Util function to check whether the specified dimension hits the guardrail.
- bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+ bool hitGuardRailLocked(const MetricDimensionKey& newKey);
static const size_t kBucketSize = sizeof(ValueBucket{});
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index c2d2cea..45735a8 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -60,8 +60,9 @@
class DurationTracker {
public:
- DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+ DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ const FieldMatcher& dimensionInCondition, bool nesting,
uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
: mConfigKey(key),
@@ -70,6 +71,7 @@
mWizard(wizard),
mConditionTrackerIndex(conditionIndex),
mBucketSizeNs(bucketSizeNs),
+ mDimensionInCondition(dimensionInCondition),
mNested(nesting),
mCurrentBucketStartTimeNs(currentBucketStartNs),
mDuration(0),
@@ -79,6 +81,8 @@
virtual ~DurationTracker(){};
+ virtual unique_ptr<DurationTracker> clone(const uint64_t eventTime) = 0;
+
virtual void noteStart(const HashableDimensionKey& key, bool condition,
const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -92,7 +96,7 @@
// events, so that the owner can safely remove the tracker.
virtual bool flushIfNeeded(
uint64_t timestampNs,
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
// Predict the anomaly timestamp given the current status.
virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
@@ -100,6 +104,10 @@
// Dump internal states for debugging
virtual void dumpStates(FILE* out, bool verbose) const = 0;
+ void setEventKey(const MetricDimensionKey& eventKey) {
+ mEventKey = eventKey;
+ }
+
protected:
// Starts the anomaly alarm.
void startAnomalyAlarm(const uint64_t eventTime) {
@@ -150,7 +158,7 @@
const int64_t mTrackerId;
- HashableDimensionKey mEventKey;
+ MetricDimensionKey mEventKey;
sp<ConditionWizard> mWizard;
@@ -158,6 +166,8 @@
const int64_t mBucketSizeNs;
+ const FieldMatcher mDimensionInCondition;
+
const bool mNested;
uint64_t mCurrentBucketStartTimeNs;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 412a0c9..db7dea4 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -25,13 +25,23 @@
namespace statsd {
MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
- const HashableDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+ const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ const FieldMatcher& dimensionInCondition, bool nesting,
uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
bool conditionSliced,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
- bucketSizeNs, conditionSliced, anomalyTrackers) {
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+ currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers) {
+}
+
+unique_ptr<DurationTracker> MaxDurationTracker::clone(const uint64_t eventTime) {
+ auto clonedTracker = make_unique<MaxDurationTracker>(*this);
+ for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end(); ++it) {
+ it->second.lastStartTime = eventTime;
+ it->second.lastDuration = 0;
+ }
+ return clonedTracker;
}
bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -44,7 +54,7 @@
if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mInfos.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(
- mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+ mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -149,7 +159,7 @@
}
bool MaxDurationTracker::flushIfNeeded(
- uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
+ uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
return false;
}
@@ -236,8 +246,14 @@
if (pair.second.state == kStopped) {
continue;
}
- bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) ==
- ConditionState::kTrue;
+ std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+ ConditionState conditionState = mWizard->query(
+ mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
+ &conditionDimensionKeySet);
+ bool conditionMet = (conditionState == ConditionState::kTrue) &&
+ (!mDimensionInCondition.has_field() ||
+ conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
+ conditionDimensionKeySet.end());
VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
noteConditionChanged(pair.first, conditionMet, timestamp);
}
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 661d131..4d32a06 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -29,10 +29,15 @@
class MaxDurationTracker : public DurationTracker {
public:
MaxDurationTracker(const ConfigKey& key, const int64_t& id,
- const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, bool conditionSliced,
+ const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+
+ MaxDurationTracker(const MaxDurationTracker& tracker) = default;
+
+ unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -41,7 +46,7 @@
bool flushIfNeeded(
uint64_t timestampNs,
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
void onSlicedConditionMayChange(const uint64_t timestamp) override;
void onConditionChanged(bool condition, const uint64_t timestamp) override;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 75d7c08..0feae36 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -25,17 +25,25 @@
using std::pair;
OringDurationTracker::OringDurationTracker(
- const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
- sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+ const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+ sp<ConditionWizard> wizard, int conditionIndex,
+ const FieldMatcher& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs,
uint64_t bucketSizeNs, bool conditionSliced,
const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
- : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
- bucketSizeNs, conditionSliced, anomalyTrackers),
+ : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+ currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers),
mStarted(),
mPaused() {
mLastStartTime = 0;
}
+unique_ptr<DurationTracker> OringDurationTracker::clone(const uint64_t eventTime) {
+ auto clonedTracker = make_unique<OringDurationTracker>(*this);
+ clonedTracker->mLastStartTime = eventTime;
+ clonedTracker->mDuration = 0;
+ return clonedTracker;
+}
+
bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
// ===========GuardRail==============
// 1. Report the tuple count if the tuple count > soft limit
@@ -45,7 +53,7 @@
if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
size_t newTupleCount = mConditionKeyMap.size() + 1;
StatsdStats::getInstance().noteMetricDimensionSize(
- mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+ mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
newTupleCount);
// 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -76,7 +84,6 @@
if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
mConditionKeyMap[key] = conditionKey;
}
-
VLOG("Oring: %s start, condition %d", key.c_str(), condition);
}
@@ -128,7 +135,7 @@
}
bool OringDurationTracker::flushIfNeeded(
- uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
+ uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
return false;
}
@@ -184,8 +191,14 @@
++it;
continue;
}
- if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
- ConditionState::kTrue) {
+ std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+ ConditionState conditionState =
+ mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+ mDimensionInCondition, &conditionDimensionKeySet);
+ if (conditionState != ConditionState::kTrue ||
+ (mDimensionInCondition.has_field() &&
+ conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
+ conditionDimensionKeySet.end())) {
startedToPaused.push_back(*it);
it = mStarted.erase(it);
VLOG("Key %s started -> paused", key.c_str());
@@ -210,8 +223,14 @@
++it;
continue;
}
- if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
- ConditionState::kTrue) {
+ std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+ ConditionState conditionState =
+ mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+ mDimensionInCondition, &conditionDimensionKeySet);
+ if (conditionState == ConditionState::kTrue &&
+ (!mDimensionInCondition.has_field() ||
+ conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition())
+ != conditionDimensionKeySet.end())) {
pausedToStarted.push_back(*it);
it = mPaused.erase(it);
VLOG("Key %s paused -> started", key.c_str());
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index 43469ca..75b5a81 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -28,11 +28,15 @@
class OringDurationTracker : public DurationTracker {
public:
OringDurationTracker(const ConfigKey& key, const int64_t& id,
- const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
- int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
- uint64_t bucketSizeNs, bool conditionSliced,
+ const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
+ int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
+ uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+ OringDurationTracker(const OringDurationTracker& tracker) = default;
+
+ unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
const ConditionKey& conditionKey) override;
void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -44,7 +48,7 @@
bool flushIfNeeded(
uint64_t timestampNs,
- std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+ std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
const uint64_t currentTimestamp) const override;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index a41f30c..6c61400 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -54,6 +54,9 @@
void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue,
ProtoOutputStream* protoOutput) {
+ if (!dimensionsValue.has_field()) {
+ return;
+ }
protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field());
switch (dimensionsValue.value_case()) {
case DimensionsValue::ValueCase::kValueStr:
@@ -103,6 +106,9 @@
void writeFieldProtoToStream(
const Field& field, util::ProtoOutputStream* protoOutput) {
+ if (!field.has_field()) {
+ return;
+ }
protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field());
if (field.has_position_index()) {
protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index());
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 160b1f4..31f51a7 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -28,13 +28,14 @@
namespace statsd {
const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey();
+const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey();
// Minimum bucket size in seconds
const long kMinBucketSizeSec = 5 * 60;
typedef std::map<int64_t, std::vector<HashableDimensionKey>> ConditionKey;
-typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
+typedef std::unordered_map<MetricDimensionKey, int64_t> DimToValMap;
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index f912e4b..3af684f 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -56,7 +56,7 @@
void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const {
+ const MetricDimensionKey& dimKey) const {
// Reminder about ids:
// subscription id - name of the Subscription (that ties the Alert to the broadcast)
// subscription rule_id - the name of the Alert (that triggers the broadcast)
@@ -92,7 +92,7 @@
void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const {
+ const MetricDimensionKey& dimKey) const {
VLOG("SubscriberReporter::sendBroadcastLocked called.");
if (mStatsCompanionService == nullptr) {
ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
@@ -107,8 +107,8 @@
}
StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
- const HashableDimensionKey& dimKey) {
- return protoToStatsDimensionsValue(dimKey.getDimensionsValue());
+ const MetricDimensionKey& dimKey) {
+ return protoToStatsDimensionsValue(dimKey.getDimensionKeyInWhat().getDimensionsValue());
}
StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 5bb458a..13fc7fd 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -80,7 +80,7 @@
*/
void alertBroadcastSubscriber(const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const;
+ const MetricDimensionKey& dimKey) const;
private:
SubscriberReporter() {};
@@ -101,7 +101,7 @@
void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
const ConfigKey& configKey,
const Subscription& subscription,
- const HashableDimensionKey& dimKey) const;
+ const MetricDimensionKey& dimKey) const;
/** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */
static StatsDimensionsValue protoToStatsDimensionsValue(
@@ -109,7 +109,7 @@
/** Converts a HashableDimensionKey to a StatsDimensionsValue. */
static StatsDimensionsValue protoToStatsDimensionsValue(
- const HashableDimensionKey& dimKey);
+ const MetricDimensionKey& dimKey);
};
} // namespace statsd
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 66bfa68..a415ea1 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -33,14 +33,14 @@
const ConfigKey kConfigKey(0, 12345);
-HashableDimensionKey getMockDimensionKey(int key, string value) {
+MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
DimensionsValue dimensionsValue;
dimensionsValue.set_field(key);
dimensionsValue.set_value_str(value);
- return HashableDimensionKey(dimensionsValue);
+ return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
}
-void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list,
+void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, 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;
@@ -48,7 +48,7 @@
}
std::shared_ptr<DimToValMap> MockBucket(
- const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list) {
+ const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) {
std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
AddValueToBucket(key_value_pair_list, bucket);
return bucket;
@@ -56,7 +56,7 @@
// Returns the value, for the given key, in that bucket, or 0 if not present.
int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
- const HashableDimensionKey& key) {
+ const MetricDimensionKey& key) {
const auto& itr = bucket->find(key);
if (itr != bucket->end()) {
return itr->second;
@@ -68,14 +68,14 @@
bool detectAnomaliesPass(AnomalyTracker& tracker,
const int64_t& bucketNum,
const std::shared_ptr<DimToValMap>& currentBucket,
- const std::set<const HashableDimensionKey>& trueList,
- const std::set<const HashableDimensionKey>& falseList) {
- for (HashableDimensionKey key : trueList) {
+ const std::set<const MetricDimensionKey>& trueList,
+ const std::set<const MetricDimensionKey>& falseList) {
+ for (MetricDimensionKey key : trueList) {
if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
return false;
}
}
- for (HashableDimensionKey key : falseList) {
+ for (MetricDimensionKey key : falseList) {
if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
return false;
}
@@ -100,7 +100,7 @@
void checkRefractoryTimes(AnomalyTracker& tracker,
const int64_t& currTimestampNs,
const int32_t& refractoryPeriodSec,
- const std::unordered_map<HashableDimensionKey, int64_t>& timestamps) {
+ const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
for (const auto& kv : timestamps) {
if (kv.second < 0) {
// Make sure that, if there is a refractory period, it is already past.
@@ -124,9 +124,9 @@
alert.set_trigger_if_sum_gt(2);
AnomalyTracker anomalyTracker(alert, kConfigKey);
- HashableDimensionKey keyA = getMockDimensionKey(1, "a");
- HashableDimensionKey keyB = getMockDimensionKey(1, "b");
- HashableDimensionKey keyC = getMockDimensionKey(1, "c");
+ MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+ MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+ MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
int64_t eventTimestamp0 = 10 * NS_PER_SEC;
int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC;
@@ -269,11 +269,11 @@
alert.set_trigger_if_sum_gt(2);
AnomalyTracker anomalyTracker(alert, kConfigKey);
- HashableDimensionKey keyA = getMockDimensionKey(1, "a");
- HashableDimensionKey keyB = getMockDimensionKey(1, "b");
- HashableDimensionKey keyC = getMockDimensionKey(1, "c");
- HashableDimensionKey keyD = getMockDimensionKey(1, "d");
- HashableDimensionKey keyE = getMockDimensionKey(1, "e");
+ MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+ MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+ MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
+ MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d");
+ MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e");
std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}});
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 819f2be..d1b7b28 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -78,7 +78,7 @@
std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
const Position position,
const std::vector<int> &uids, const string& conditionName) {
- std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
+ std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
std::vector<int> uid_indexes;
switch(position) {
case Position::FIRST:
@@ -265,6 +265,9 @@
TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
for (Position position :
{ Position::ANY, Position::FIRST, Position::LAST}) {
+ FieldMatcher dimensionInCondition;
+ std::unordered_set<HashableDimensionKey> dimensionKeys;
+
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
position);
@@ -307,7 +310,8 @@
const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by this uid
@@ -361,7 +365,8 @@
// query again
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
@@ -369,6 +374,9 @@
}
TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+ FieldMatcher dimensionInCondition;
+ std::unordered_set<HashableDimensionKey> dimensionKeys;
+
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
Position::ANY /* position */);
@@ -410,7 +418,8 @@
ConditionKey queryKey;
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by this uid
@@ -452,13 +461,17 @@
// query again
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ dimensionKeys.clear();
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
TEST(SimpleConditionTrackerTest, TestStopAll) {
for (Position position :
{Position::ANY, Position::FIRST, Position::LAST}) {
+ FieldMatcher dimensionInCondition;
+ std::unordered_set<HashableDimensionKey> dimensionKeys;
SimplePredicate simplePredicate = getWakeLockHeldCondition(
true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
position);
@@ -502,7 +515,8 @@
const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
// another wake lock acquired by uid2
@@ -528,8 +542,9 @@
// TEST QUERY
const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -550,15 +565,15 @@
// TEST QUERY
const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
-
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
// TEST QUERY
const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
conditionCache[0] = ConditionState::kNotEvaluated;
-
- conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+ conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+ conditionCache, dimensionKeys);
EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
}
diff --git a/cmds/statsd/tests/dimension_test.cpp b/cmds/statsd/tests/dimension_test.cpp
new file mode 100644
index 0000000..678abae
--- /dev/null
+++ b/cmds/statsd/tests/dimension_test.cpp
@@ -0,0 +1,149 @@
+// 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 "dimension.h"
+
+#include <gtest/gtest.h>
+
+using namespace android::os::statsd;
+
+#ifdef __ANDROID__
+
+TEST(DimensionTest, subLeafNodes) {
+ DimensionsValue dimension;
+ int tagId = 100;
+ dimension.set_field(tagId);
+ auto child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(1);
+ child->set_value_int(2000);
+
+ child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(3);
+ child->set_value_str("test");
+
+ child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(4);
+ auto grandChild = child->mutable_value_tuple()->add_dimensions_value();
+ grandChild->set_field(1);
+ grandChild->set_value_float(1.3f);
+ grandChild = child->mutable_value_tuple()->add_dimensions_value();
+ grandChild->set_field(3);
+ grandChild->set_value_str("tag");
+
+ child = dimension.mutable_value_tuple()->add_dimensions_value();
+ child->set_field(6);
+ child->set_value_bool(false);
+
+ DimensionsValue sub_dimension;
+ FieldMatcher matcher;
+
+ // Tag id not matched.
+ matcher.set_field(tagId + 1);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field not exist.
+ matcher.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(5);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field exists.
+ matcher.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(3);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field exists.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(6);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Field exists.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(1);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Not leaf field.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ matcher.add_child()->set_field(4);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Grand-child leaf field not exist.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ auto childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(2);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Grand-child leaf field.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(1);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Multiple grand-child fields.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ childMatcher->add_child()->set_field(1);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Multiple fields.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ childMatcher->add_child()->set_field(1);
+ matcher.add_child()->set_field(3);
+ EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+ // Subset of the fields not exist.
+ matcher.Clear();
+ sub_dimension.Clear();
+ matcher.set_field(tagId);
+ childMatcher = matcher.add_child();
+ childMatcher->set_field(4);
+ childMatcher->add_child()->set_field(3);
+ childMatcher->add_child()->set_field(1);
+ matcher.add_child()->set_field(2);
+ EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
new file mode 100644
index 0000000..b5d48ef
--- /dev/null
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -0,0 +1,725 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateCountMetricWithNoLinkConfig() {
+ StatsdConfig config;
+ auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ *config.add_predicate() = screenIsOffPredicate;
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by any attribution node and both by uid and tag.
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateAttributionUidAndTagDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(holdingWakelockPredicate, combinationPredicate);
+
+ auto metric = config.add_count_metric();
+ metric->set_id(StringToId("ScreenBrightnessChangeMetric"));
+ metric->set_what(screenBrightnessChangeAtomMatcher.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_what() = CreateDimensions(
+ android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ metric->set_bucket(ONE_MINUTE);
+ return config;
+}
+
+} // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) {
+ ConfigKey cfgKey;
+ auto config = CreateCountMetricWithNoLinkConfig();
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 1));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs - 10));
+
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 200));
+ events.push_back(CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1));
+
+ events.push_back(CreateAcquireWakelockEvent(
+ attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 100));
+ events.push_back(CreateReleaseWakelockEvent(
+ attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 50));
+
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 123, bucketStartTimeNs + 11));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 123, bucketStartTimeNs + 101));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 123, bucketStartTimeNs + 201));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + 203));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + bucketSizeNs - 99));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + bucketSizeNs - 2));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + bucketSizeNs - 1));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 456, bucketStartTimeNs + bucketSizeNs + 2));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + 2 * bucketSizeNs - 11));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + 2 * bucketSizeNs - 9));
+ events.push_back(CreateScreenBrightnessChangedEvent(
+ 789, bucketStartTimeNs + 2 * bucketSizeNs - 1));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+ EXPECT_EQ(countMetrics.data_size(), 7);
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs );
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 3);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).count(), 1);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+ data = countMetrics.data(5);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+ data = countMetrics.data(6);
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+ ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+}
+
+namespace {
+
+StatsdConfig CreateCountMetricWithLinkConfig() {
+ StatsdConfig config;
+ auto appCrashMatcher = CreateProcessCrashAtomMatcher();
+ *config.add_atom_matcher() = appCrashMatcher;
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field*/);
+
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+ auto metric = config.add_count_metric();
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_id(StringToId("AppCrashMetric"));
+ metric->set_what(appCrashMatcher.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_what() = CreateDimensions(
+ android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */});
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+ // Links between crash atom and condition of app is in syncing.
+ auto links = metric->add_links();
+ links->set_condition(isSyncingPredicate.id());
+ auto dimensionWhat = links->mutable_fields_in_what();
+ dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ return config;
+}
+
+} // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) {
+ ConfigKey cfgKey;
+ auto config = CreateCountMetricWithLinkConfig();
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 11));
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 101));
+ events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 101));
+
+ events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 201));
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 211));
+ events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 211));
+
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 401));
+ events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 401));
+ events.push_back(CreateAppCrashEvent(555, bucketStartTimeNs + 401));
+
+ events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + bucketSizeNs + 301));
+ events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + bucketSizeNs + 301));
+
+ events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701));
+
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 700));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 300));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+ bucketStartTimeNs + bucketSizeNs - 1));
+
+ events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400));
+ events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 600));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::CountMetricDataWrapper countMetrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+ EXPECT_EQ(countMetrics.data_size(), 5);
+ auto data = countMetrics.data(0);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(1);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(2);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 222);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 2);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = countMetrics.data(3);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).count(), 1);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ data = countMetrics.data(4);
+ EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 777);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).count(), 1);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType aggregationType) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
+ *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ auto inBatterySaverModePredicate = CreateBatterySaverModePredicate();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field */);
+
+ *config.add_predicate() = inBatterySaverModePredicate;
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+ auto metric = config.add_duration_metric();
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_id(StringToId("BatterySaverModeDurationMetric"));
+ metric->set_what(inBatterySaverModePredicate.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ return config;
+}
+
+} // namespace
+
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) {
+ for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+ ConfigKey cfgKey;
+ auto config = CreateDurationMetricConfigNoLink(aggregationType);
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 1));
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 101));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 110));
+
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 201));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 500));
+
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 600));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 850));
+
+ events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870));
+ events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900));
+
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 800));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 300));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+ bucketStartTimeNs + bucketSizeNs - 1));
+
+ events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
+ events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 700));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::DurationMetricDataWrapper metrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
+
+ EXPECT_EQ(metrics.data_size(), 3);
+ auto data = metrics.data(0);
+ EXPECT_FALSE(data.dimensions_in_what().has_field());
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ data = metrics.data(1);
+ EXPECT_FALSE(data.dimensions_in_what().has_field());
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ data = metrics.data(2);
+ EXPECT_FALSE(data.dimensions_in_what().has_field());
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ }
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType aggregationType) {
+ StatsdConfig config;
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+ auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+ auto isSyncingPredicate = CreateIsSyncingPredicate();
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field */);
+
+ auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+
+ *config.add_predicate() = screenIsOffPredicate;
+ *config.add_predicate() = isSyncingPredicate;
+ *config.add_predicate() = isInBackgroundPredicate;
+ auto combinationPredicate = config.add_predicate();
+ combinationPredicate->set_id(987654);
+ combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+ addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+ addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+ auto metric = config.add_duration_metric();
+ metric->set_bucket(ONE_MINUTE);
+ metric->set_id(StringToId("AppInBackgroundMetric"));
+ metric->set_what(isInBackgroundPredicate.id());
+ metric->set_condition(combinationPredicate->id());
+ *metric->mutable_dimensions_in_what() = CreateDimensions(
+ android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+ *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+ // Links between crash atom and condition of app is in syncing.
+ auto links = metric->add_links();
+ links->set_condition(isSyncingPredicate.id());
+ auto dimensionWhat = links->mutable_fields_in_what();
+ dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ return config;
+}
+
+} // namespace
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) {
+ for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+ ConfigKey cfgKey;
+ auto config = CreateDurationMetricConfigWithLink(aggregationType);
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+ std::vector<AttributionNode> attributions1 =
+ {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(222, "GMSCoreModule2")};
+
+ std::vector<AttributionNode> attributions2 =
+ {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+ CreateAttribution(555, "GMSCoreModule2")};
+
+ std::vector<std::unique_ptr<LogEvent>> events;
+
+ events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 101));
+ events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + 110));
+
+ events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 201));
+ events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + bucketSizeNs + 100));
+
+ events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399));
+ events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800));
+
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+ events.push_back(CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 801));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 300));
+
+ events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+ events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+ bucketStartTimeNs + bucketSizeNs - 1));
+
+ events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
+ events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+ bucketStartTimeNs + bucketSizeNs + 700));
+
+ sortLogEventsByTimestamp(&events);
+
+ for (const auto& event : events) {
+ processor->OnLogEvent(event.get());
+ }
+
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+ EXPECT_EQ(reports.reports_size(), 1);
+ EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+ StatsLogReport::DurationMetricDataWrapper metrics;
+ sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
+
+ EXPECT_EQ(metrics.data_size(), 3);
+ auto data = metrics.data(0);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ EXPECT_FALSE(data.dimensions_in_condition().has_field());
+ EXPECT_EQ(data.bucket_info_size(), 1);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+ data = metrics.data(1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+ data = metrics.data(2);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+ EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
+ ValidateAttributionUidAndTagDimension(
+ data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+ EXPECT_EQ(data.bucket_info_size(), 2);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401);
+ EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+ EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+ EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+ EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+ }
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 4504a95..233031c 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -44,9 +44,10 @@
auto screenIsOffPredicate = CreateScreenIsOffPredicate();
auto isSyncingPredicate = CreateIsSyncingPredicate();
- *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() =
- CreateDimensions(
- android::util::SYNC_STATE_CHANGED, {1 /* uid field */, 2 /* name field*/});
+ auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+ *syncDimension = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+ syncDimension->add_child()->set_field(2 /* name field*/);
auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
*isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
@@ -78,9 +79,8 @@
auto dimensionWhat = links->mutable_fields_in_what();
dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
- auto dimensionCondition = links->mutable_fields_in_condition();
- dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED);
- dimensionCondition->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::SYNC_STATE_CHANGED, {Position::FIRST});
// Links between crash atom and condition of app is in background.
links = countMetric->add_links();
@@ -88,7 +88,7 @@
dimensionWhat = links->mutable_fields_in_what();
dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
dimensionWhat->add_child()->set_field(1); // uid field.
- dimensionCondition = links->mutable_fields_in_condition();
+ auto dimensionCondition = links->mutable_fields_in_condition();
dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
dimensionCondition->add_child()->set_field(1); // uid field.
return config;
@@ -132,12 +132,14 @@
CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
bucketStartTimeNs + 2 * bucketSizeNs - 100);
+ std::vector<AttributionNode> attributions =
+ {CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
auto syncOnEvent1 =
- CreateSyncStartEvent(appUid, "ReadEmail", bucketStartTimeNs + 50);
+ CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
auto syncOffEvent1 =
- CreateSyncEndEvent(appUid, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
+ CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
auto syncOnEvent2 =
- CreateSyncStartEvent(appUid, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+ CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
auto moveToBackgroundEvent1 =
CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 4ad2097..50b3532 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -67,9 +67,9 @@
// Flushes.
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -80,10 +80,10 @@
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY].size());
- const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY][1];
+ EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
EXPECT_EQ(1LL, bucketInfo2.mCount);
@@ -91,9 +91,9 @@
// nothing happens in bucket 3. we should not record anything for bucket 3.
countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(2UL, buckets3.size());
}
@@ -124,10 +124,10 @@
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
{
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -167,9 +167,9 @@
{getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+ EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
- EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+ EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
bucketStartTimeNs);
@@ -181,9 +181,9 @@
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
- EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
countProducer.mPastBuckets.end());
- const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets.size());
const auto& bucketInfo = buckets[0];
EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -229,13 +229,13 @@
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// One event in bucket #2. No alarm as bucket #0 is trashed out.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// Two events in bucket #3.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
@@ -244,13 +244,13 @@
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
// Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event5.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event7.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
}
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index a59f1fe..c9fe252 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -62,9 +62,9 @@
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
- EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
durationProducer.mPastBuckets.end());
- const auto& buckets = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(2UL, buckets.size());
EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -107,9 +107,9 @@
durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
- EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+ EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
durationProducer.mPastBuckets.end());
- const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+ const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
EXPECT_EQ(1UL, buckets2.size());
EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index da00cae..3deab37 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -114,9 +114,9 @@
key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+ EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
- EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+ EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 4533ac6..58be5b0 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -218,7 +218,7 @@
EXPECT_EQ(13L,
gaugeProducer.mCurrentSlicedBucket->begin()->
second.front().mFields->begin()->second.value_int());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
std::shared_ptr<LogEvent> event2 =
std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20);
@@ -231,7 +231,7 @@
EXPECT_EQ(15L,
gaugeProducer.mCurrentSlicedBucket->begin()->
second.front().mFields->begin()->second.value_int());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
std::shared_ptr<LogEvent> event3 =
@@ -245,7 +245,7 @@
EXPECT_EQ(26L,
gaugeProducer.mCurrentSlicedBucket->begin()->
second.front().mFields->begin()->second.value_int());
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
// The event4 does not have the gauge field. Thus the current bucket value is 0.
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 0772b0d40..203f028 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -41,22 +41,24 @@
const int TagId = 1;
-const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1");
-const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
-const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
-const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
// Event starts again. This would not change anything as it already starts.
@@ -75,16 +77,22 @@
}
TEST(MaxDurationTrackerTest, TestStopAll) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
@@ -105,21 +113,26 @@
}
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, false, {});
// The event starts.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
- // Starts again. Does not change anything.
+ // Starts again. Does not DEFAULT_DIMENSION_KEY anything.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1,
ConditionKey());
@@ -135,16 +148,21 @@
}
TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {});
// 2 starts
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
@@ -160,7 +178,8 @@
EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
// real stop now.
- tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+ tracker.noteStop(DEFAULT_DIMENSION_KEY,
+ bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
EXPECT_EQ(3u, buckets[eventKey].size());
@@ -170,16 +189,20 @@
}
TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
+ const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conditionKey1;
- HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps");
+ MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
- EXPECT_CALL(*wizard, query(_, conditionKey1)) // #4
+ EXPECT_CALL(*wizard, query(_, conditionKey1, _, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -187,8 +210,8 @@
int64_t durationTimeNs = 2 * 1000;
int64_t metricId = 1;
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
- bucketSizeNs, true, {});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, true, {});
EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
@@ -204,6 +227,10 @@
}
TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+ const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+ const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+ FieldMatcher dimensionInCondition;
int64_t metricId = 1;
Alert alert;
alert.set_id(101);
@@ -213,7 +240,7 @@
const int32_t refPeriodSec = 1;
alert.set_refractory_period_secs(refPeriodSec);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -221,8 +248,8 @@
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
- bucketSizeNs, false, {anomalyTracker});
+ MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey());
tracker.noteStop(key1, eventStartTimeNs + 10, false);
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 6b8893e..80e16a1 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -38,24 +38,26 @@
const ConfigKey kConfigKey(0, 12345);
const int TagId = 1;
const int64_t metricId = 123;
-const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
-
-const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
-const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
-const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
TEST(OringDurationTrackerTest, TestDurationOverlap) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketSizeNs, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -71,16 +73,23 @@
}
TEST(OringDurationTrackerTest, TestDurationNested) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
@@ -95,16 +104,23 @@
}
TEST(OringDurationTrackerTest, TestStopAll) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey()); // overlapping wl
@@ -118,17 +134,24 @@
}
TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, false, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, false, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -150,23 +173,30 @@
}
TEST(OringDurationTrackerTest, TestDurationConditionChange) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
- EXPECT_CALL(*wizard, query(_, key1)) // #4
+ EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketSizeNs, true, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, true, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
@@ -181,25 +211,32 @@
}
TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
- EXPECT_CALL(*wizard, query(_, key1))
+ EXPECT_CALL(*wizard, query(_, key1, _, _))
.Times(2)
.WillOnce(Return(ConditionState::kFalse))
.WillOnce(Return(ConditionState::kTrue));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
uint64_t durationTimeNs = 2 * 1000;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
- bucketStartTimeNs, bucketSizeNs, true, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ false, bucketStartTimeNs, bucketSizeNs, true, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
// condition to false; record duration 5n
@@ -216,22 +253,29 @@
}
TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey key1;
key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
- EXPECT_CALL(*wizard, query(_, key1)) // #4
+ EXPECT_CALL(*wizard, query(_, key1, _, _)) // #4
.WillOnce(Return(ConditionState::kFalse));
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
uint64_t bucketStartTimeNs = 10000000000;
uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, true, {});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, true, {});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
@@ -249,6 +293,13 @@
}
TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -256,7 +307,7 @@
alert.set_num_buckets(2);
alert.set_refractory_period_secs(1);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -264,8 +315,8 @@
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
- bucketSizeNs, true, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker});
// Nothing in the past bucket.
tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
@@ -310,6 +361,12 @@
}
TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -318,7 +375,7 @@
const int32_t refPeriodSec = 45;
alert.set_refractory_period_secs(refPeriodSec);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -326,8 +383,8 @@
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
- bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
@@ -352,6 +409,13 @@
}
TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
+ const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+ const std::vector<HashableDimensionKey> kConditionKey1 =
+ {getMockedDimensionKey(TagId, 1, "maps")};
+ const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+ const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+ FieldMatcher dimensionInCondition;
Alert alert;
alert.set_id(101);
alert.set_metric_id(1);
@@ -360,7 +424,7 @@
const int32_t refPeriodSec = 45;
alert.set_refractory_period_secs(refPeriodSec);
- unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+ unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
ConditionKey conkey;
conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
@@ -369,8 +433,9 @@
uint64_t bucketSizeNs = 30 * NS_PER_SEC;
sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
- OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
- bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+ OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+ true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false,
+ {anomalyTracker});
tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index fff3dbf..55c078d 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -299,26 +299,26 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
// Value sum == 30 <= 130.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// One event in bucket #2. No alarm as bucket #0 is trashed out.
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
// Value sum == 130 <= 130.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
// Three events in bucket #3.
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
// Anomaly at event 4 since Value sum == 131 > 130!
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
// Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
// Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
- EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+ EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
}
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
index fc7245c..ab9345a 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
@@ -26,6 +26,13 @@
return HashableDimensionKey(dimensionsValue);
}
+MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, string value) {
+ DimensionsValue dimensionsValue;
+ dimensionsValue.set_field(tagId);
+ dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key);
+ dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value);
+ return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
+}
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 23e86f9..0a97456 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -25,10 +25,12 @@
class MockConditionWizard : public ConditionWizard {
public:
- MOCK_METHOD2(
+ MOCK_METHOD4(
query,
ConditionState(const int conditionIndex,
- const ConditionKey& conditionParameters));
+ const ConditionKey& conditionParameters,
+ const FieldMatcher& dimensionFields,
+ std::unordered_set<HashableDimensionKey> *dimensionKeySet));
};
class MockStatsPullerManager : public StatsPullerManager {
@@ -39,6 +41,7 @@
};
HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
+MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
} // namespace statsd
} // namespace os
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 9f4582d..13055cb 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <gtest/gtest.h>
#include "statsd_test_util.h"
namespace android {
@@ -27,6 +26,22 @@
return atom_matcher;
}
+AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
+ return atom_matcher;
+}
+
+AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_id(StringToId("UidProcessStateChanged"));
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
+ return atom_matcher;
+}
+
AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
WakelockStateChanged::State state) {
AtomMatcher atom_matcher;
@@ -47,6 +62,30 @@
return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
}
+AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher(
+ const string& name, BatterySaverModeStateChanged::State state) {
+ AtomMatcher atom_matcher;
+ atom_matcher.set_id(StringToId(name));
+ auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+ simple_atom_matcher->set_atom_id(android::util::BATTERY_SAVER_MODE_STATE_CHANGED);
+ auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+ field_value_matcher->set_field(1); // State field.
+ field_value_matcher->set_eq_int(state);
+ return atom_matcher;
+}
+
+AtomMatcher CreateBatterySaverModeStartAtomMatcher() {
+ return CreateBatterySaverModeStateChangedAtomMatcher(
+ "BatterySaverModeStart", BatterySaverModeStateChanged::ON);
+}
+
+
+AtomMatcher CreateBatterySaverModeStopAtomMatcher() {
+ return CreateBatterySaverModeStateChangedAtomMatcher(
+ "BatterySaverModeStop", BatterySaverModeStateChanged::OFF);
+}
+
+
AtomMatcher CreateScreenStateChangedAtomMatcher(
const string& name, android::view::DisplayStateEnum state) {
AtomMatcher atom_matcher;
@@ -59,6 +98,7 @@
return atom_matcher;
}
+
AtomMatcher CreateScreenTurnedOnAtomMatcher() {
return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
@@ -128,6 +168,13 @@
"ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
}
+Predicate CreateBatterySaverModePredicate() {
+ Predicate predicate;
+ predicate.set_id(StringToId("BatterySaverIsOn"));
+ predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
+ predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
+ return predicate;
+}
Predicate CreateScreenIsOnPredicate() {
Predicate predicate;
@@ -218,6 +265,31 @@
return event;
}
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(
+ android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON));
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(
+ android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF));
+ event->init();
+ return event;
+}
+
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+ int level, uint64_t timestampNs) {
+ auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
+ EXPECT_TRUE(event->write(level));
+ event->init();
+ return event;
+
+}
+
std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
const std::vector<AttributionNode>& attributions, const string& wakelockName,
const WakelockStateChanged::State state, uint64_t timestampNs) {
@@ -267,9 +339,10 @@
}
std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
- const int uid, const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
+ const std::vector<AttributionNode>& attributions,
+ const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
- event->write(uid);
+ event->write(attributions);
event->write(name);
event->write(state);
event->init();
@@ -277,13 +350,13 @@
}
std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const int uid, const string& name, uint64_t timestampNs){
- return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::ON, timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs){
+ return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
}
std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const int uid, const string& name, uint64_t timestampNs) {
- return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::OFF, timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs) {
+ return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
}
std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index ff8fef0c..6638893 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -14,6 +14,7 @@
#pragma once
+#include <gtest/gtest.h>
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "statslog.h"
#include "src/logd/LogEvent.h"
@@ -26,6 +27,18 @@
// Create AtomMatcher proto to simply match a specific atom type.
AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
+// Create AtomMatcher proto for screen brightness state changed.
+AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
+
+// Create AtomMatcher proto for starting battery save mode.
+AtomMatcher CreateBatterySaverModeStartAtomMatcher();
+
+// Create AtomMatcher proto for stopping battery save mode.
+AtomMatcher CreateBatterySaverModeStopAtomMatcher();
+
+// Create AtomMatcher proto for process state changed.
+AtomMatcher CreateUidProcessStateChangedAtomMatcher();
+
// Create AtomMatcher proto for acquiring wakelock.
AtomMatcher CreateAcquireWakelockAtomMatcher();
@@ -59,6 +72,9 @@
// Create Predicate proto for screen is off.
Predicate CreateScreenIsOffPredicate();
+// Create Predicate proto for battery saver mode.
+Predicate CreateBatterySaverModePredicate();
+
// Create Predicate proto for holding wakelock.
Predicate CreateHoldingWakelockPredicate();
@@ -86,6 +102,15 @@
std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
const android::view::DisplayStateEnum state, uint64_t timestampNs);
+// Create log event for screen brightness state changed.
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+ int level, uint64_t timestampNs);
+
+// Create log event when battery saver starts.
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
+// Create log event when battery saver stops.
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
+
// Create log event for app moving to background.
std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
@@ -94,11 +119,11 @@
// Create log event when the app sync starts.
std::unique_ptr<LogEvent> CreateSyncStartEvent(
- const int uid, const string& name, uint64_t timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
// Create log event when the app sync ends.
std::unique_ptr<LogEvent> CreateSyncEndEvent(
- const int uid, const string& name, uint64_t timestampNs);
+ const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
// Create log event when the app sync ends.
std::unique_ptr<LogEvent> CreateAppCrashEvent(
@@ -136,9 +161,12 @@
template <typename T>
void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) {
- std::map<HashableDimensionKey, int> dimensionIndexMap;
+ std::map<MetricDimensionKey, int> dimensionIndexMap;
for (int i = 0; i < metricData.data_size(); ++i) {
- dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimensions_in_what(), i));
+ dimensionIndexMap.insert(std::make_pair(
+ MetricDimensionKey(HashableDimensionKey(metricData.data(i).dimensions_in_what()),
+ HashableDimensionKey(metricData.data(i).dimensions_in_condition())),
+ i));
}
for (const auto& itr : dimensionIndexMap) {
*sortedMetricData->add_data() = metricData.data(itr.second);
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index d39aa1d..57575ae 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -116,28 +116,32 @@
findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 1);
+ StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+ StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_AC);
}
});
findViewById(R.id.unplug).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, 0);
+ StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED,
+ StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_NONE);
}
});
findViewById(R.id.screen_on).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 2);
+ StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+ StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON);
}
});
findViewById(R.id.screen_off).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, 1);
+ StatsLog.write(StatsLog.SCREEN_STATE_CHANGED,
+ StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF);
}
});
@@ -255,7 +259,9 @@
}
int[] uids = new int[] {mUids[id]};
String[] tags = new String[] {"acquire"};
- StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, 0, name, 1);
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+ StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 1);");
@@ -269,7 +275,9 @@
}
int[] uids = new int[] {mUids[id]};
String[] tags = new String[] {"release"};
- StatsLog.write(10, uids, tags, 0, name, 0);
+ StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
+ StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
StringBuilder sb = new StringBuilder();
sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
.append(", ").append(name).append(", 0);");
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fdb0ac9..04c44a3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -425,6 +425,12 @@
* safely called after {@link #onPause()} and allows and application to safely
* wait until {@link #onStop()} to save persistent state.</p>
*
+ * <p class="note">For applications targeting platforms starting with
+ * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)}
+ * will always be called after {@link #onStop}, so an application may safely
+ * perform fragment transactions in {@link #onStop} and will be able to save
+ * persistent state later.</p>
+ *
* <p>For those methods that are not marked as being killable, the activity's
* process will not be killed by the system starting from the time the method
* is called and continuing after it returns. Thus an activity is in the killable
@@ -1577,8 +1583,11 @@
* call through to the default implementation, otherwise be prepared to save
* all of the state of each view yourself.
*
- * <p>If called, this method will occur before {@link #onStop}. There are
- * no guarantees about whether it will occur before or after {@link #onPause}.
+ * <p>If called, this method will occur after {@link #onStop} for applications
+ * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}.
+ * For applications targeting earlier platform versions this method will occur
+ * before {@link #onStop} and there are no guarantees about whether it will
+ * occur before or after {@link #onPause}.
*
* @param outState Bundle in which to place your saved state.
*
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 29e091b..42825f0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -488,12 +488,14 @@
}
}
- public boolean isPreHoneycomb() {
- if (activity != null) {
- return activity.getApplicationInfo().targetSdkVersion
- < android.os.Build.VERSION_CODES.HONEYCOMB;
- }
- return false;
+ private boolean isPreHoneycomb() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.HONEYCOMB;
+ }
+
+ private boolean isPreP() {
+ return activity != null && activity.getApplicationInfo().targetSdkVersion
+ < android.os.Build.VERSION_CODES.P;
}
public boolean isPersistable() {
@@ -4165,9 +4167,12 @@
* {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
*/
private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
+ // Before P onSaveInstanceState was called before onStop, starting with P it's
+ // called after. Before Honeycomb state was always saved before onPause.
final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
&& !r.isPreHoneycomb();
- if (shouldSaveState) {
+ final boolean isPreP = r.isPreP();
+ if (shouldSaveState && isPreP) {
callActivityOnSaveInstanceState(r);
}
@@ -4186,6 +4191,10 @@
r.setState(ON_STOP);
EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
r.activity.getComponentName().getClassName(), reason);
+
+ if (shouldSaveState && !isPreP) {
+ callActivityOnSaveInstanceState(r);
+ }
}
private void updateVisibility(ActivityClientRecord r, boolean show) {
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 7c53ec1..21d245d 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -16,6 +16,11 @@
package android.os;
+import static android.system.OsConstants.SPLICE_F_MORE;
+import static android.system.OsConstants.SPLICE_F_MOVE;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISREG;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.provider.DocumentsContract.Document;
@@ -29,6 +34,7 @@
import com.android.internal.annotations.VisibleForTesting;
+import libcore.io.IoUtils;
import libcore.util.EmptyArray;
import java.io.BufferedInputStream;
@@ -41,10 +47,12 @@
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
@@ -81,6 +89,14 @@
private static final File[] EMPTY = new File[0];
+ private static final boolean ENABLE_COPY_OPTIMIZATIONS = true;
+
+ private static final long COPY_CHECKPOINT_BYTES = 524288;
+
+ public interface CopyListener {
+ public void onProgress(long progress);
+ }
+
/**
* Set owner and mode of of given {@link File}.
*
@@ -185,6 +201,9 @@
return false;
}
+ /**
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+ */
@Deprecated
public static boolean copyFile(File srcFile, File destFile) {
try {
@@ -195,14 +214,19 @@
}
}
- // copy a file from srcFile to destFile, return true if succeed, return
- // false if fail
+ /**
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+ */
+ @Deprecated
public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
try (InputStream in = new FileInputStream(srcFile)) {
copyToFileOrThrow(in, destFile);
}
}
+ /**
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
+ */
@Deprecated
public static boolean copyToFile(InputStream inputStream, File destFile) {
try {
@@ -214,28 +238,153 @@
}
/**
- * Copy data from a source stream to destFile.
- * Return true if succeed, return false if failed.
+ * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
*/
- public static void copyToFileOrThrow(InputStream inputStream, File destFile)
- throws IOException {
+ @Deprecated
+ public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
if (destFile.exists()) {
destFile.delete();
}
- FileOutputStream out = new FileOutputStream(destFile);
- try {
- byte[] buffer = new byte[4096];
- int bytesRead;
- while ((bytesRead = inputStream.read(buffer)) >= 0) {
- out.write(buffer, 0, bytesRead);
- }
- } finally {
- out.flush();
+ try (FileOutputStream out = new FileOutputStream(destFile)) {
+ copy(in, out);
try {
- out.getFD().sync();
- } catch (IOException e) {
+ Os.fsync(out.getFD());
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
}
- out.close();
+ }
+ }
+
+ public static void copy(File from, File to) throws IOException {
+ try (FileInputStream in = new FileInputStream(from);
+ FileOutputStream out = new FileOutputStream(to)) {
+ copy(in, out);
+ }
+ }
+
+ public static void copy(InputStream in, OutputStream out) throws IOException {
+ copy(in, out, null, null);
+ }
+
+ public static void copy(InputStream in, OutputStream out, CopyListener listener,
+ CancellationSignal signal) throws IOException {
+ if (ENABLE_COPY_OPTIMIZATIONS) {
+ if (in instanceof FileInputStream && out instanceof FileOutputStream) {
+ copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
+ listener, signal);
+ }
+ }
+
+ // Worse case fallback to userspace
+ copyInternalUserspace(in, out, listener, signal);
+ }
+
+ public static void copy(FileDescriptor in, FileDescriptor out) throws IOException {
+ copy(in, out, null, null);
+ }
+
+ public static void copy(FileDescriptor in, FileDescriptor out, CopyListener listener,
+ CancellationSignal signal) throws IOException {
+ if (ENABLE_COPY_OPTIMIZATIONS) {
+ try {
+ final StructStat st_in = Os.fstat(in);
+ final StructStat st_out = Os.fstat(out);
+ if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
+ copyInternalSendfile(in, out, listener, signal);
+ } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
+ copyInternalSplice(in, out, listener, signal);
+ }
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ }
+
+ // Worse case fallback to userspace
+ copyInternalUserspace(in, out, listener, signal);
+ }
+
+ /**
+ * Requires one of input or output to be a pipe.
+ */
+ @VisibleForTesting
+ public static void copyInternalSplice(FileDescriptor in, FileDescriptor out,
+ CopyListener listener, CancellationSignal signal) throws ErrnoException {
+ long progress = 0;
+ long checkpoint = 0;
+
+ long t;
+ while ((t = Os.splice(in, null, out, null, COPY_CHECKPOINT_BYTES,
+ SPLICE_F_MOVE | SPLICE_F_MORE)) != 0) {
+ progress += t;
+ checkpoint += t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ }
+
+ /**
+ * Requires both input and output to be a regular file.
+ */
+ @VisibleForTesting
+ public static void copyInternalSendfile(FileDescriptor in, FileDescriptor out,
+ CopyListener listener, CancellationSignal signal) throws ErrnoException {
+ long progress = 0;
+ long checkpoint = 0;
+
+ long t;
+ while ((t = Os.sendfile(out, in, null, COPY_CHECKPOINT_BYTES)) != 0) {
+ progress += t;
+ checkpoint += t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public static void copyInternalUserspace(FileDescriptor in, FileDescriptor out,
+ CopyListener listener, CancellationSignal signal) throws IOException {
+ copyInternalUserspace(new FileInputStream(in), new FileOutputStream(out), listener, signal);
+ }
+
+ @VisibleForTesting
+ public static void copyInternalUserspace(InputStream in, OutputStream out,
+ CopyListener listener, CancellationSignal signal) throws IOException {
+ long progress = 0;
+ long checkpoint = 0;
+ byte[] buffer = new byte[8192];
+
+ int t;
+ while ((t = in.read(buffer)) != -1) {
+ out.write(buffer, 0, t);
+
+ progress += t;
+ checkpoint += t;
+
+ if (checkpoint >= COPY_CHECKPOINT_BYTES) {
+ if (signal != null) {
+ signal.throwIfCanceled();
+ }
+ if (listener != null) {
+ listener.onProgress(progress);
+ }
+ checkpoint = 0;
+ }
}
}
@@ -797,4 +946,69 @@
}
return val * pow;
}
+
+ @VisibleForTesting
+ public static class MemoryPipe extends Thread implements AutoCloseable {
+ private final FileDescriptor[] pipe;
+ private final byte[] data;
+ private final boolean sink;
+
+ private MemoryPipe(byte[] data, boolean sink) throws IOException {
+ try {
+ this.pipe = Os.pipe();
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ this.data = data;
+ this.sink = sink;
+ }
+
+ private MemoryPipe startInternal() {
+ super.start();
+ return this;
+ }
+
+ public static MemoryPipe createSource(byte[] data) throws IOException {
+ return new MemoryPipe(data, false).startInternal();
+ }
+
+ public static MemoryPipe createSink(byte[] data) throws IOException {
+ return new MemoryPipe(data, true).startInternal();
+ }
+
+ public FileDescriptor getFD() {
+ return sink ? pipe[1] : pipe[0];
+ }
+
+ public FileDescriptor getInternalFD() {
+ return sink ? pipe[0] : pipe[1];
+ }
+
+ @Override
+ public void run() {
+ final FileDescriptor fd = getInternalFD();
+ try {
+ int i = 0;
+ while (i < data.length) {
+ if (sink) {
+ i += Os.read(fd, data, i, data.length - i);
+ } else {
+ i += Os.write(fd, data, i, data.length - i);
+ }
+ }
+ } catch (IOException | ErrnoException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (sink) {
+ SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
+ }
+ IoUtils.closeQuietly(fd);
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ IoUtils.closeQuietly(getFD());
+ }
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1f0d683..ad0ce49 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1903,7 +1903,7 @@
cp.call(cr.getPackageName(), mCallSetCommand, name, arg);
String newValue = getStringForUser(cr, name, userHandle);
StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newValue, prevValue, tag,
- makeDefault ? 1 : 0, userHandle);
+ makeDefault, userHandle);
} catch (RemoteException e) {
Log.w(TAG, "Can't set key " + name + " in " + mUri, e);
return false;
diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java
index 11e1e67..e97f963a 100644
--- a/core/java/android/service/settings/suggestions/Suggestion.java
+++ b/core/java/android/service/settings/suggestions/Suggestion.java
@@ -40,6 +40,7 @@
*/
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_HAS_BUTTON,
+ FLAG_ICON_TINTABLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Flags {
@@ -49,6 +50,10 @@
* Flag for suggestion type with a single button
*/
public static final int FLAG_HAS_BUTTON = 1 << 0;
+ /**
+ * @hide
+ */
+ public static final int FLAG_ICON_TINTABLE = 1 << 1;
private final String mId;
private final CharSequence mTitle;
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 2e4cccf..5f6d9ce 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.Context;
import android.media.session.MediaController;
import android.media.update.ApiLoader;
@@ -163,14 +162,6 @@
}
/**
- * @hide
- */
- @SystemApi
- public MediaControlView2Provider getProvider() {
- return mProvider;
- }
-
- /**
* Sets MediaController instance to control corresponding MediaSession.
*/
public void setController(MediaController controller) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7def876..40dcf25b 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3880,7 +3880,8 @@
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
mIsolatedUids.put(isolatedUid, appUid);
- StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1);
+ StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid,
+ StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED);
final Uid u = getUidStatsLocked(appUid);
u.addIsolatedUid(isolatedUid);
}
@@ -3904,7 +3905,8 @@
*/
public void removeIsolatedUidLocked(int isolatedUid) {
StatsLog.write(
- StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0);
+ StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1),
+ isolatedUid, StatsLog.ISOLATED_UID_CHANGED__EVENT__REMOVED);
final int idx = mIsolatedUids.indexOfKey(isolatedUid);
if (idx >= 0) {
final int ownerUid = mIsolatedUids.valueAt(idx);
@@ -4255,10 +4257,12 @@
if (wc != null) {
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name, 1);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
} else {
StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name, 1);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
}
}
}
@@ -4298,10 +4302,12 @@
getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
if (wc != null) {
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(),
- getPowerManagerWakeLockLevel(type), name, 0);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
} else {
StatsLog.write_non_chained(StatsLog.WAKELOCK_STATE_CHANGED, uid, null,
- getPowerManagerWakeLockLevel(type), name, 0);
+ getPowerManagerWakeLockLevel(type), name,
+ StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
}
}
}
@@ -4426,7 +4432,8 @@
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 1);
+ uid, null, name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
uid = mapUid(uid);
noteLongPartialWakeLockStartInternal(name, historyName, uid);
@@ -4439,7 +4446,8 @@
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 1);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4450,7 +4458,8 @@
noteLongPartialWakeLockStartInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 1);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -4470,8 +4479,8 @@
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
- StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- uid, null, name, historyName, 0);
+ StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED, uid, null,
+ name, historyName, StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
uid = mapUid(uid);
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
@@ -4484,7 +4493,8 @@
final int uid = mapUid(workSource.get(i));
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write_non_chained(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workSource.get(i), workSource.getName(i), name, historyName, 0);
+ workSource.get(i), workSource.getName(i), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
final ArrayList<WorkChain> workChains = workSource.getWorkChains();
@@ -4494,7 +4504,8 @@
final int uid = workChain.getAttributionUid();
noteLongPartialWakeLockFinishInternal(name, historyName, uid);
StatsLog.write(StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), name, historyName, 0);
+ workChain.getUids(), workChain.getTags(), name, historyName,
+ StatsLog.LONG_PARTIAL_WAKELOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -4518,7 +4529,8 @@
long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
timer.add(deltaUptime * 1000, 1); // time in in microseconds
- StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason, deltaUptime * 1000);
+ StatsLog.write(StatsLog.KERNEL_WAKEUP_REPORTED, mLastWakeupReason,
+ /* duration_usec */ deltaUptime * 1000);
mLastWakeupReason = null;
}
}
@@ -4659,10 +4671,12 @@
mGpsNesting++;
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 1);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
} else {
StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON);
}
getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
@@ -4683,10 +4697,11 @@
}
if (workChain == null) {
- StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null, 0);
+ StatsLog.write_non_chained(StatsLog.GPS_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
} else {
StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), 0);
+ workChain.getTags(), StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF);
}
getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
@@ -4941,7 +4956,9 @@
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtime);
}
addHistoryRecordLocked(elapsedRealtime, uptime);
- StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ? 1 : 0);
+ StatsLog.write(StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED, enabled ?
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON :
+ StatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
@@ -5545,16 +5562,19 @@
if (workChain != null) {
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
if (isUnoptimized) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
}
} else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 1);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
if (isUnoptimized) {
StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 1);
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
}
}
@@ -5594,16 +5614,19 @@
if (workChain != null) {
StatsLog.write(
- StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), 0);
+ StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
if (isUnoptimized) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
} else {
- StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null, 0);
+ StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
if (isUnoptimized) {
StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
- 0);
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
}
@@ -5656,7 +5679,8 @@
for (int j = 0; j < allWorkChains.size(); ++j) {
StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
allWorkChains.get(j).getUids(),
- allWorkChains.get(j).getTags(), 0);
+ allWorkChains.get(j).getTags(),
+ StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
}
allWorkChains.clear();
}
@@ -5666,7 +5690,8 @@
for (int j = 0; j < unoptimizedWorkChains.size(); ++j) {
StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
unoptimizedWorkChains.get(j).getUids(),
- unoptimizedWorkChains.get(j).getTags(), 0);
+ unoptimizedWorkChains.get(j).getTags(),
+ StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
}
unoptimizedWorkChains.clear();
}
@@ -6011,7 +6036,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockAcquiredLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 1);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6021,7 +6047,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockAcquiredLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 1);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON);
}
}
}
@@ -6031,7 +6058,8 @@
for (int i=0; i<N; i++) {
final int uid = mapUid(ws.get(i));
noteFullWifiLockReleasedLocked(uid);
- StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i), 0);
+ StatsLog.write_non_chained(StatsLog.WIFI_LOCK_STATE_CHANGED, ws.get(i), ws.getName(i),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6041,7 +6069,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteFullWifiLockReleasedLocked(uid);
StatsLog.write(StatsLog.WIFI_LOCK_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -6052,7 +6081,7 @@
final int uid = mapUid(ws.get(i));
noteWifiScanStartedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 1);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6062,7 +6091,7 @@
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStartedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED, workChain.getUids(),
- workChain.getTags(), 1);
+ workChain.getTags(), StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__ON);
}
}
}
@@ -6073,7 +6102,7 @@
final int uid = mapUid(ws.get(i));
noteWifiScanStoppedLocked(uid);
StatsLog.write_non_chained(StatsLog.WIFI_SCAN_STATE_CHANGED, ws.get(i), ws.getName(i),
- 0);
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
final List<WorkChain> workChains = ws.getWorkChains();
@@ -6083,7 +6112,8 @@
final int uid = mapUid(workChain.getAttributionUid());
noteWifiScanStoppedLocked(uid);
StatsLog.write(StatsLog.WIFI_SCAN_STATE_CHANGED,
- workChain.getUids(), workChain.getTags(), 0);
+ workChain.getUids(), workChain.getTags(),
+ StatsLog.WIFI_SCAN_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7035,7 +7065,8 @@
}
mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON);
}
}
@@ -7045,7 +7076,8 @@
mWifiMulticastEnabled = false;
mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(
- StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null,
+ StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF);
}
}
@@ -7098,14 +7130,16 @@
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__ON);
}
public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7113,7 +7147,8 @@
public void noteResetAudioLocked(long elapsedRealtimeMs) {
if (mAudioTurnedOnTimer != null) {
mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
+ StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
}
}
@@ -7127,7 +7162,8 @@
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
+ StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON);
}
public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7135,7 +7171,7 @@
mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
- null, 0);
+ null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
}
}
}
@@ -7144,7 +7180,7 @@
if (mVideoTurnedOnTimer != null) {
mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
- 0);
+ StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
}
}
@@ -7158,7 +7194,8 @@
public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,1);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
}
public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
@@ -7166,7 +7203,7 @@
mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
- 0);
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7174,7 +7211,8 @@
public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
if (mFlashlightTurnedOnTimer != null) {
mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
+ StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
}
}
@@ -7188,14 +7226,16 @@
public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 1);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__ON);
}
public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -7203,7 +7243,8 @@
public void noteResetCameraLocked(long elapsedRealtimeMs) {
if (mCameraTurnedOnTimer != null) {
mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null, 0);
+ StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
+ StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
}
}
@@ -9777,7 +9818,8 @@
DualTimer t = mSyncStats.startObject(name);
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 1);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+ StatsLog.SYNC_STATE_CHANGED__STATE__ON);
}
}
@@ -9786,7 +9828,8 @@
if (t != null) {
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
- StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name, 0);
+ StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
+ StatsLog.SYNC_STATE_CHANGED__STATE__OFF);
}
}
}
@@ -9796,7 +9839,7 @@
if (t != null) {
t.startRunningLocked(elapsedRealtimeMs);
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 1);
+ name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED);
}
}
@@ -9806,7 +9849,7 @@
t.stopRunningLocked(elapsedRealtimeMs);
if (!t.isRunningLocked()) { // only tell statsd if truly stopped
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
- name, 0);
+ name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED);
}
}
if (mBsi.mOnBatteryTimeBase.isRunning()) {
@@ -9919,7 +9962,7 @@
t.startRunningLocked(elapsedRealtimeMs);
if (sensor != Sensor.GPS) {
StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
- 1);
+ StatsLog.SENSOR_STATE_CHANGED__STATE__ON);
}
}
@@ -9930,7 +9973,7 @@
t.stopRunningLocked(elapsedRealtimeMs);
if (sensor != Sensor.GPS) {
StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
- sensor, 0);
+ sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
}
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index f9a2341..e69a360 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -55,6 +55,8 @@
public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
/** Do not enfore hidden API access restrictions. */
public static final int DISABLE_HIDDEN_API_CHECKS = 1 << 11;
+ /** Force generation of native debugging information for backtraces. */
+ public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f70d3c2..ebb5f9f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -132,12 +132,6 @@
void clickQsTile(in ComponentName tile);
void handleSystemKey(in int key);
- /**
- * Methods to show toast messages for screen pinning
- */
- void showPinningEnterExitToast(boolean entering);
- void showPinningEscapeToast();
-
void showShutdownUi(boolean isReboot, String reason);
// Used to show the dialog when FingerprintService starts authentication
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index adf4287..cb0b53c 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -81,12 +81,6 @@
void clickTile(in ComponentName tile);
void handleSystemKey(in int key);
- /**
- * Methods to show toast messages for screen pinning
- */
- void showPinningEnterExitToast(boolean entering);
- void showPinningEscapeToast();
-
// Used to show the dialog when FingerprintService starts authentication
void showFingerprintDialog(in Bundle bundle, IFingerprintDialogReceiver receiver);
// Used to hide the dialog when a finger is authenticated
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 71550be..f8885a2 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -160,7 +160,7 @@
final File file = new File(mBasePath, name);
final FileInputStream is = new FileInputStream(file);
try {
- Streams.copy(is, zos);
+ FileUtils.copy(is, zos);
} finally {
IoUtils.closeQuietly(is);
}
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index c88cf5c..ba56d59 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -26,10 +26,11 @@
#include <SkPictureRecorder.h>
#include <hwui/AnimatedImageDrawable.h>
#include <hwui/Canvas.h>
+#include <utils/Looper.h>
using namespace android;
-static jmethodID gAnimatedImageDrawable_postOnAnimationEndMethodID;
+static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID;
// Note: jpostProcess holds a handle to the ImageDecoder.
static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
@@ -123,9 +124,9 @@
return drawable->start();
}
-static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
- drawable->stop();
+ return drawable->stop();
}
// Java's LOOP_INFINITE relies on this being the same.
@@ -137,33 +138,63 @@
drawable->setRepetitionCount(loopCount);
}
-class JniAnimationEndListener : public OnAnimationEndListener {
+class InvokeListener : public MessageHandler {
public:
- JniAnimationEndListener(JNIEnv* env, jobject javaObject) {
+ InvokeListener(JNIEnv* env, jobject javaObject) {
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
- mJavaObject = env->NewGlobalRef(javaObject);
+ // Hold a weak reference to break a cycle that would prevent GC.
+ mWeakRef = env->NewWeakGlobalRef(javaObject);
}
- ~JniAnimationEndListener() override {
+ ~InvokeListener() override {
auto* env = get_env_or_die(mJvm);
- env->DeleteGlobalRef(mJavaObject);
+ env->DeleteWeakGlobalRef(mWeakRef);
}
- void onAnimationEnd() override {
+ virtual void handleMessage(const Message&) override {
auto* env = get_env_or_die(mJvm);
- env->CallVoidMethod(mJavaObject, gAnimatedImageDrawable_postOnAnimationEndMethodID);
+ jobject localRef = env->NewLocalRef(mWeakRef);
+ if (localRef) {
+ env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID);
+ }
}
private:
JavaVM* mJvm;
- jobject mJavaObject;
+ jweak mWeakRef;
+};
+
+class JniAnimationEndListener : public OnAnimationEndListener {
+public:
+ JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) {
+ mListener = new InvokeListener(env, javaObject);
+ mLooper = std::move(looper);
+ }
+
+ void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); }
+
+private:
+ sp<InvokeListener> mListener;
+ sp<Looper> mLooper;
};
static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/,
jlong nativePtr, jobject jdrawable) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
- drawable->setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener>(
- new JniAnimationEndListener(env, jdrawable)));
+ if (!jdrawable) {
+ drawable->setOnAnimationEndListener(nullptr);
+ } else {
+ sp<Looper> looper = Looper::getForThread();
+ if (!looper.get()) {
+ doThrowISE(env,
+ "Must set AnimatedImageDrawable's AnimationCallback on a thread with a "
+ "looper!");
+ return;
+ }
+
+ drawable->setOnAnimationEndListener(
+ std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable));
+ }
}
static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
@@ -186,7 +217,7 @@
{ "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter },
{ "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning },
{ "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart },
- { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop },
+ { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop },
{ "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount },
{ "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
@@ -195,7 +226,7 @@
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable");
- gAnimatedImageDrawable_postOnAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "postOnAnimationEnd", "()V");
+ gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V");
return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index dd3e6f0..937b3ff 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -44,11 +44,9 @@
struct NativeFamilyBuilder {
NativeFamilyBuilder(uint32_t langId, int variant)
- : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)),
- allowUnsupportedFont(false) {}
+ : langId(langId), variant(static_cast<minikin::FontFamily::Variant>(variant)) {}
uint32_t langId;
minikin::FontFamily::Variant variant;
- bool allowUnsupportedFont;
std::vector<minikin::Font> fonts;
std::vector<minikin::FontVariation> axes;
};
@@ -70,22 +68,17 @@
}
std::unique_ptr<NativeFamilyBuilder> builder(
reinterpret_cast<NativeFamilyBuilder*>(builderPtr));
+ if (builder->fonts.empty()) {
+ return 0;
+ }
std::shared_ptr<minikin::FontFamily> family = std::make_shared<minikin::FontFamily>(
builder->langId, builder->variant, std::move(builder->fonts));
- if (family->getCoverage().length() == 0 && !builder->allowUnsupportedFont) {
+ if (family->getCoverage().length() == 0) {
return 0;
}
return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
}
-static void FontFamily_allowUnsupportedFont(jlong builderPtr) {
- if (builderPtr == 0) {
- return;
- }
- NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
- builder->allowUnsupportedFont = true;
-}
-
static void FontFamily_abort(jlong builderPtr) {
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
delete builder;
@@ -270,7 +263,6 @@
static const JNINativeMethod gFontFamilyMethods[] = {
{ "nInitBuilder", "(Ljava/lang/String;I)J", (void*)FontFamily_initBuilder },
{ "nCreateFamily", "(J)J", (void*)FontFamily_create },
- { "nAllowUnsupportedFont", "(J)V", (void*)FontFamily_allowUnsupportedFont },
{ "nAbort", "(J)V", (void*)FontFamily_abort },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index c79f5bd..12da273 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -36,6 +36,7 @@
#define ENCODING_AAC_ELD 15
#define ENCODING_AAC_XHE 16
#define ENCODING_AC4 17
+#define ENCODING_E_AC3_JOC 18
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -80,6 +81,8 @@
return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
case ENCODING_AC4:
return AUDIO_FORMAT_AC4;
+ // case ENCODING_E_AC3_JOC: // FIXME Not defined on the native side yet
+ // return AUDIO_FORMAT_E_AC3_JOC;
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -130,6 +133,8 @@
// return ENCODING_AAC_XHE;
case AUDIO_FORMAT_AC4:
return ENCODING_AC4;
+ // case AUDIO_FORMAT_E_AC3_JOC: // FIXME Not defined on the native side yet
+ // return ENCODING_E_AC3_JOC;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index afbc579..61a22c1 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1262,6 +1262,18 @@
return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
}
+static int android_media_AudioTrack_setPresentation(
+ JNIEnv *env, jobject thiz, jint presentationId, jint programId) {
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "AudioTrack not initialized");
+ return (jint)AUDIO_JAVA_ERROR;
+ }
+
+ return (jint)lpTrack->selectPresentation((int)presentationId, (int)programId);
+}
+
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1333,6 +1345,7 @@
{"native_getVolumeShaperState",
"(I)Landroid/media/VolumeShaper$State;",
(void *)android_media_AudioTrack_get_volume_shaper_state},
+ {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
};
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3b752c4..224503b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4423,6 +4423,15 @@
<!-- DO NOT TRANSLATE -->
<string name="date_picker_day_typeface">sans-serif-medium</string>
+ <!-- Notify use that they are in Lock-to-app -->
+ <string name="lock_to_app_toast">To unpin this screen, touch & hold Back and Overview
+ buttons</string>
+
+ <!-- Starting lock-to-app indication. -->
+ <string name="lock_to_app_start">Screen pinned</string>
+ <!-- Exting lock-to-app indication. -->
+ <string name="lock_to_app_exit">Screen unpinned</string>
+
<!-- Lock-to-app unlock pin string -->
<string name="lock_to_app_unlock_pin">Ask for PIN before unpinning</string>
<!-- Lock-to-app unlock pattern string -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fee5a80..a338f89 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -767,6 +767,9 @@
<java-symbol type="string" name="kilobyteShort" />
<java-symbol type="string" name="last_month" />
<java-symbol type="string" name="launchBrowserDefault" />
+ <java-symbol type="string" name="lock_to_app_toast" />
+ <java-symbol type="string" name="lock_to_app_start" />
+ <java-symbol type="string" name="lock_to_app_exit" />
<java-symbol type="string" name="lock_to_app_unlock_pin" />
<java-symbol type="string" name="lock_to_app_unlock_pattern" />
<java-symbol type="string" name="lock_to_app_unlock_password" />
diff --git a/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
new file mode 100644
index 0000000..4f7c924
--- /dev/null
+++ b/core/tests/benchmarks/src/android/os/FileUtilsBenchmark.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os;
+
+import static android.os.FileUtils.copyInternalSendfile;
+import static android.os.FileUtils.copyInternalSplice;
+import static android.os.FileUtils.copyInternalUserspace;
+
+import android.os.FileUtils.MemoryPipe;
+
+import com.google.caliper.BeforeExperiment;
+import com.google.caliper.Param;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+public class FileUtilsBenchmark {
+ @Param({"32", "32000", "32000000"})
+ private int mSize;
+
+ private File mSrc;
+ private File mDest;
+
+ private byte[] mData;
+
+ @BeforeExperiment
+ protected void setUp() throws Exception {
+ mSrc = new File("/data/local/tmp/src");
+ mDest = new File("/data/local/tmp/dest");
+
+ mData = new byte[mSize];
+
+ try (FileOutputStream os = new FileOutputStream(mSrc)) {
+ os.write(mData);
+ }
+ }
+
+ public void timeRegularUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null);
+ }
+ }
+ }
+
+ public void timeRegularSendfile(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalSendfile(in.getFD(), out.getFD(), null, null);
+ }
+ }
+ }
+
+ public void timePipeSourceUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (MemoryPipe in = MemoryPipe.createSource(mData);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null);
+ }
+ }
+ }
+
+ public void timePipeSourceSplice(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (MemoryPipe in = MemoryPipe.createSource(mData);
+ FileOutputStream out = new FileOutputStream(mDest)) {
+ copyInternalSplice(in.getFD(), out.getFD(), null, null);
+ }
+ }
+ }
+
+ public void timePipeSinkUserspace(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ MemoryPipe out = MemoryPipe.createSink(mData)) {
+ copyInternalUserspace(in.getFD(), out.getFD(), null, null);
+ }
+ }
+ }
+
+ public void timePipeSinkSplice(int reps) throws Exception {
+ for (int i = 0; i < reps; i++) {
+ try (FileInputStream in = new FileInputStream(mSrc);
+ MemoryPipe out = MemoryPipe.createSink(mData)) {
+ copyInternalSplice(in.getFD(), out.getFD(), null, null);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index cd20192..b7220b3 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,16 +21,19 @@
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.os.FileUtils.MemoryPipe;
import android.provider.DocumentsContract.Document;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import libcore.io.IoUtils;
+import libcore.io.Streams;
import com.google.android.collect.Sets;
@@ -40,11 +43,13 @@
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.FileWriter;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Random;
@RunWith(AndroidJUnit4.class)
public class FileUtilsTest {
@@ -56,6 +61,8 @@
private File mCopyFile;
private File mTarget;
+ private final int[] DATA_SIZES = { 32, 32_000, 32_000_000 };
+
private Context getContext() {
return InstrumentationRegistry.getContext();
}
@@ -80,7 +87,7 @@
@Test
public void testCopyFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
assertFalse(mCopyFile.exists());
FileUtils.copyFile(mTestFile, mCopyFile);
assertTrue(mCopyFile.exists());
@@ -97,6 +104,83 @@
}
@Test
+ public void testCopy_FileToFile() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File src = new File(mTarget, "src");
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+ writeFile(src, expected);
+
+ try (FileInputStream in = new FileInputStream(src);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in, out);
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_FileToPipe() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File src = new File(mTarget, "src");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+ writeFile(src, expected);
+
+ try (FileInputStream in = new FileInputStream(src);
+ MemoryPipe out = MemoryPipe.createSink(actual)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ out.join();
+ }
+
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_PipeToFile() throws Exception {
+ for (int size : DATA_SIZES) {
+ final File dest = new File(mTarget, "dest");
+
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+
+ try (MemoryPipe in = MemoryPipe.createSource(expected);
+ FileOutputStream out = new FileOutputStream(dest)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ }
+
+ actual = readFile(dest);
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
+ public void testCopy_PipeToPipe() throws Exception {
+ for (int size : DATA_SIZES) {
+ byte[] expected = new byte[size];
+ byte[] actual = new byte[size];
+ new Random().nextBytes(expected);
+
+ try (MemoryPipe in = MemoryPipe.createSource(expected);
+ MemoryPipe out = MemoryPipe.createSink(actual)) {
+ FileUtils.copy(in.getFD(), out.getFD());
+ out.join();
+ }
+
+ assertArrayEquals(expected, actual);
+ }
+ }
+
+ @Test
public void testIsFilenameSafe() throws Exception {
assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
@@ -106,7 +190,7 @@
@Test
public void testReadTextFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
@@ -127,7 +211,7 @@
@Test
public void testReadTextFileWithZeroLengthFile() throws Exception {
- stageFile(mTestFile, TEST_DATA);
+ writeFile(mTestFile, TEST_DATA);
new FileOutputStream(mTestFile).close(); // Zero out the file
assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
@@ -381,12 +465,21 @@
file.setLastModified(System.currentTimeMillis() - age);
}
- private void stageFile(File file, String data) throws Exception {
- FileWriter writer = new FileWriter(file);
- try {
- writer.write(data, 0, data.length());
- } finally {
- writer.close();
+ private void writeFile(File file, String data) throws Exception {
+ writeFile(file, data.getBytes());
+ }
+
+ private void writeFile(File file, byte[] data) throws Exception {
+ try (FileOutputStream out = new FileOutputStream(file)) {
+ out.write(data);
+ }
+ }
+
+ private byte[] readFile(File file) throws Exception {
+ try (FileInputStream in = new FileInputStream(file);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ Streams.copy(in, out);
+ return out.toByteArray();
}
}
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index d77e601..fe2b523 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -160,25 +160,6 @@
isItalic);
}
- /**
- * Allow creating unsupported FontFamily.
- *
- * For compatibility reasons, we still need to create a FontFamily object even if Minikin failed
- * to find any usable 'cmap' table for some reasons, e.g. broken 'cmap' table, no 'cmap' table
- * encoded with Unicode code points, etc. Without calling this method, the freeze() method will
- * return null if Minikin fails to find any usable 'cmap' table. By calling this method, the
- * freeze() won't fail and will create an empty FontFamily. This empty FontFamily is placed at
- * the top of the fallback chain but is never used. if we don't create this empty FontFamily
- * and put it at top, bad things (performance regressions, unexpected glyph selection) will
- * happen.
- */
- public void allowUnsupportedFont() {
- if (mBuilderPtr == 0) {
- throw new IllegalStateException("Unable to allow unsupported font.");
- }
- nAllowUnsupportedFont(mBuilderPtr);
- }
-
// TODO: Remove once internal user stop using private API.
private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
return nAddFont(builderPtr, font, ttcIndex, -1, -1);
@@ -190,9 +171,6 @@
private static native long nCreateFamily(long mBuilderPtr);
@CriticalNative
- private static native void nAllowUnsupportedFont(long builderPtr);
-
- @CriticalNative
private static native void nAbort(long mBuilderPtr);
@CriticalNative
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index ef41507..04c5295 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -818,12 +818,9 @@
if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
null /* axes */)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback
- // font selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return Typeface.DEFAULT;
+ }
final FontFamily[] families = { fontFamily };
typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
@@ -870,12 +867,9 @@
final FontFamily fontFamily = new FontFamily();
if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
- // Due to backward compatibility, even if the font is not supported by our font
- // stack, we need to place the empty font at the first place. The typeface with
- // empty font behaves different from default typeface especially in fallback font
- // selection.
- fontFamily.allowUnsupportedFont();
- fontFamily.freeze();
+ if (!fontFamily.freeze()) {
+ return Typeface.DEFAULT;
+ }
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index bd49b87..27c8fda 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -348,7 +348,9 @@
if (mState == null) {
throw new IllegalStateException("called stop on empty AnimatedImageDrawable");
}
- nStop(mState.mNativePtr);
+ if (nStop(mState.mNativePtr)) {
+ postOnAnimationEnd();
+ }
}
// Animatable2 overrides
@@ -365,21 +367,31 @@
nSetOnAnimationEndListener(mState.mNativePtr, this);
}
- mAnimationCallbacks.add(callback);
+ if (!mAnimationCallbacks.contains(callback)) {
+ mAnimationCallbacks.add(callback);
+ }
}
@Override
public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) {
- if (callback == null || mAnimationCallbacks == null) {
+ if (callback == null || mAnimationCallbacks == null
+ || !mAnimationCallbacks.remove(callback)) {
return false;
}
- return mAnimationCallbacks.remove(callback);
+ if (mAnimationCallbacks.isEmpty()) {
+ clearAnimationCallbacks();
+ }
+
+ return true;
}
@Override
public void clearAnimationCallbacks() {
- mAnimationCallbacks = null;
+ if (mAnimationCallbacks != null) {
+ mAnimationCallbacks = null;
+ nSetOnAnimationEndListener(mState.mNativePtr, null);
+ }
}
private void postOnAnimationStart() {
@@ -413,6 +425,21 @@
return mHandler;
}
+ /**
+ * Called by JNI.
+ *
+ * The JNI code has already posted this to the thread that created the
+ * callback, so no need to post.
+ */
+ @SuppressWarnings("unused")
+ private void onAnimationEnd() {
+ if (mAnimationCallbacks != null) {
+ for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
+ callback.onAnimationEnd(this);
+ }
+ }
+ }
+
private static native long nCreate(long nativeImageDecoder,
@Nullable ImageDecoder decoder, int width, int height, Rect cropRect)
@@ -432,7 +459,7 @@
@FastNative
private static native boolean nStart(long nativePtr);
@FastNative
- private static native void nStop(long nativePtr);
+ private static native boolean nStop(long nativePtr);
@FastNative
private static native void nSetLoopCount(long nativePtr, int loopCount);
// Pass the drawable down to native so it can call onAnimationEnd.
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 5356d3b..2bded9b 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -48,8 +48,10 @@
return true;
}
-void AnimatedImageDrawable::stop() {
+bool AnimatedImageDrawable::stop() {
+ bool wasRunning = mRunning;
mRunning = false;
+ return wasRunning;
}
bool AnimatedImageDrawable::isRunning() {
@@ -180,7 +182,6 @@
if (finalFrame) {
if (mEndListener) {
mEndListener->onAnimationEnd();
- mEndListener = nullptr;
}
}
}
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 9d84ed5..2fd6f40 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -68,7 +68,9 @@
// Returns true if the animation was started; false otherwise (e.g. it was
// already running)
bool start();
- void stop();
+ // Returns true if the animation was stopped; false otherwise (e.g. it was
+ // already stopped)
+ bool stop();
bool isRunning();
void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 43f46ef..a0d000d 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -44,7 +44,6 @@
minikinPaint.familyVariant = paint->getFamilyVariant();
minikinPaint.fontStyle = resolvedFace->fStyle;
minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
- minikinPaint.hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit());
return minikinPaint;
}
@@ -55,18 +54,23 @@
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
minikin::Layout layout;
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+ const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+ const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+ const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
if (mt == nullptr) {
- layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+ layout.doLayout(textBuf,range, bidiFlags, minikinPaint, startHyphen, endHyphen);
return layout;
}
- if (mt->buildLayout(minikin::U16StringPiece(buf, bufSize),
- minikin::Range(start, start + count),
- minikinPaint, bidiFlags, mtOffset, &layout)) {
+ if (mt->buildLayout(textBuf, range, minikinPaint, bidiFlags, mtOffset, startHyphen, endHyphen,
+ &layout)) {
return layout;
}
- layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint);
+ layout.doLayout(textBuf, range, bidiFlags, minikinPaint, startHyphen, endHyphen);
return layout;
}
@@ -74,8 +78,14 @@
const Typeface* typeface, const uint16_t* buf, size_t start,
size_t count, size_t bufSize, float* advances) {
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
- return minikin::Layout::measureText(buf, start, count, bufSize, bidiFlags, minikinPaint,
- advances, nullptr /* extent */);
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::Range range(start, start + count);
+ const minikin::HyphenEdit hyphenEdit = static_cast<minikin::HyphenEdit>(paint->getHyphenEdit());
+ const minikin::StartHyphenEdit startHyphen = minikin::startHyphenEdit(hyphenEdit);
+ const minikin::EndHyphenEdit endHyphen = minikin::endHyphenEdit(hyphenEdit);
+
+ return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen,
+ endHyphen, advances, nullptr /* extent */);
}
bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 018db9a..fa3f99a 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -88,11 +88,6 @@
boolean providerMeetsCriteria(String provider, in Criteria criteria);
ProviderProperties getProviderProperties(String provider);
String getNetworkProviderPackage();
- boolean isProviderEnabled(String provider);
- boolean isProviderEnabledForUser(String provider, int userId);
- boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
- boolean isLocationEnabledForUser(int userId);
- void setLocationEnabledForUser(boolean enabled, int userId);
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b07d042..f98480b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -265,6 +265,12 @@
public static final int ENCODING_AAC_XHE = 16;
/** Audio data format: AC-4 sync frame transport format */
public static final int ENCODING_AC4 = 17;
+ /** Audio data format: E-AC-3-JOC compressed
+ * E-AC-3-JOC streams can be decoded by downstream devices supporting {@link #ENCODING_E_AC3}.
+ * Use {@link #ENCODING_E_AC3} as the AudioTrack encoding when the downstream device
+ * supports {@link #ENCODING_E_AC3} but not {@link #ENCODING_E_AC3_JOC}.
+ **/
+ public static final int ENCODING_E_AC3_JOC = 18;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -512,6 +518,7 @@
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -537,6 +544,7 @@
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
@@ -564,6 +572,7 @@
return true;
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -593,6 +602,7 @@
return true;
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_MP3:
@@ -829,6 +839,7 @@
case ENCODING_PCM_FLOAT:
case ENCODING_AC3:
case ENCODING_E_AC3:
+ case ENCODING_E_AC3_JOC:
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
@@ -1044,6 +1055,7 @@
ENCODING_PCM_FLOAT,
ENCODING_AC3,
ENCODING_E_AC3,
+ ENCODING_E_AC3_JOC,
ENCODING_DTS,
ENCODING_DTS_HD,
ENCODING_IEC61937,
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
new file mode 100644
index 0000000..4652c18
--- /dev/null
+++ b/media/java/android/media/AudioPresentation.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+
+/**
+ * The AudioPresentation class encapsulates the information that describes an audio presentation
+ * which is available in next generation audio content.
+ *
+ * Used by {@link MediaExtractor} {@link MediaExtractor#getAudioPresentations(int)} and
+ * {@link AudioTrack} {@link AudioTrack#setPresentation(AudioPresentation)} to query available
+ * presentations and to select one.
+ *
+ * A list of available audio presentations in a media source can be queried using
+ * {@link MediaExtractor#getAudioPresentations(int)}. This list can be presented to a user for
+ * selection.
+ * An AudioPresentation can be passed to an offloaded audio decoder via
+ * {@link AudioTrack#setPresentation(AudioPresentation)} to request decoding of the selected
+ * presentation. An audio stream may contain multiple presentations that differ by language,
+ * accessibility, end point mastering and dialogue enhancement. An audio presentation may also have
+ * a set of description labels in different languages to help the user to make an informed
+ * selection.
+ */
+public final class AudioPresentation {
+ private final int mPresentationId;
+ private final int mProgramId;
+ private final Map<String, String> mLabels;
+ private final String mLanguage;
+
+ /** @hide */
+ @IntDef(
+ value = {
+ MASTERING_NOT_INDICATED,
+ MASTERED_FOR_STEREO,
+ MASTERED_FOR_SURROUND,
+ MASTERED_FOR_3D,
+ MASTERED_FOR_HEADPHONE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MasteringIndicationType {}
+
+ private final @MasteringIndicationType int mMasteringIndication;
+ private final boolean mAudioDescriptionAvailable;
+ private final boolean mSpokenSubtitlesAvailable;
+ private final boolean mDialogueEnhancementAvailable;
+
+ /**
+ * No preferred reproduction channel layout.
+ */
+ public static final int MASTERING_NOT_INDICATED = 0;
+ /**
+ * Stereo speaker layout.
+ */
+ public static final int MASTERED_FOR_STEREO = 1;
+ /**
+ * Two-dimensional (e.g. 5.1) speaker layout.
+ */
+ public static final int MASTERED_FOR_SURROUND = 2;
+ /**
+ * Three-dimensional (e.g. 5.1.2) speaker layout.
+ */
+ public static final int MASTERED_FOR_3D = 3;
+ /**
+ * Prerendered for headphone playback.
+ */
+ public static final int MASTERED_FOR_HEADPHONE = 4;
+
+ AudioPresentation(int presentationId,
+ int programId,
+ Map<String, String> labels,
+ String language,
+ @MasteringIndicationType int masteringIndication,
+ boolean audioDescriptionAvailable,
+ boolean spokenSubtitlesAvailable,
+ boolean dialogueEnhancementAvailable) {
+ this.mPresentationId = presentationId;
+ this.mProgramId = programId;
+ this.mLanguage = language;
+ this.mMasteringIndication = masteringIndication;
+ this.mAudioDescriptionAvailable = audioDescriptionAvailable;
+ this.mSpokenSubtitlesAvailable = spokenSubtitlesAvailable;
+ this.mDialogueEnhancementAvailable = dialogueEnhancementAvailable;
+
+ this.mLabels = new HashMap<String, String>(labels);
+ }
+
+ /**
+ * The framework uses this presentation id to select an audio presentation rendered by a
+ * decoder. Presentation id is typically sequential, but does not have to be.
+ * @hide
+ */
+ public int getPresentationId() {
+ return mPresentationId;
+ }
+
+ /**
+ * The framework uses this program id to select an audio presentation rendered by a decoder.
+ * Program id can be used to further uniquely identify the presentation to a decoder.
+ * @hide
+ */
+ public int getProgramId() {
+ return mProgramId;
+ }
+
+ /**
+ * @return a map of available text labels for this presentation. Each label is indexed by its
+ * locale corresponding to the language code as specified by ISO 639-2 [42]. Either ISO 639-2/B
+ * or ISO 639-2/T could be used.
+ */
+ public Map<Locale, String> getLabels() {
+ Map<Locale, String> localeLabels = new HashMap<>();
+ for (Map.Entry<String, String> entry : mLabels.entrySet()) {
+ localeLabels.put(new Locale(entry.getKey()), entry.getValue());
+ }
+ return localeLabels;
+ }
+
+ /**
+ * @return the locale corresponding to audio presentation's ISO 639-1/639-2 language code.
+ */
+ public Locale getLocale() {
+ return new Locale(mLanguage);
+ }
+
+ /**
+ * @return the mastering indication of the audio presentation.
+ * See {@link #MASTERING_NOT_INDICATED}, {@link #MASTERED_FOR_STEREO},
+ * {@link #MASTERED_FOR_SURROUND}, {@link #MASTERED_FOR_3D}, {@link #MASTERED_FOR_HEADPHONE}
+ */
+ @MasteringIndicationType
+ public int getMasteringIndication() {
+ return mMasteringIndication;
+ }
+
+ /**
+ * Indicates whether an audio description for the visually impaired is available.
+ * @return {@code true} if audio description is available.
+ */
+ public boolean hasAudioDescription() {
+ return mAudioDescriptionAvailable;
+ }
+
+ /**
+ * Indicates whether spoken subtitles for the visually impaired are available.
+ * @return {@code true} if spoken subtitles are available.
+ */
+ public boolean hasSpokenSubtitles() {
+ return mSpokenSubtitlesAvailable;
+ }
+
+ /**
+ * Indicates whether dialogue enhancement is available.
+ * @return {@code true} if dialogue enhancement is available.
+ */
+ public boolean hasDialogueEnhancement() {
+ return mDialogueEnhancementAvailable;
+ }
+}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 4e9ce8e..8e822a5 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2008,6 +2008,25 @@
}
/**
+ * Sets the audio presentation.
+ * If the audio presentation is invalid then {@link #ERROR_BAD_VALUE} will be returned.
+ * If a multi-stream decoder (MSD) is not present, or the format does not support
+ * multiple presentations, then {@link #ERROR_INVALID_OPERATION} will be returned.
+ * @param presentation see {@link AudioPresentation}. In particular, id should be set.
+ * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
+ * {@link #ERROR_INVALID_OPERATION}
+ * @throws IllegalArgumentException if the audio presentation is null.
+ * @throws IllegalStateException if track is not initialized.
+ */
+ public int setPresentation(@NonNull AudioPresentation presentation) {
+ if (presentation == null) {
+ throw new IllegalArgumentException("audio presentation is null");
+ }
+ return native_setPresentation(presentation.getPresentationId(),
+ presentation.getProgramId());
+ }
+
+ /**
* Sets the initialization state of the instance. This method was originally intended to be used
* in an AudioTrack subclass constructor to set a subclass-specific post-initialization state.
* However, subclasses of AudioTrack are no longer recommended, so this method is obsolete.
@@ -3245,6 +3264,7 @@
@NonNull VolumeShaper.Operation operation);
private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
+ private native final int native_setPresentation(int presentationId, int programId);
//---------------------------------------------------------
// Utility methods
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 174d6a3..4919eeb 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -22,6 +22,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.media.AudioPresentation;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaHTTPService;
@@ -40,6 +41,7 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -396,6 +398,17 @@
}
/**
+ * Get the list of available audio presentations for the track.
+ * @param trackIndex index of the track.
+ * @return a list of available audio presentations for a given valid audio track index.
+ * The list will be empty if the source does not contain any audio presentations.
+ */
+ @NonNull
+ public List<AudioPresentation> getAudioPresentations(int trackIndex) {
+ return new ArrayList<AudioPresentation>();
+ }
+
+ /**
* Get the PSSH info if present.
* @return a map of uuid-to-bytes, with the uuid specifying
* the crypto scheme, and the bytes being the data specific to that scheme.
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 3eb9d52..fefa1ed 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -28,11 +28,13 @@
import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -47,22 +49,17 @@
import com.android.internal.database.SortCursor;
-import libcore.io.Streams;
-
import java.io.Closeable;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
-import static android.content.ContentProvider.maybeAddUserId;
-import static android.content.pm.PackageManager.NameNotFoundException;
-
/**
* RingtoneManager provides access to ringtones, notification, and other types
* of sounds. It manages querying the different media providers and combines the
@@ -855,7 +852,7 @@
final Uri cacheUri = getCacheForType(type, context.getUserId());
try (InputStream in = openRingtone(context, ringtoneUri);
OutputStream out = resolver.openOutputStream(cacheUri)) {
- Streams.copy(in, out);
+ FileUtils.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to cache ringtone: " + e);
}
@@ -960,7 +957,7 @@
// Copy contents to external ringtone storage. Throws IOException if the copy fails.
try (final InputStream input = mContext.getContentResolver().openInputStream(fileUri);
final OutputStream output = new FileOutputStream(outFile)) {
- Streams.copy(input, output);
+ FileUtils.copy(input, output);
}
// Tell MediaScanner about the new file. Wait for it to assign a {@link Uri}.
diff --git a/media/jni/android_media_AudioPresentation.h b/media/jni/android_media_AudioPresentation.h
new file mode 100644
index 0000000..71b8dac
--- /dev/null
+++ b/media/jni/android_media_AudioPresentation.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+#define _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
+
+#include "jni.h"
+
+#include <media/AudioPresentationInfo.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <nativehelper/ScopedLocalRef.h>
+
+namespace android {
+
+struct JAudioPresentationInfo {
+ struct fields_t {
+ jclass clazz;
+ jmethodID constructID;
+
+ // list parameters
+ jclass listclazz;
+ jmethodID listConstructId;
+ jmethodID listAddId;
+
+ void init(JNIEnv *env) {
+ jclass lclazz = env->FindClass("android/media/AudioPresentation");
+ if (lclazz == NULL) {
+ return;
+ }
+
+ clazz = (jclass)env->NewGlobalRef(lclazz);
+ if (clazz == NULL) {
+ return;
+ }
+
+ constructID = env->GetMethodID(clazz, "<init>",
+ "(IILjava/util/Map;Ljava/lang/String;IZZZ)V");
+ env->DeleteLocalRef(lclazz);
+
+ // list objects
+ jclass llistclazz = env->FindClass("java/util/ArrayList");
+ CHECK(llistclazz != NULL);
+ listclazz = static_cast<jclass>(env->NewGlobalRef(llistclazz));
+ CHECK(listclazz != NULL);
+ listConstructId = env->GetMethodID(listclazz, "<init>", "()V");
+ CHECK(listConstructId != NULL);
+ listAddId = env->GetMethodID(listclazz, "add", "(Ljava/lang/Object;)Z");
+ CHECK(listAddId != NULL);
+ env->DeleteLocalRef(llistclazz);
+ }
+
+ void exit(JNIEnv *env) {
+ env->DeleteGlobalRef(clazz);
+ clazz = NULL;
+ env->DeleteGlobalRef(listclazz);
+ listclazz = NULL;
+ }
+ };
+
+ static status_t ConvertMessageToMap(JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
+ ScopedLocalRef<jclass> hashMapClazz(env, env->FindClass("java/util/HashMap"));
+
+ if (hashMapClazz.get() == NULL) {
+ return -EINVAL;
+ }
+ jmethodID hashMapConstructID =
+ env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
+
+ if (hashMapConstructID == NULL) {
+ return -EINVAL;
+ }
+ jmethodID hashMapPutID =
+ env->GetMethodID(
+ hashMapClazz.get(),
+ "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+ if (hashMapPutID == NULL) {
+ return -EINVAL;
+ }
+
+ jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
+
+ for (size_t i = 0; i < msg->countEntries(); ++i) {
+ AMessage::Type valueType;
+ const char *key = msg->getEntryNameAt(i, &valueType);
+
+ if (!strncmp(key, "android._", 9)) {
+ // don't expose private keys (starting with android._)
+ continue;
+ }
+
+ jobject valueObj = NULL;
+
+ AString val;
+ CHECK(msg->findString(key, &val));
+
+ valueObj = env->NewStringUTF(val.c_str());
+
+ if (valueObj != NULL) {
+ jstring keyObj = env->NewStringUTF(key);
+
+ env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
+
+ env->DeleteLocalRef(keyObj); keyObj = NULL;
+ env->DeleteLocalRef(valueObj); valueObj = NULL;
+ }
+ }
+
+ *map = hashMap;
+
+ return OK;
+ }
+
+ jobject asJobject(JNIEnv *env, const fields_t& fields, const AudioPresentationInfo &info) {
+ jobject list = env->NewObject(fields.listclazz, fields.listConstructId);
+
+ for (size_t i = 0; i < info.countPresentations(); ++i) {
+ const sp<AudioPresentation> &ap = info.getPresentation(i);
+ jobject jLabelObject;
+
+ sp<AMessage> labelMessage = new AMessage();
+ for (size_t i = 0; i < ap->mLabels.size(); ++i) {
+ labelMessage->setString(ap->mLabels.keyAt(i).string(),
+ ap->mLabels.valueAt(i).string());
+ }
+ if (ConvertMessageToMap(env, labelMessage, &jLabelObject) != OK) {
+ return NULL;
+ }
+ jstring jLanguage = env->NewStringUTF(ap->mLanguage.string());
+
+ jobject jValueObj = env->NewObject(fields.clazz, fields.constructID,
+ static_cast<jint>(ap->mPresentationId),
+ static_cast<jint>(ap->mProgramId),
+ jLabelObject,
+ jLanguage,
+ static_cast<jint>(ap->mMasteringIndication),
+ static_cast<jboolean>((ap->mAudioDescriptionAvailable == 1) ?
+ 1 : 0),
+ static_cast<jboolean>((ap->mSpokenSubtitlesAvailable == 1) ?
+ 1 : 0),
+ static_cast<jboolean>((ap->mDialogueEnhancementAvailable == 1) ?
+ 1 : 0));
+ if (jValueObj == NULL) {
+ env->DeleteLocalRef(jLanguage); jLanguage = NULL;
+ return NULL;
+ }
+
+ env->CallBooleanMethod(list, fields.listAddId, jValueObj);
+ env->DeleteLocalRef(jValueObj); jValueObj = NULL;
+ env->DeleteLocalRef(jLanguage); jLanguage = NULL;
+ }
+ return list;
+ }
+};
+} // namespace android
+
+#endif // _ANDROID_MEDIA_AUDIO_PRESENTATION_H_
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 8b01aef..9f165bc 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -30,6 +30,7 @@
import android.content.res.ObbScanner;
import android.os.Binder;
import android.os.Environment.UserEnvironment;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -43,7 +44,6 @@
import com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
-import libcore.io.Streams;
import java.io.File;
import java.io.FileInputStream;
@@ -260,7 +260,7 @@
in = new FileInputStream(sourcePath);
out = new ParcelFileDescriptor.AutoCloseOutputStream(
target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
- Streams.copy(in, out);
+ FileUtils.copy(in, out);
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index 7983896..c23f226 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -17,21 +17,20 @@
package com.android.settingslib.core.instrumentation;
import android.app.Activity;
-import android.content.Context;
+import android.arch.lifecycle.Lifecycle.Event;
+import android.arch.lifecycle.LifecycleObserver;
+import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Intent;
import android.os.SystemClock;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnPause;
-import com.android.settingslib.core.lifecycle.events.OnResume;
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
/**
* Logs visibility change of a fragment.
*/
-public class VisibilityLoggerMixin implements LifecycleObserver, OnResume, OnPause {
+public class VisibilityLoggerMixin implements LifecycleObserver {
private static final String TAG = "VisibilityLoggerMixin";
@@ -55,7 +54,7 @@
mMetricsFeature = metricsFeature;
}
- @Override
+ @OnLifecycleEvent(Event.ON_RESUME)
public void onResume() {
mVisibleTimestamp = SystemClock.elapsedRealtime();
if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
@@ -63,7 +62,7 @@
}
}
- @Override
+ @OnLifecycleEvent(Event.ON_PAUSE)
public void onPause() {
mVisibleTimestamp = 0;
if (mMetricsFeature != null && mMetricsCategory != METRICS_CATEGORY_UNKNOWN) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
index a264886..1ab6afe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -18,6 +18,7 @@
import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -30,6 +31,8 @@
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.TestConfig;
@@ -39,6 +42,8 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
@@ -110,6 +115,30 @@
.hidden(nullable(Context.class), anyInt());
}
+ @Test
+ public void activityShouldBecomeVisibleAndHide() {
+ ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
+ TestActivity testActivity = ac.get();
+ MockitoAnnotations.initMocks(testActivity);
+ ac.create().start().resume();
+ verify(testActivity.mMetricsFeatureProvider, times(1)).visible(any(), anyInt(), anyInt());
+ ac.pause().stop().destroy();
+ verify(testActivity.mMetricsFeatureProvider, times(1)).hidden(any(), anyInt());
+ }
+
+ public static class TestActivity extends FragmentActivity {
+ @Mock
+ MetricsFeatureProvider mMetricsFeatureProvider;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ VisibilityLoggerMixin mixin = new VisibilityLoggerMixin(
+ TestInstrumentable.TEST_METRIC, mMetricsFeatureProvider);
+ getLifecycle().addObserver(mixin);
+ super.onCreate(savedInstanceState);
+ }
+ }
+
private final class TestInstrumentable implements Instrumentable {
public static final int TEST_METRIC = 12345;
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
index a3118b0..c2b1009 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
@@ -84,25 +84,7 @@
android:layout_height="@dimen/screen_pinning_request_button_height"
android:layout_weight="0"
android:paddingStart="@dimen/screen_pinning_request_frame_padding"
- android:paddingEnd="@dimen/screen_pinning_request_frame_padding"
- android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent">
-
- <ImageView
- android:id="@+id/screen_pinning_home_bg_light"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="matrix"
- android:src="@drawable/screen_pinning_light_bg_circ" />
-
- <ImageView
- android:id="@+id/screen_pinning_home_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingEnd="@dimen/screen_pinning_request_inner_padding"
- android:paddingStart="@dimen/screen_pinning_request_inner_padding"
- android:paddingTop="@dimen/screen_pinning_request_inner_padding"
- android:scaleType="matrix"
- android:src="@drawable/screen_pinning_bg_circ" />
+ android:paddingEnd="@dimen/screen_pinning_request_frame_padding" >
<ImageView
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fadcbcd..5557f8e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1281,23 +1281,12 @@
<string name="screen_pinning_title">Screen is pinned</string>
<!-- Screen pinning dialog description. -->
<string name="screen_pinning_description">This keeps it in view until you unpin. Touch & hold Back and Overview to unpin.</string>
- <string name="screen_pinning_description_recents_invisible">This keeps it in view until you unpin. Touch & hold Back and Home to unpin.</string>
<!-- Screen pinning dialog description. -->
<string name="screen_pinning_description_accessible">This keeps it in view until you unpin. Touch & hold Overview to unpin.</string>
- <string name="screen_pinning_description_recents_invisible_accessible">This keeps it in view until you unpin. Touch & hold Home to unpin.</string>
- <!-- Notify use that they are in Lock-to-app -->
- <string name="screen_pinning_toast">To unpin this screen, touch & hold Back and Overview
- buttons</string>
- <string name="screen_pinning_toast_recents_invisible">To unpin this screen, touch & hold Back
- and Home buttons</string>
<!-- Screen pinning positive response. -->
<string name="screen_pinning_positive">Got it</string>
<!-- Screen pinning negative response. -->
<string name="screen_pinning_negative">No thanks</string>
- <!-- Enter/Exiting screen pinning indication. -->
- <string name="screen_pinning_start">Screen pinned</string>
- <string name="screen_pinning_exit">Screen unpinned</string>
-
<!-- Hide quick settings tile confirmation title -->
<string name="quick_settings_reset_confirmation_title">Hide <xliff:g id="tile_label" example="Hotspot">%1$s</xliff:g>?</string>
diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
index 43b918d..89bc82f 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
@@ -15,19 +15,13 @@
*/
package com.android.systemui;
-import android.annotation.StringRes;
import android.content.Context;
import android.view.WindowManager;
import android.widget.Toast;
-import static android.widget.Toast.Duration;
public class SysUIToast {
- public static Toast makeText(Context context, @StringRes int resId, @Duration int duration) {
- return makeText(context, context.getString(resId), duration);
- }
-
- public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
+ public static Toast makeText(Context context, CharSequence text, int duration) {
Toast toast = Toast.makeText(context, text, duration);
toast.getWindowParams().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 653e500..eedc50f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -707,6 +707,9 @@
&& !mLockPatternUtils.isLockScreenDisabled(
KeyguardUpdateMonitor.getCurrentUser()),
mSecondaryDisplayShowing, true /* forceCallbacks */);
+ } else {
+ // The system's keyguard is disabled or missing.
+ setShowingLocked(false, mSecondaryDisplayShowing, true);
}
mStatusBarKeyguardViewManager =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 6b0d592..6ccb817 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -36,7 +36,6 @@
public class QSContainerImpl extends FrameLayout {
private final Point mSizePoint = new Point();
- private final Path mClipPath = new Path();
private int mHeightOverride = -1;
protected View mQSPanel;
@@ -46,7 +45,6 @@
private QSCustomizer mQSCustomizer;
private View mQSFooter;
private View mBackground;
- private float mRadius;
private int mSideMargins;
public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -62,8 +60,6 @@
mQSCustomizer = findViewById(R.id.qs_customize);
mQSFooter = findViewById(R.id.qs_footer);
mBackground = findViewById(R.id.quick_settings_background);
- mRadius = getResources().getDimensionPixelSize(
- Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
setClickable(true);
@@ -115,18 +111,6 @@
updateExpansion();
}
- @Override
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- boolean ret;
- canvas.save();
- if (child != mQSCustomizer) {
- canvas.clipPath(mClipPath);
- }
- ret = super.drawChild(canvas, child, drawingTime);
- canvas.restore();
- return ret;
- }
-
/**
* Overrides the height of this view (post-layout), so that the content is clipped to that
* height and the background is set to that height.
@@ -146,10 +130,6 @@
mQSFooter.setTranslationY(height - mQSFooter.getHeight());
mBackground.setTop(mQSPanel.getTop());
mBackground.setBottom(height);
-
- ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
- mRadius,
- mClipPath);
}
protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 57f7818..316ad16 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -23,12 +23,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.os.Binder;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.view.Gravity;
+import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -40,9 +43,6 @@
import android.widget.TextView;
import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.statusbar.phone.NavigationBarView;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
@@ -233,30 +233,11 @@
.setVisibility(View.INVISIBLE);
}
- StatusBar statusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
- NavigationBarView navigationBarView = statusBar.getNavigationBarView();
- final boolean recentsVisible = navigationBarView != null
- && navigationBarView.isRecentsButtonVisible();
boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
- int descriptionStringResId;
- if (recentsVisible) {
- mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(VISIBLE);
- mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(INVISIBLE);
- mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(INVISIBLE);
- descriptionStringResId = touchExplorationEnabled
- ? R.string.screen_pinning_description_accessible
- : R.string.screen_pinning_description;
- } else {
- mLayout.findViewById(R.id.screen_pinning_recents_group).setVisibility(INVISIBLE);
- mLayout.findViewById(R.id.screen_pinning_home_bg_light).setVisibility(VISIBLE);
- mLayout.findViewById(R.id.screen_pinning_home_bg).setVisibility(VISIBLE);
- descriptionStringResId = touchExplorationEnabled
- ? R.string.screen_pinning_description_recents_invisible_accessible
- : R.string.screen_pinning_description_recents_invisible;
- }
-
((TextView) mLayout.findViewById(R.id.screen_pinning_description))
- .setText(descriptionStringResId);
+ .setText(touchExplorationEnabled
+ ? R.string.screen_pinning_description_accessible
+ : R.string.screen_pinning_description);
final int backBgVisibility = touchExplorationEnabled ? View.INVISIBLE : View.VISIBLE;
mLayout.findViewById(R.id.screen_pinning_back_bg).setVisibility(backBgVisibility);
mLayout.findViewById(R.id.screen_pinning_back_bg_light).setVisibility(backBgVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index f5f62b85..79e9f7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -24,7 +24,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
import android.support.annotation.VisibleForTesting;
import android.util.Pair;
@@ -91,8 +90,6 @@
private static final int MSG_FINGERPRINT_ERROR = 42 << MSG_SHIFT;
private static final int MSG_FINGERPRINT_HIDE = 43 << MSG_SHIFT;
private static final int MSG_SHOW_CHARGING_ANIMATION = 44 << MSG_SHIFT;
- private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
- private static final int MSG_SHOW_PINNING_TOAST_ESCAPE = 46 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -151,8 +148,6 @@
default void clickTile(ComponentName tile) { }
default void handleSystemKey(int arg1) { }
- default void showPinningEnterExitToast(boolean entering) { }
- default void showPinningEscapeToast() { }
default void handleShowGlobalActionsMenu() { }
default void handleShowShutdownUi(boolean isReboot, String reason) { }
@@ -458,21 +453,6 @@
}
@Override
- public void showPinningEnterExitToast(boolean entering) {
- synchronized (mLock) {
- mHandler.obtainMessage(MSG_SHOW_PINNING_TOAST_ENTER_EXIT, entering).sendToTarget();
- }
- }
-
- @Override
- public void showPinningEscapeToast() {
- synchronized (mLock) {
- mHandler.obtainMessage(MSG_SHOW_PINNING_TOAST_ESCAPE).sendToTarget();
- }
- }
-
-
- @Override
public void showGlobalActionsMenu() {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_GLOBAL_ACTIONS);
@@ -787,16 +767,6 @@
mCallbacks.get(i).showChargingAnimation(msg.arg1);
}
break;
- case MSG_SHOW_PINNING_TOAST_ENTER_EXIT:
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showPinningEnterExitToast(msg.arg1 != 0);
- }
- break;
- case MSG_SHOW_PINNING_TOAST_ESCAPE:
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showPinningEscapeToast();
- }
- break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3dfb913..3ebeb4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -379,7 +379,7 @@
// Because space is usually constrained in the auto use-case, there should not be a
// pinned notification when the shade has been expanded. Ensure this by removing all heads-
// up notifications.
- mHeadsUpManager.releaseAllImmediately();
+ mHeadsUpManager.removeAllHeadsUpEntries();
super.animateExpandNotificationsPanel();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
deleted file mode 100644
index c45c538..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.support.v4.util.ArraySet;
-import android.util.Log;
-import android.util.Pools;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.Stack;
-
-/**
- * A implementation of HeadsUpManager for phone and car.
- */
-public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
- ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
- OnHeadsUpChangedListener {
- private static final String TAG = "HeadsUpManagerPhone";
- private static final boolean DEBUG = false;
-
- private final View mStatusBarWindowView;
- private final int mStatusBarHeight;
- private final NotificationGroupManager mGroupManager;
- private final StatusBar mBar;
- private final VisualStabilityManager mVisualStabilityManager;
-
- private boolean mReleaseOnExpandFinish;
- private boolean mTrackingHeadsUp;
- private HashSet<String> mSwipedOutKeys = new HashSet<>();
- private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
- private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
- = new ArraySet<>();
- private boolean mIsExpanded;
- private int[] mTmpTwoArray = new int[2];
- private boolean mHeadsUpGoingAway;
- private boolean mWaitingOnCollapseWhenGoingAway;
- private boolean mIsObserving;
- private int mStatusBarState;
-
- private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
- private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
-
- @Override
- public HeadsUpEntryPhone acquire() {
- if (!mPoolObjects.isEmpty()) {
- return mPoolObjects.pop();
- }
- return new HeadsUpEntryPhone();
- }
-
- @Override
- public boolean release(@NonNull HeadsUpEntryPhone instance) {
- instance.reset();
- mPoolObjects.push(instance);
- return true;
- }
- };
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Constructor:
-
- public HeadsUpManagerPhone(@NonNull final Context context, @NonNull View statusBarWindowView,
- @NonNull NotificationGroupManager groupManager, @NonNull StatusBar bar,
- @NonNull VisualStabilityManager visualStabilityManager) {
- super(context);
-
- mStatusBarWindowView = statusBarWindowView;
- mGroupManager = groupManager;
- mBar = bar;
- mVisualStabilityManager = visualStabilityManager;
-
- Resources resources = mContext.getResources();
- mStatusBarHeight = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
-
- addListener(new OnHeadsUpChangedListener() {
- @Override
- public void onHeadsUpPinnedModeChanged(boolean hasPinnedNotification) {
- if (DEBUG) Log.w(TAG, "onHeadsUpPinnedModeChanged");
- updateTouchableRegionListener();
- }
- });
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Public methods:
-
- /**
- * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
- * that a user might have consciously clicked on it.
- *
- * @param key the key of the touched notification
- * @return whether the touch is invalid and should be discarded
- */
- public boolean shouldSwallowClick(@NonNull String key) {
- HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
- if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
- return true;
- }
- return false;
- }
-
- public void onExpandingFinished() {
- if (mReleaseOnExpandFinish) {
- releaseAllImmediately();
- mReleaseOnExpandFinish = false;
- } else {
- for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
- if (isHeadsUp(entry.key)) {
- // Maybe the heads-up was removed already
- removeHeadsUpEntry(entry);
- }
- }
- }
- mEntriesToRemoveAfterExpand.clear();
- }
-
- /**
- * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
- * from the list even after a Heads Up Notification is gone.
- */
- public void setTrackingHeadsUp(boolean trackingHeadsUp) {
- mTrackingHeadsUp = trackingHeadsUp;
- }
-
- /**
- * Notify that the status bar panel gets expanded or collapsed.
- *
- * @param isExpanded True to notify expanded, false to notify collapsed.
- */
- public void setIsPanelExpanded(boolean isExpanded) {
- if (isExpanded != mIsExpanded) {
- mIsExpanded = isExpanded;
- if (isExpanded) {
- // make sure our state is sane
- mWaitingOnCollapseWhenGoingAway = false;
- mHeadsUpGoingAway = false;
- updateTouchableRegionListener();
- }
- }
- }
-
- /**
- * Set the current state of the statusbar.
- */
- public void setStatusBarState(int statusBarState) {
- mStatusBarState = statusBarState;
- }
-
- /**
- * Set that we are exiting the headsUp pinned mode, but some notifications might still be
- * animating out. This is used to keep the touchable regions in a sane state.
- */
- public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
- if (headsUpGoingAway != mHeadsUpGoingAway) {
- mHeadsUpGoingAway = headsUpGoingAway;
- if (!headsUpGoingAway) {
- waitForStatusBarLayout();
- }
- updateTouchableRegionListener();
- }
- }
-
- /**
- * Notifies that a remote input textbox in notification gets active or inactive.
- * @param entry The entry of the target notification.
- * @param remoteInputActive True to notify active, False to notify inactive.
- */
- public void setRemoteInputActive(
- @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
- HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
- if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
- headsUpEntry.remoteInputActive = remoteInputActive;
- if (remoteInputActive) {
- headsUpEntry.removeAutoRemovalCallbacks();
- } else {
- headsUpEntry.updateEntry(false /* updatePostTime */);
- }
- }
- }
-
- @VisibleForTesting
- public void removeMinimumDisplayTimeForTesting() {
- mMinimumDisplayTime = 1;
- mHeadsUpNotificationDecay = 1;
- mTouchAcceptanceDelay = 1;
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // HeadsUpManager public methods overrides:
-
- @Override
- public boolean isTrackingHeadsUp() {
- return mTrackingHeadsUp;
- }
-
- @Override
- public void snooze() {
- super.snooze();
- mReleaseOnExpandFinish = true;
- }
-
- /**
- * React to the removal of the notification in the heads up.
- *
- * @return true if the notification was removed and false if it still needs to be kept around
- * for a bit since it wasn't shown long enough
- */
- @Override
- public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
- if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
- return super.removeNotification(key, ignoreEarliestRemovalTime);
- } else {
- HeadsUpEntryPhone entry = getHeadsUpEntryPhone(key);
- entry.removeAsSoonAsPossible();
- return false;
- }
- }
-
- public void addSwipedOutNotification(@NonNull String key) {
- mSwipedOutKeys.add(key);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Dumpable overrides:
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("HeadsUpManagerPhone state:");
- dumpInternal(fd, pw, args);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // ViewTreeObserver.OnComputeInternalInsetsListener overrides:
-
- /**
- * Overridden from TreeObserver.
- */
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (mIsExpanded || mBar.isBouncerShowing()) {
- // The touchable region is always the full area when expanded
- return;
- }
- if (hasPinnedHeadsUp()) {
- ExpandableNotificationRow topEntry = getTopEntry().row;
- if (topEntry.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
- if (groupSummary != null) {
- topEntry = groupSummary;
- }
- }
- topEntry.getLocationOnScreen(mTmpTwoArray);
- int minX = mTmpTwoArray[0];
- int maxX = mTmpTwoArray[0] + topEntry.getWidth();
- int maxY = topEntry.getIntrinsicHeight();
-
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(minX, 0, maxX, maxY);
- } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
- }
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // VisualStabilityManager.Callback overrides:
-
- @Override
- public void onReorderingAllowed() {
- mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
- for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (isHeadsUp(entry.key)) {
- // Maybe the heads-up was removed already
- removeHeadsUpEntry(entry);
- }
- }
- mEntriesToRemoveWhenReorderingAllowed.clear();
- mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // HeadsUpManager utility (protected) methods overrides:
-
- @Override
- protected HeadsUpEntry createHeadsUpEntry() {
- return mEntryPool.acquire();
- }
-
- @Override
- protected void releaseHeadsUpEntry(HeadsUpEntry entry) {
- mEntryPool.release((HeadsUpEntryPhone) entry);
- }
-
- @Override
- protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
- return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
- || super.shouldHeadsUpBecomePinned(entry);
- }
-
- @Override
- protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dumpInternal(fd, pw, args);
- pw.print(" mStatusBarState="); pw.println(mStatusBarState);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Protected utility methods:
-
- @Nullable
- protected HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
- return (HeadsUpEntryPhone) getHeadsUpEntry(key);
- }
-
- @Nullable
- protected HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
- return (HeadsUpEntryPhone) getTopHeadsUpEntry();
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Private utility methods:
-
- private boolean wasShownLongEnough(@NonNull String key) {
- if (mSwipedOutKeys.contains(key)) {
- // We always instantly dismiss views being manually swiped out.
- mSwipedOutKeys.remove(key);
- return true;
- }
-
- HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
- HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
- if (headsUpEntry != topEntry) {
- return true;
- }
- return headsUpEntry.wasShownLongEnough();
- }
-
- /**
- * We need to wait on the whole panel to collapse, before we can remove the touchable region
- * listener.
- */
- private void waitForStatusBarLayout() {
- mWaitingOnCollapseWhenGoingAway = true;
- mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
- mStatusBarWindowView.removeOnLayoutChangeListener(this);
- mWaitingOnCollapseWhenGoingAway = false;
- updateTouchableRegionListener();
- }
- }
- });
- }
-
- private void updateTouchableRegionListener() {
- boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway
- || mWaitingOnCollapseWhenGoingAway;
- if (shouldObserve == mIsObserving) {
- return;
- }
- if (shouldObserve) {
- mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- mStatusBarWindowView.requestLayout();
- } else {
- mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- }
- mIsObserving = shouldObserve;
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // HeadsUpEntryPhone:
-
- protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
- public void setEntry(@NonNull final NotificationData.Entry entry) {
- Runnable removeHeadsUpRunnable = () -> {
- if (!mVisualStabilityManager.isReorderingAllowed()) {
- mEntriesToRemoveWhenReorderingAllowed.add(entry);
- mVisualStabilityManager.addReorderingAllowedCallback(
- HeadsUpManagerPhone.this);
- } else if (!mTrackingHeadsUp) {
- removeHeadsUpEntry(entry);
- } else {
- mEntriesToRemoveAfterExpand.add(entry);
- }
- };
-
- super.setEntry(entry, removeHeadsUpRunnable);
- }
-
- public boolean wasShownLongEnough() {
- return earliestRemovaltime < mClock.currentTimeMillis();
- }
-
- @Override
- public void updateEntry(boolean updatePostTime) {
- super.updateEntry(updatePostTime);
-
- if (mEntriesToRemoveAfterExpand.contains(entry)) {
- mEntriesToRemoveAfterExpand.remove(entry);
- }
- if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
- mEntriesToRemoveWhenReorderingAllowed.remove(entry);
- }
- }
-
- @Override
- public void expanded(boolean expanded) {
- if (this.expanded == expanded) {
- return;
- }
-
- this.expanded = expanded;
- if (expanded) {
- removeAutoRemovalCallbacks();
- } else {
- updateEntry(false /* updatePostTime */);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 2bfdefe..c85571c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -23,7 +23,7 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
/**
@@ -31,7 +31,7 @@
*/
public class HeadsUpTouchHelper implements Gefingerpoken {
- private HeadsUpManagerPhone mHeadsUpManager;
+ private HeadsUpManager mHeadsUpManager;
private NotificationStackScrollLayout mStackScroller;
private int mTrackingPointer;
private float mTouchSlop;
@@ -43,7 +43,7 @@
private NotificationPanelView mPanel;
private ExpandableNotificationRow mPickedChild;
- public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
+ public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
NotificationStackScrollLayout stackScroller,
NotificationPanelView notificationPanelView) {
mHeadsUpManager = headsUpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 242be71..65c45a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -22,13 +22,11 @@
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
-import static com.android.systemui.OverviewProxyService.OverviewProxyListener;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.annotation.IdRes;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -71,7 +69,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
-import android.widget.Button;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -156,18 +153,6 @@
private Animator mRotateShowAnimator;
private Animator mRotateHideAnimator;
- private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
- @Override
- public void onConnectionChanged(boolean isConnected) {
- mNavigationBarView.onOverviewProxyConnectionChanged(isConnected);
- updateScreenPinningGestures();
- }
-
- @Override
- public void onRecentsAnimationStarted() {
- mNavigationBarView.setRecentsAnimationStarted(true);
- }
- };
// ----- Fragment Lifecycle Callbacks -----
@@ -254,14 +239,12 @@
filter.addAction(Intent.ACTION_SCREEN_ON);
getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
notifyNavigationBarScreenOn();
- mOverviewProxyService.addCallback(mOverviewProxyListener);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mNavigationBarView.getLightTransitionsController().destroy(getContext());
- mOverviewProxyService.removeCallback(mOverviewProxyListener);
getContext().unregisterReceiver(mBroadcastReceiver);
}
@@ -531,7 +514,6 @@
if (masked != mDisabledFlags1) {
mDisabledFlags1 = masked;
if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1);
- updateScreenPinningGestures();
}
}
@@ -546,7 +528,7 @@
private boolean shouldDisableNavbarGestures() {
return !mStatusBar.isDeviceProvisioned()
|| (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0
- || mNavigationBarView.getRecentsButton().getVisibility() != View.VISIBLE;
+ || mOverviewProxyService.getProxy() != null;
}
private void repositionNavigationBar() {
@@ -558,24 +540,6 @@
((View) mNavigationBarView.getParent()).getLayoutParams());
}
- private void updateScreenPinningGestures() {
- if (mNavigationBarView == null) {
- return;
- }
-
- // Change the cancel pin gesture to home and back if recents button is invisible
- boolean recentsVisible = mNavigationBarView.isRecentsButtonVisible();
- ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
- ButtonDispatcher backButton = mNavigationBarView.getBackButton();
- if (recentsVisible) {
- homeButton.setOnLongClickListener(this::onHomeLongClick);
- backButton.setOnLongClickListener(this::onLongPressBackRecents);
- } else {
- homeButton.setOnLongClickListener(this::onLongPressBackHome);
- backButton.setOnLongClickListener(this::onLongPressBackHome);
- }
- }
-
private void notifyNavigationBarScreenOn() {
mNavigationBarView.notifyScreenOn();
}
@@ -685,29 +649,20 @@
mCommandQueue.toggleRecentApps();
}
- private boolean onLongPressBackHome(View v) {
- return onLongPressNavigationButtons(v, R.id.back, R.id.home);
- }
-
- private boolean onLongPressBackRecents(View v) {
- return onLongPressNavigationButtons(v, R.id.back, R.id.recent_apps);
- }
-
/**
- * This handles long-press of both back and recents/home. Back is the common button with
- * combination of recents if it is visible or home if recents is invisible.
- * They are handled together to capture them both being long-pressed
+ * This handles long-press of both back and recents. They are
+ * handled together to capture them both being long-pressed
* at the same time to exit screen pinning (lock task).
*
- * When accessibility mode is on, only a long-press from recents/home
+ * When accessibility mode is on, only a long-press from recents
* is required to exit.
*
* In all other circumstances we try to pass through long-press events
* for Back, so that apps can still use it. Which can be from two things.
* 1) Not currently in screen pinning (lock task).
- * 2) Back is long-pressed without recents/home.
+ * 2) Back is long-pressed without recents.
*/
- private boolean onLongPressNavigationButtons(View v, @IdRes int btnId1, @IdRes int btnId2) {
+ private boolean onLongPressBackRecents(View v) {
try {
boolean sendBackLongPress = false;
IActivityManager activityManager = ActivityManagerNative.getDefault();
@@ -715,7 +670,6 @@
boolean inLockTaskMode = activityManager.isInLockTaskMode();
if (inLockTaskMode && !touchExplorationEnabled) {
long time = System.currentTimeMillis();
-
// If we recently long-pressed the other button then they were
// long-pressed 'together'
if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
@@ -723,32 +677,26 @@
// When exiting refresh disabled flags.
mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
return true;
- } else if (v.getId() == btnId1) {
- ButtonDispatcher button = btnId2 == R.id.recent_apps
- ? mNavigationBarView.getRecentsButton()
- : mNavigationBarView.getHomeButton();
- if (!button.getCurrentView().isPressed()) {
- // If we aren't pressing recents/home right now then they presses
- // won't be together, so send the standard long-press action.
- sendBackLongPress = true;
- }
+ } else if ((v.getId() == R.id.back)
+ && !mNavigationBarView.getRecentsButton().getCurrentView().isPressed()) {
+ // If we aren't pressing recents right now then they presses
+ // won't be together, so send the standard long-press action.
+ sendBackLongPress = true;
}
mLastLockToAppLongPress = time;
} else {
// If this is back still need to handle sending the long-press event.
- if (v.getId() == btnId1) {
+ if (v.getId() == R.id.back) {
sendBackLongPress = true;
} else if (touchExplorationEnabled && inLockTaskMode) {
- // When in accessibility mode a long press that is recents/home (not back)
+ // When in accessibility mode a long press that is recents (not back)
// should stop lock task.
activityManager.stopSystemLockTaskMode();
// When exiting refresh disabled flags.
mNavigationBarView.setDisabledFlags(mDisabledFlags1, true);
return true;
- } else if (v.getId() == btnId2) {
- return btnId2 == R.id.recent_apps
- ? onLongPressRecents()
- : onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView());
+ } else if (v.getId() == R.id.recent_apps) {
+ return onLongPressRecents();
}
}
if (sendBackLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index cd220a7..e04ba83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -207,6 +207,23 @@
}
}
+ private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ updateSlippery();
+ setDisabledFlags(mDisabledFlags, true);
+ setUpSwipeUpOnboarding(isConnected);
+ }
+
+ @Override
+ public void onRecentsAnimationStarted() {
+ mRecentsAnimationStarted = true;
+ if (mSwipeUpOnboarding != null) {
+ mSwipeUpOnboarding.onRecentsAnimationStarted();
+ }
+ }
+ };
+
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -263,19 +280,6 @@
notifyVerticalChangedListener(mVertical);
}
- public void setRecentsAnimationStarted(boolean started) {
- mRecentsAnimationStarted = started;
- if (mSwipeUpOnboarding != null) {
- mSwipeUpOnboarding.onRecentsAnimationStarted();
- }
- }
-
- public void onConnectionChanged(boolean isConnected) {
- updateSlippery();
- setDisabledFlags(mDisabledFlags, true);
- setUpSwipeUpOnboarding(isConnected);
- }
-
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mGestureHelper.onTouchEvent(event)) {
@@ -300,7 +304,7 @@
}
}
}
- return mRecentsAnimationStarted || mGestureHelper.onInterceptTouchEvent(event);
+ return mGestureHelper.onInterceptTouchEvent(event) || mRecentsAnimationStarted;
}
public void abortCurrentGesture() {
@@ -349,10 +353,6 @@
return mButtonDispatchers;
}
- public boolean isRecentsButtonVisible() {
- return getRecentsButton().getVisibility() == View.VISIBLE;
- }
-
private void updateCarModeIcons(Context ctx) {
mBackCarModeIcon = getDrawable(ctx,
R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode);
@@ -613,9 +613,6 @@
final ViewGroup navbarView = ((ViewGroup) getParent());
final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView
.getLayoutParams();
- if (lp == null) {
- return;
- }
if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) {
lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
changed = true;
@@ -679,12 +676,6 @@
}
}
- public void onOverviewProxyConnectionChanged(boolean isConnected) {
- setSlippery(!isConnected);
- setDisabledFlags(mDisabledFlags, true);
- setUpSwipeUpOnboarding(isConnected);
- }
-
@Override
protected void onDraw(Canvas canvas) {
mGestureHelper.onDraw(canvas);
@@ -882,6 +873,7 @@
onPluginDisconnected(null); // Create default gesture helper
Dependency.get(PluginManager.class).addPluginListener(this,
NavGesture.class, false /* Only one */);
+ mOverviewProxyService.addCallback(mOverviewProxyListener);
setUpSwipeUpOnboarding(mOverviewProxyService.getProxy() != null);
}
@@ -892,6 +884,7 @@
if (mGestureHelper != null) {
mGestureHelper.destroy();
}
+ mOverviewProxyService.removeCallback(mOverviewProxyListener);
setUpSwipeUpOnboarding(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 52d005c..cd2e77a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -68,12 +68,14 @@
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.util.List;
+import java.util.Collection;
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener,
@@ -1569,7 +1571,7 @@
private void updatePanelExpanded() {
boolean isExpanded = !isFullyCollapsed();
if (mPanelExpanded != isExpanded) {
- mHeadsUpManager.setIsPanelExpanded(isExpanded);
+ mHeadsUpManager.setIsExpanded(isExpanded);
mStatusBar.setPanelExpanded(isExpanded);
mPanelExpanded = isExpanded;
}
@@ -2336,7 +2338,7 @@
}
@Override
- public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
super.setHeadsUpManager(headsUpManager);
mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
this);
@@ -2628,8 +2630,8 @@
}
}
- public void setPulsing(boolean pulsing) {
- mKeyguardStatusView.setPulsing(pulsing);
+ public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+ mKeyguardStatusView.setPulsing(pulsing != null);
positionClockAndNotifications();
mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
+ mKeyguardStatusView.getClockBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 6daabed..2b7e474 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -50,7 +50,7 @@
import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -75,7 +75,7 @@
}
protected StatusBar mStatusBar;
- protected HeadsUpManagerPhone mHeadsUpManager;
+ protected HeadsUpManager mHeadsUpManager;
private float mPeekHeight;
private float mHintDistance;
@@ -1252,7 +1252,7 @@
*/
protected abstract int getClearAllHeight();
- public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
mHeadsUpManager = headsUpManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3777a6c..426268b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -208,7 +208,6 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -220,7 +219,6 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -406,13 +404,6 @@
protected NotificationEntryManager mEntryManager;
protected NotificationViewHierarchyManager mViewHierarchyManager;
- /**
- * Helper that is responsible for showing the right toast when a disallowed activity operation
- * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
- * fully locked mode we only show that unlocking is blocked.
- */
- private ScreenPinningNotify mScreenPinningNotify;
-
// for disabling the status bar
private int mDisabled1 = 0;
private int mDisabled2 = 0;
@@ -811,14 +802,15 @@
.commit();
mIconController = Dependency.get(StatusBarIconController.class);
- mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this,
- mVisualStabilityManager);
+ mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager);
+ mHeadsUpManager.setBar(this);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
+ mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
mEntryManager.setUpWithPresenter(this, mStackScroller, this, mHeadsUpManager);
@@ -838,7 +830,7 @@
} catch (RemoteException ex) {
// no window manager? good luck with that
}
- mScreenPinningNotify = new ScreenPinningNotify(mContext);
+
mStackScroller.setLongPressListener(mEntryManager.getNotificationLongClicker());
mStackScroller.setStatusBar(this);
mStackScroller.setGroupManager(mGroupManager);
@@ -1349,8 +1341,7 @@
@Override
public void onPerformRemoveNotification(StatusBarNotification n) {
- if (mStackScroller.hasPulsingNotifications() &&
- !mHeadsUpManager.hasHeadsUpNotifications()) {
+ if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -2099,8 +2090,9 @@
}
public void maybeEscalateHeadsUp() {
- mHeadsUpManager.getAllEntries().forEach(entry -> {
- final StatusBarNotification sbn = entry.notification;
+ Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
+ for (HeadsUpManager.HeadsUpEntry entry : entries) {
+ final StatusBarNotification sbn = entry.entry.notification;
final Notification notification = sbn.getNotification();
if (notification.fullScreenIntent != null) {
if (DEBUG) {
@@ -2110,11 +2102,11 @@
EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
sbn.getKey());
notification.fullScreenIntent.send();
- entry.notifyFullScreenIntentLaunched();
+ entry.entry.notifyFullScreenIntentLaunched();
} catch (PendingIntent.CanceledException e) {
}
}
- });
+ }
mHeadsUpManager.releaseAllImmediately();
}
@@ -2149,21 +2141,6 @@
}
- @Override
- public void showPinningEnterExitToast(boolean entering) {
- if (entering) {
- mScreenPinningNotify.showPinningStartToast();
- } else {
- mScreenPinningNotify.showPinningExitToast();
- }
- }
-
- @Override
- public void showPinningEscapeToast() {
- mScreenPinningNotify.showEscapeToast(getNavigationBarView() == null
- || getNavigationBarView().isRecentsButtonVisible());
- }
-
boolean panelsEnabled() {
return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0
&& (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
@@ -4659,22 +4636,24 @@
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- if (mHeadsUpManager.hasHeadsUpNotifications()) {
+ Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries =
+ mHeadsUpManager.getAllEntries();
+ if (!pulsingEntries.isEmpty()) {
// Only pulse the stack scroller if there's actually something to show.
// Otherwise just show the always-on screen.
- setPulsing(true);
+ setPulsing(pulsingEntries);
}
}
@Override
public void onPulseFinished() {
callback.onPulseFinished();
- setPulsing(false);
+ setPulsing(null);
}
- private void setPulsing(boolean pulsing) {
+ private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
mNotificationPanel.setPulsing(pulsing);
- mVisualStabilityManager.setPulsing(pulsing);
+ mVisualStabilityManager.setPulsing(pulsing != null);
mIgnoreTouchWhilePulsing = false;
}
}, reason);
@@ -4822,7 +4801,7 @@
// for heads up notifications
- protected HeadsUpManagerPhone mHeadsUpManager;
+ protected HeadsUpManager mHeadsUpManager;
private AboveShelfObserver mAboveShelfObserver;
@@ -4925,7 +4904,7 @@
// Release the HUN notification to the shade.
if (isPresenterFullyCollapsed()) {
- HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
+ HeadsUpManager.setIsClickedNotification(row, true);
}
//
// In most cases, when FLAG_AUTO_CANCEL is set, the notification will
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index a2b896d..53dfb24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,68 +16,118 @@
package com.android.systemui.statusbar.policy;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.os.SystemClock;
import android.os.Handler;
import android.os.Looper;
-import android.util.ArrayMap;
+import android.os.SystemClock;
import android.provider.Settings;
+import android.support.v4.util.ArraySet;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pools;
+import android.view.View;
+import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Iterator;
-import java.util.stream.Stream;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Stack;
/**
* A manager which handles heads up notifications which is a special mode where
* they simply peek from the top of the screen.
*/
-public class HeadsUpManager {
+public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener,
+ VisualStabilityManager.Callback {
private static final String TAG = "HeadsUpManager";
private static final boolean DEBUG = false;
private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
+ private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
- protected final Clock mClock = new Clock();
- protected final Context mContext;
- protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
- protected final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final int mHeadsUpNotificationDecay;
+ private final int mMinimumDisplayTime;
- protected int mHeadsUpNotificationDecay;
- protected int mMinimumDisplayTime;
- protected int mTouchAcceptanceDelay;
- protected int mSnoozeLengthMs;
- protected boolean mHasPinnedNotification;
- protected int mUser;
-
- private final HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
+ private final int mTouchAcceptanceDelay;
private final ArrayMap<String, Long> mSnoozedPackages;
- private final ContentObserver mSettingsObserver;
+ private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
+ private final int mDefaultSnoozeLengthMs;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
- public HeadsUpManager(@NonNull final Context context) {
+ private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
+
+ @Override
+ public HeadsUpEntry acquire() {
+ if (!mPoolObjects.isEmpty()) {
+ return mPoolObjects.pop();
+ }
+ return new HeadsUpEntry();
+ }
+
+ @Override
+ public boolean release(HeadsUpEntry instance) {
+ instance.reset();
+ mPoolObjects.push(instance);
+ return true;
+ }
+ };
+
+ private final View mStatusBarWindowView;
+ private final int mStatusBarHeight;
+ private final Context mContext;
+ private final NotificationGroupManager mGroupManager;
+ private StatusBar mBar;
+ private int mSnoozeLengthMs;
+ private ContentObserver mSettingsObserver;
+ private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
+ private HashSet<String> mSwipedOutKeys = new HashSet<>();
+ private int mUser;
+ private Clock mClock;
+ private boolean mReleaseOnExpandFinish;
+ private boolean mTrackingHeadsUp;
+ private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
+ private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+ = new ArraySet<>();
+ private boolean mIsExpanded;
+ private boolean mHasPinnedNotification;
+ private int[] mTmpTwoArray = new int[2];
+ private boolean mHeadsUpGoingAway;
+ private boolean mWaitingOnCollapseWhenGoingAway;
+ private boolean mIsObserving;
+ private boolean mRemoteInputActive;
+ private float mExpandedHeight;
+ private VisualStabilityManager mVisualStabilityManager;
+ private int mStatusBarState;
+
+ public HeadsUpManager(final Context context, View statusBarWindowView,
+ NotificationGroupManager groupManager) {
mContext = context;
- Resources resources = context.getResources();
- mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
- mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
+ Resources resources = mContext.getResources();
mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
mSnoozedPackages = new ArrayMap<>();
- int defaultSnoozeLengthMs =
- resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
+ mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
+ mSnoozeLengthMs = mDefaultSnoozeLengthMs;
+ mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
+ mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
+ mClock = new Clock();
mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
- SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
+ SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -92,26 +142,47 @@
context.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
mSettingsObserver);
+ mStatusBarWindowView = statusBarWindowView;
+ mGroupManager = groupManager;
+ mStatusBarHeight = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
}
- /**
- * Adds an OnHeadUpChangedListener to observe events.
- */
- public void addListener(@NonNull OnHeadsUpChangedListener listener) {
+ private void updateTouchableRegionListener() {
+ boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway
+ || mWaitingOnCollapseWhenGoingAway;
+ if (shouldObserve == mIsObserving) {
+ return;
+ }
+ if (shouldObserve) {
+ mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ mStatusBarWindowView.requestLayout();
+ } else {
+ mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ }
+ mIsObserving = shouldObserve;
+ }
+
+ public void setBar(StatusBar bar) {
+ mBar = bar;
+ }
+
+ public void addListener(OnHeadsUpChangedListener listener) {
mListeners.add(listener);
}
- /**
- * Removes the OnHeadUpChangedListener from the observer list.
- */
- public void removeListener(@NonNull OnHeadsUpChangedListener listener) {
+ public void removeListener(OnHeadsUpChangedListener listener) {
mListeners.remove(listener);
}
+ public StatusBar getBar() {
+ return mBar;
+ }
+
/**
* Called when posting a new notification to the heads up.
*/
- public void showNotification(@NonNull NotificationData.Entry headsUp) {
+ public void showNotification(NotificationData.Entry headsUp) {
if (DEBUG) Log.v(TAG, "showNotification");
addHeadsUpEntry(headsUp);
updateNotification(headsUp, true);
@@ -121,7 +192,7 @@
/**
* Called when updating or posting a notification to the heads up.
*/
- public void updateNotification(@NonNull NotificationData.Entry headsUp, boolean alert) {
+ public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
if (DEBUG) Log.v(TAG, "updateNotification");
headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -133,13 +204,14 @@
// with the groupmanager
return;
}
- headsUpEntry.updateEntry(true /* updatePostTime */);
+ headsUpEntry.updateEntry();
setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
}
}
- private void addHeadsUpEntry(@NonNull NotificationData.Entry entry) {
- HeadsUpEntry headsUpEntry = createHeadsUpEntry();
+ private void addHeadsUpEntry(NotificationData.Entry entry) {
+ HeadsUpEntry headsUpEntry = mEntryPool.acquire();
+
// This will also add the entry to the sortedList
headsUpEntry.setEntry(entry);
mHeadsUpEntries.put(entry.key, headsUpEntry);
@@ -151,17 +223,16 @@
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
- protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
- return hasFullScreenIntent(entry);
+ private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+ return mStatusBarState != StatusBarState.KEYGUARD
+ && !mIsExpanded || hasFullScreenIntent(entry);
}
- protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
+ private boolean hasFullScreenIntent(NotificationData.Entry entry) {
return entry.notification.getNotification().fullScreenIntent != null;
}
- protected void setEntryPinned(
- @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
- if (DEBUG) Log.v(TAG, "setEntryPinned: " + isPinned);
+ private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) {
ExpandableNotificationRow row = headsUpEntry.entry.row;
if (row.isPinned() != isPinned) {
row.setPinned(isPinned);
@@ -176,35 +247,33 @@
}
}
- protected void removeHeadsUpEntry(NotificationData.Entry entry) {
+ private void removeHeadsUpEntry(NotificationData.Entry entry) {
HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
- onHeadsUpEntryRemoved(remove);
- releaseHeadsUpEntry(remove);
- }
-
- protected void onHeadsUpEntryRemoved(HeadsUpEntry remove) {
- NotificationData.Entry entry = remove.entry;
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
entry.row.setHeadsUp(false);
setEntryPinned(remove, false /* isPinned */);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
+ mEntryPool.release(remove);
}
- protected void updatePinnedMode() {
+ public void removeAllHeadsUpEntries() {
+ for (String key : mHeadsUpEntries.keySet()) {
+ removeHeadsUpEntry(mHeadsUpEntries.get(key).entry);
+ }
+ }
+
+ private void updatePinnedMode() {
boolean hasPinnedNotification = hasPinnedNotificationInternal();
if (hasPinnedNotification == mHasPinnedNotification) {
return;
}
- if (DEBUG) {
- Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " +
- hasPinnedNotification);
- }
mHasPinnedNotification = hasPinnedNotification;
if (mHasPinnedNotification) {
MetricsLogger.count(mContext, "note_peek", 1);
}
+ updateTouchableRegionListener();
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
}
@@ -216,36 +285,47 @@
* @return true if the notification was removed and false if it still needs to be kept around
* for a bit since it wasn't shown long enough
*/
- public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
- if (DEBUG) Log.v(TAG, "removeNotification");
- releaseImmediately(key);
- return true;
+ public boolean removeNotification(String key, boolean ignoreEarliestRemovalTime) {
+ if (DEBUG) Log.v(TAG, "remove");
+ if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
+ releaseImmediately(key);
+ return true;
+ } else {
+ getHeadsUpEntry(key).removeAsSoonAsPossible();
+ return false;
+ }
}
- /**
- * Returns if the given notification is in the Heads Up Notification list or not.
- */
+ private boolean wasShownLongEnough(String key) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ HeadsUpEntry topEntry = getTopEntry();
+ if (mSwipedOutKeys.contains(key)) {
+ // We always instantly dismiss views being manually swiped out.
+ mSwipedOutKeys.remove(key);
+ return true;
+ }
+ if (headsUpEntry != topEntry) {
+ return true;
+ }
+ return headsUpEntry.wasShownLongEnough();
+ }
+
public boolean isHeadsUp(String key) {
return mHeadsUpEntries.containsKey(key);
}
/**
- * Pushes any current Heads Up notification down into the shade.
+ * Push any current Heads Up notification down into the shade.
*/
public void releaseAllImmediately() {
if (DEBUG) Log.v(TAG, "releaseAllImmediately");
- Iterator<HeadsUpEntry> iterator = mHeadsUpEntries.values().iterator();
- while (iterator.hasNext()) {
- HeadsUpEntry entry = iterator.next();
- iterator.remove();
- onHeadsUpEntryRemoved(entry);
+ ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet());
+ for (String key : keys) {
+ releaseImmediately(key);
}
}
- /**
- * Pushes the given Heads Up notification down into the shade.
- */
- public void releaseImmediately(@NonNull String key) {
+ public void releaseImmediately(String key) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
if (headsUpEntry == null) {
return;
@@ -254,14 +334,11 @@
removeHeadsUpEntry(shadeEntry);
}
- /**
- * Returns if the given notification is snoozed or not.
- */
- public boolean isSnoozed(@NonNull String packageName) {
+ public boolean isSnoozed(String packageName) {
final String key = snoozeKey(packageName, mUser);
Long snoozedUntil = mSnoozedPackages.get(key);
if (snoozedUntil != null) {
- if (snoozedUntil > mClock.currentTimeMillis()) {
+ if (snoozedUntil > SystemClock.elapsedRealtime()) {
if (DEBUG) Log.v(TAG, key + " snoozed");
return true;
}
@@ -270,61 +347,33 @@
return false;
}
- /**
- * Snoozes all current Heads Up Notifications.
- */
public void snooze() {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
String packageName = entry.entry.notification.getPackageName();
mSnoozedPackages.put(snoozeKey(packageName, mUser),
- mClock.currentTimeMillis() + mSnoozeLengthMs);
+ SystemClock.elapsedRealtime() + mSnoozeLengthMs);
}
+ mReleaseOnExpandFinish = true;
}
- private static String snoozeKey(@NonNull String packageName, int user) {
+ private static String snoozeKey(String packageName, int user) {
return user + "," + packageName;
}
- protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
+ private HeadsUpEntry getHeadsUpEntry(String key) {
return mHeadsUpEntries.get(key);
}
- /**
- * Returns the entry of given Heads Up Notification.
- *
- * @param key Key of heads up notification
- */
- public NotificationData.Entry getEntry(@NonNull String key) {
- HeadsUpEntry entry = mHeadsUpEntries.get(key);
- return entry != null ? entry.entry : null;
+ public NotificationData.Entry getEntry(String key) {
+ return mHeadsUpEntries.get(key).entry;
}
- /**
- * Returns the stream of all current Heads Up Notifications.
- */
- @NonNull
- public Stream<NotificationData.Entry> getAllEntries() {
- return mHeadsUpEntries.values().stream().map(headsUpEntry -> headsUpEntry.entry);
+ public Collection<HeadsUpEntry> getAllEntries() {
+ return mHeadsUpEntries.values();
}
- /**
- * Returns the top Heads Up Notification, which appeares to show at first.
- */
- @Nullable
- public NotificationData.Entry getTopEntry() {
- HeadsUpEntry topEntry = getTopHeadsUpEntry();
- return (topEntry != null) ? topEntry.entry : null;
- }
-
- /**
- * Returns if any heads up notification is available or not.
- */
- public boolean hasHeadsUpNotifications() {
- return !mHeadsUpEntries.isEmpty();
- }
-
- protected HeadsUpEntry getTopHeadsUpEntry() {
+ public HeadsUpEntry getTopEntry() {
if (mHeadsUpEntries.isEmpty()) {
return null;
}
@@ -338,21 +387,56 @@
}
/**
- * Sets the current user.
+ * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
+ * that a user might have consciously clicked on it.
+ *
+ * @param key the key of the touched notification
+ * @return whether the touch is invalid and should be discarded
*/
+ public boolean shouldSwallowClick(String key) {
+ HeadsUpEntry entry = mHeadsUpEntries.get(key);
+ if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
+ return true;
+ }
+ return false;
+ }
+
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ if (mIsExpanded || mBar.isBouncerShowing()) {
+ // The touchable region is always the full area when expanded
+ return;
+ }
+ if (mHasPinnedNotification) {
+ ExpandableNotificationRow topEntry = getTopEntry().entry.row;
+ if (topEntry.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+ if (groupSummary != null) {
+ topEntry = groupSummary;
+ }
+ }
+ topEntry.getLocationOnScreen(mTmpTwoArray);
+ int minX = mTmpTwoArray[0];
+ int maxX = mTmpTwoArray[0] + topEntry.getWidth();
+ int maxY = topEntry.getIntrinsicHeight();
+
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(minX, 0, maxX, maxY);
+ } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
+ }
+ }
+
public void setUser(int user) {
mUser = user;
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("HeadsUpManager state:");
- dumpInternal(fd, pw, args);
- }
-
- protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
- pw.print(" now="); pw.println(mClock.currentTimeMillis());
+ pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
pw.print(" mUser="); pw.println(mUser);
for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
pw.print(" HeadsUpEntry="); pw.println(entry.entry);
@@ -365,9 +449,6 @@
}
}
- /**
- * Returns if there are any pinned Heads Up Notifications or not.
- */
public boolean hasPinnedHeadsUp() {
return mHasPinnedNotification;
}
@@ -383,8 +464,14 @@
}
/**
- * Unpins all pinned Heads Up Notifications.
+ * Notifies that a notification was swiped out and will be removed.
+ *
+ * @param key the notification key
*/
+ public void addSwipedOutNotification(String key) {
+ mSwipedOutKeys.add(key);
+ }
+
public void unpinAll() {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
@@ -394,13 +481,60 @@
}
}
- /**
- * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
- * well.
- */
+ public void onExpandingFinished() {
+ if (mReleaseOnExpandFinish) {
+ releaseAllImmediately();
+ mReleaseOnExpandFinish = false;
+ } else {
+ for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+ if (isHeadsUp(entry.key)) {
+ // Maybe the heads-up was removed already
+ removeHeadsUpEntry(entry);
+ }
+ }
+ }
+ mEntriesToRemoveAfterExpand.clear();
+ }
+
+ public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+ mTrackingHeadsUp = trackingHeadsUp;
+ }
+
public boolean isTrackingHeadsUp() {
- // Might be implemented in subclass.
- return false;
+ return mTrackingHeadsUp;
+ }
+
+ public void setIsExpanded(boolean isExpanded) {
+ if (isExpanded != mIsExpanded) {
+ mIsExpanded = isExpanded;
+ if (isExpanded) {
+ // make sure our state is sane
+ mWaitingOnCollapseWhenGoingAway = false;
+ mHeadsUpGoingAway = false;
+ updateTouchableRegionListener();
+ }
+ }
+ }
+
+ /**
+ * @return the height of the top heads up notification when pinned. This is different from the
+ * intrinsic height, which also includes whether the notification is system expanded and
+ * is mainly used when dragging down from a heads up notification.
+ */
+ public int getTopHeadsUpPinnedHeight() {
+ HeadsUpEntry topEntry = getTopEntry();
+ if (topEntry == null || topEntry.entry == null) {
+ return 0;
+ }
+ ExpandableNotificationRow row = topEntry.entry.row;
+ if (row.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(row.getStatusBarNotification());
+ if (groupSummary != null) {
+ row = groupSummary;
+ }
+ }
+ return row.getPinnedHeadsUpHeight();
}
/**
@@ -419,67 +553,147 @@
}
/**
- * Sets an entry to be expanded and therefore stick in the heads up area if it's pinned
- * until it's collapsed again.
+ * Set that we are exiting the headsUp pinned mode, but some notifications might still be
+ * animating out. This is used to keep the touchable regions in a sane state.
*/
+ public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+ if (headsUpGoingAway != mHeadsUpGoingAway) {
+ mHeadsUpGoingAway = headsUpGoingAway;
+ if (!headsUpGoingAway) {
+ waitForStatusBarLayout();
+ }
+ updateTouchableRegionListener();
+ }
+ }
+
+ /**
+ * We need to wait on the whole panel to collapse, before we can remove the touchable region
+ * listener.
+ */
+ private void waitForStatusBarLayout() {
+ mWaitingOnCollapseWhenGoingAway = true;
+ mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
+ mStatusBarWindowView.removeOnLayoutChangeListener(this);
+ mWaitingOnCollapseWhenGoingAway = false;
+ updateTouchableRegionListener();
+ }
+ }
+ });
+ }
+
+ public static void setIsClickedNotification(View child, boolean clicked) {
+ child.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
+ }
+
+ public static boolean isClickedHeadsUpNotification(View child) {
+ Boolean clicked = (Boolean) child.getTag(TAG_CLICKED_NOTIFICATION);
+ return clicked != null && clicked;
+ }
+
+ public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
+ HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+ if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
+ headsUpEntry.remoteInputActive = remoteInputActive;
+ if (remoteInputActive) {
+ headsUpEntry.removeAutoRemovalCallbacks();
+ } else {
+ headsUpEntry.updateEntry(false /* updatePostTime */);
+ }
+ }
+ }
/**
* Set an entry to be expanded and therefore stick in the heads up area if it's pinned
* until it's collapsed again.
*/
- public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
- HeadsUpManager.HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
- if (headsUpEntry != null && entry.row.isPinned()) {
- headsUpEntry.expanded(expanded);
+ public void setExpanded(NotificationData.Entry entry, boolean expanded) {
+ HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+ if (headsUpEntry != null && headsUpEntry.expanded != expanded && entry.row.isPinned()) {
+ headsUpEntry.expanded = expanded;
+ if (expanded) {
+ headsUpEntry.removeAutoRemovalCallbacks();
+ } else {
+ headsUpEntry.updateEntry(false /* updatePostTime */);
+ }
}
}
- @NonNull
- protected HeadsUpEntry createHeadsUpEntry() {
- return new HeadsUpEntry();
+ @Override
+ public void onReorderingAllowed() {
+ mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
+ for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+ if (isHeadsUp(entry.key)) {
+ // Maybe the heads-up was removed already
+ removeHeadsUpEntry(entry);
+ }
+ }
+ mEntriesToRemoveWhenReorderingAllowed.clear();
+ mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
}
- protected void releaseHeadsUpEntry(@NonNull HeadsUpEntry entry) {
- // Do nothing for HeadsUpEntry.
+ public void setVisualStabilityManager(VisualStabilityManager visualStabilityManager) {
+ mVisualStabilityManager = visualStabilityManager;
+ }
+
+ public void setStatusBarState(int statusBarState) {
+ mStatusBarState = statusBarState;
}
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created.
*/
- protected class HeadsUpEntry implements Comparable<HeadsUpEntry> {
- @Nullable public NotificationData.Entry entry;
+ public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+ public NotificationData.Entry entry;
public long postTime;
- public boolean remoteInputActive;
public long earliestRemovaltime;
+ private Runnable mRemoveHeadsUpRunnable;
+ public boolean remoteInputActive;
public boolean expanded;
- private Runnable mRemoveHeadsUpRunnable;
-
- public void setEntry(@Nullable final NotificationData.Entry entry) {
- setEntry(entry, null);
- }
-
- public void setEntry(@Nullable final NotificationData.Entry entry,
- @Nullable Runnable removeHeadsUpRunnable) {
+ public void setEntry(final NotificationData.Entry entry) {
this.entry = entry;
- this.mRemoveHeadsUpRunnable = removeHeadsUpRunnable;
// The actual post time will be just after the heads-up really slided in
postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay;
- updateEntry(true /* updatePostTime */);
+ mRemoveHeadsUpRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!mVisualStabilityManager.isReorderingAllowed()) {
+ mEntriesToRemoveWhenReorderingAllowed.add(entry);
+ mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManager.this);
+ } else if (!mTrackingHeadsUp) {
+ removeHeadsUpEntry(entry);
+ } else {
+ mEntriesToRemoveAfterExpand.add(entry);
+ }
+ }
+ };
+ updateEntry();
+ }
+
+ public void updateEntry() {
+ updateEntry(true);
}
public void updateEntry(boolean updatePostTime) {
- if (DEBUG) Log.v(TAG, "updateEntry");
-
long currentTime = mClock.currentTimeMillis();
earliestRemovaltime = currentTime + mMinimumDisplayTime;
if (updatePostTime) {
postTime = Math.max(postTime, currentTime);
}
removeAutoRemovalCallbacks();
-
+ if (mEntriesToRemoveAfterExpand.contains(entry)) {
+ mEntriesToRemoveAfterExpand.remove(entry);
+ }
+ if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
+ mEntriesToRemoveWhenReorderingAllowed.remove(entry);
+ }
if (!isSticky()) {
long finishTime = postTime + mHeadsUpNotificationDecay;
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
@@ -493,7 +707,7 @@
}
@Override
- public int compareTo(@NonNull HeadsUpEntry o) {
+ public int compareTo(HeadsUpEntry o) {
boolean isPinned = entry.row.isPinned();
boolean otherPinned = o.entry.row.isPinned();
if (isPinned && !otherPinned) {
@@ -520,29 +734,26 @@
: -1;
}
- public void expanded(boolean expanded) {
- this.expanded = expanded;
- }
-
- public void reset() {
- entry = null;
- expanded = false;
- remoteInputActive = false;
- removeAutoRemovalCallbacks();
- mRemoveHeadsUpRunnable = null;
- }
-
public void removeAutoRemovalCallbacks() {
- if (mRemoveHeadsUpRunnable != null)
- mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+ mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+ }
+
+ public boolean wasShownLongEnough() {
+ return earliestRemovaltime < mClock.currentTimeMillis();
}
public void removeAsSoonAsPossible() {
- if (mRemoveHeadsUpRunnable != null) {
- removeAutoRemovalCallbacks();
- mHandler.postDelayed(mRemoveHeadsUpRunnable,
- earliestRemovaltime - mClock.currentTimeMillis());
- }
+ removeAutoRemovalCallbacks();
+ mHandler.postDelayed(mRemoveHeadsUpRunnable,
+ earliestRemovaltime - mClock.currentTimeMillis());
+ }
+
+ public void reset() {
+ removeAutoRemovalCallbacks();
+ entry = null;
+ mRemoveHeadsUpRunnable = null;
+ expanded = false;
+ remoteInputActive = false;
}
}
@@ -551,4 +762,5 @@
return SystemClock.elapsedRealtime();
}
}
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
deleted file mode 100644
index 1e3c123c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.view.View;
-
-import com.android.systemui.R;
-
-/**
- * A class of utility static methods for heads up notifications.
- */
-public final class HeadsUpUtil {
- private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
-
- /**
- * Set the given view as clicked or not-clicked.
- * @param view The view to be set the flag to.
- * @param clicked True to set as clicked. False to not-clicked.
- */
- public static void setIsClickedHeadsUpNotification(View view, boolean clicked) {
- view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
- }
-
- /**
- * Check if the given view has the flag of "clicked notification"
- * @param view The view to be checked.
- * @return True if the view has clicked. False othrewise.
- */
- public static boolean isClickedHeadsUpNotification(View view) {
- Boolean clicked = (Boolean) view.getTag(TAG_CLICKED_NOTIFICATION);
- return clicked != null && clicked;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index d7a810e..424858a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -64,7 +64,7 @@
private boolean mPanelTracking;
private boolean mExpansionChanging;
private boolean mPanelFullWidth;
- private boolean mPulsing;
+ private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
private boolean mUnlockHintRunning;
private boolean mQsCustomizerShowing;
private int mIntrinsicPadding;
@@ -315,18 +315,23 @@
}
public boolean hasPulsingNotifications() {
- return mPulsing;
+ return mPulsing != null;
}
- public void setPulsing(boolean hasPulsing) {
+ public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> hasPulsing) {
mPulsing = hasPulsing;
}
public boolean isPulsing(NotificationData.Entry entry) {
- if (!mPulsing || mHeadsUpManager == null) {
+ if (mPulsing == null) {
return false;
}
- return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
+ for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
+ if (e.entry == entry) {
+ return true;
+ }
+ }
+ return false;
}
public boolean isPanelTracking() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 1b55a5b..c114a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -92,11 +92,10 @@
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import android.support.v4.graphics.ColorUtils;
@@ -289,7 +288,7 @@
private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
= new HashSet<>();
- private HeadsUpManagerPhone mHeadsUpManager;
+ private HeadsUpManager mHeadsUpManager;
private boolean mTrackingHeadsUp;
private ScrimController mScrimController;
private boolean mForceNoOverlappingRendering;
@@ -359,7 +358,7 @@
}
};
private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
- private boolean mPulsing;
+ private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
private boolean mDrawBackgroundAsSrc;
private boolean mFadingOut;
private boolean mParentNotFullyVisible;
@@ -691,7 +690,7 @@
}
private void updateAlgorithmHeightAndPadding() {
- if (mPulsing) {
+ if (mPulsing != null) {
mTopPadding = mClockBottom;
} else {
mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
@@ -921,27 +920,6 @@
}
/**
- * @return the height of the top heads up notification when pinned. This is different from the
- * intrinsic height, which also includes whether the notification is system expanded and
- * is mainly used when dragging down from a heads up notification.
- */
- private int getTopHeadsUpPinnedHeight() {
- NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
- if (topEntry == null) {
- return 0;
- }
- ExpandableNotificationRow row = topEntry.row;
- if (row.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(row.getStatusBarNotification());
- if (groupSummary != null) {
- row = groupSummary;
- }
- }
- return row.getPinnedHeadsUpHeight();
- }
-
- /**
* @return the position from where the appear transition ends when expanding.
* Measured in absolute height.
*/
@@ -952,7 +930,7 @@
int minNotificationsForShelf = 1;
if (mTrackingHeadsUp
|| (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
- appearPosition = getTopHeadsUpPinnedHeight();
+ appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
minNotificationsForShelf = 2;
} else {
appearPosition = 0;
@@ -1220,9 +1198,9 @@
if (slidingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mHeadsUpManager.getTopEntry().row != row
+ && mHeadsUpManager.getTopEntry().entry.row != row
&& mGroupManager.getGroupSummary(
- mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
+ mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
!= row) {
continue;
}
@@ -2142,7 +2120,7 @@
@Override
public boolean hasPulsingNotifications() {
- return mPulsing;
+ return mPulsing != null;
}
private void updateScrollability() {
@@ -2775,7 +2753,7 @@
}
private boolean isClickedHeadsUp(View child) {
- return HeadsUpUtil.isClickedHeadsUpNotification(child);
+ return HeadsUpManager.isClickedHeadsUpNotification(child);
}
/**
@@ -4280,7 +4258,7 @@
mAnimationFinishedRunnables.add(runnable);
}
- public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
mHeadsUpManager = headsUpManager;
mAmbientState.setHeadsUpManager(headsUpManager);
}
@@ -4348,8 +4326,8 @@
return mIsExpanded;
}
- public void setPulsing(boolean pulsing, int clockBottom) {
- if (!mPulsing && !pulsing) {
+ public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing, int clockBottom) {
+ if (mPulsing == null && pulsing == null) {
return;
}
mPulsing = pulsing;
@@ -4488,7 +4466,7 @@
pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ " alpha:%f scrollY:%d]",
this.getClass().getSimpleName(),
- mPulsing ? "T":"f",
+ mPulsing != null ?"T":"f",
mAmbientState.isQsCustomizerShowing() ? "T":"f",
getVisibility() == View.VISIBLE ? "visible"
: getVisibility() == View.GONE ? "gone"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 04a7bd7..682b849 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -30,7 +30,7 @@
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
/**
* A state of a view. This can be used to apply a set of view properties to a view with
@@ -582,7 +582,7 @@
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
+ HeadsUpManager.setIsClickedNotification(child, false);
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
child.setTag(TAG_START_TRANSLATION_Y, null);
child.setTag(TAG_END_TRANSLATION_Y, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index f3c1171..6e7477f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,7 +32,6 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationInflaterTest;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -52,7 +51,7 @@
public NotificationTestHelper(Context context) {
mContext = context;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
+ mHeadsUpManager = new HeadsUpManager(mContext, null, mGroupManager);
}
public ExpandableNotificationRow createRow() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
deleted file mode 100644
index 28f9417..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.view.View;
-import android.service.notification.StatusBarNotification;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class HeadsUpManagerPhoneTest extends SysuiTestCase {
- @Rule public MockitoRule rule = MockitoJUnit.rule();
-
- private static final String TEST_PACKAGE_NAME = "test";
- private static final int TEST_UID = 0;
-
- private HeadsUpManagerPhone mHeadsUpManager;
-
- private NotificationData.Entry mEntry;
- private StatusBarNotification mSbn;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper());
-
- @Mock private NotificationGroupManager mGroupManager;
- @Mock private View mStatusBarWindowView;
- @Mock private StatusBar mBar;
- @Mock private ExpandableNotificationRow mRow;
- @Mock private VisualStabilityManager mVSManager;
-
- @Before
- public void setUp() {
- when(mVSManager.isReorderingAllowed()).thenReturn(true);
-
- mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarWindowView, mGroupManager, mBar, mVSManager);
-
- Notification.Builder n = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
- mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
- 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
-
- mEntry = new NotificationData.Entry(mSbn);
- mEntry.row = mRow;
- mEntry.expandedIcon = mock(StatusBarIconView.class);
- }
-
- @Test
- public void testBasicOperations() {
- // Check the initial state.
- assertNull(mHeadsUpManager.getEntry(mEntry.key));
- assertNull(mHeadsUpManager.getTopEntry());
- assertEquals(0, mHeadsUpManager.getAllEntries().count());
- assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
-
- // Add a notification.
- mHeadsUpManager.showNotification(mEntry);
-
- assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
- assertEquals(mEntry, mHeadsUpManager.getTopEntry());
- assertEquals(1, mHeadsUpManager.getAllEntries().count());
- assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
-
- // Update the notification.
- mHeadsUpManager.updateNotification(mEntry, false);
-
- assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
- assertEquals(mEntry, mHeadsUpManager.getTopEntry());
- assertEquals(1, mHeadsUpManager.getAllEntries().count());
- assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
-
- // Remove but defer, since the notification is visible on display.
- mHeadsUpManager.removeNotification(mEntry.key, false);
-
- assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
- assertEquals(mEntry, mHeadsUpManager.getTopEntry());
- assertEquals(1, mHeadsUpManager.getAllEntries().count());
- assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 31442af..bdf9b1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -86,8 +86,8 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -110,7 +110,7 @@
@Mock private UnlockMethodCache mUnlockMethodCache;
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
- @Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private HeadsUpManager mHeadsUpManager;
@Mock private SystemServicesProxy mSystemServicesProxy;
@Mock private NotificationPanelView mNotificationPanelView;
@Mock private IStatusBarService mBarService;
@@ -588,7 +588,7 @@
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
- NotificationStackScrollLayout stack, HeadsUpManagerPhone hum,
+ NotificationStackScrollLayout stack, HeadsUpManager hum,
PowerManager pm, NotificationPanelView panelView,
IStatusBarService barService, NotificationListener notificationListener,
NotificationLogger notificationLogger,
@@ -650,7 +650,7 @@
public void setUpForTest(NotificationPresenter presenter,
NotificationListContainer listContainer,
Callback callback,
- HeadsUpManagerPhone headsUpManager,
+ HeadsUpManager headsUpManager,
NotificationData notificationData) {
super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
mNotificationData = notificationData;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
index c18ed73..f7bb065 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
@@ -44,11 +44,13 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+@Ignore
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -87,7 +89,7 @@
public void tearDown() throws Exception {
TestableLooper.get(this).processAllMessages();
}
-
+/*
@Test
public void testClickMediaRouterItemConnectsMedia() {
mDialog.show();
@@ -137,7 +139,7 @@
.getText().toString().contains("Phone"));
mDialog.dismiss();
}
-
+*/
@Test
public void testNoMediaScanIfInCall() {
mDialog.setIsInCall(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 4888fb2..43d60e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -55,6 +56,7 @@
import java.util.function.Predicate;
+@Ignore
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -111,7 +113,7 @@
+ " failed test", condition.test(view));
}
}
-
+/*
@Test
public void testContentDescriptions() {
mDialog.show(SHOW_REASON_UNKNOWN);
@@ -218,4 +220,5 @@
verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
}
+ */
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
deleted file mode 100644
index 4ab2063..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/ZenModePanelTest.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.net.Uri;
-import android.provider.Settings;
-import android.service.notification.Condition;
-import android.service.notification.ZenModeConfig;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.FlakyTest;
-import android.view.LayoutInflater;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Ignore
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ZenModePanelTest extends SysuiTestCase {
-
- ZenModePanel mPanel;
- ZenModeController mController;
- Uri mForeverId;
-
- @Before
- public void setup() throws Exception {
- final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
- mPanel = (ZenModePanel) layoutInflater.inflate(com.android.systemui.R.layout.zen_mode_panel,
- null);
- mController = mock(ZenModeController.class);
- mForeverId = Condition.newId(mContext).appendPath("forever").build();
-
- mPanel.init(mController);
- }
-
- private void assertProperConditionTagTypes(boolean hasAlarm) {
- final int N = mPanel.getVisibleConditions();
- assertEquals(hasAlarm ? 3 : 2, N);
-
- assertEquals(mForeverId, mPanel.getConditionTagAt(0).condition.id);
- assertTrue(ZenModeConfig.isValidCountdownConditionId(
- mPanel.getConditionTagAt(1).condition.id));
- assertFalse(ZenModeConfig.isValidCountdownToAlarmConditionId(
- mPanel.getConditionTagAt(1).condition.id));
- if (hasAlarm) {
- assertTrue(ZenModeConfig.isValidCountdownToAlarmConditionId(
- mPanel.getConditionTagAt(2).condition.id));
- }
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_alarmExists() {
- Condition forever = new Condition(mForeverId, "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
- mPanel.handleUpdateConditions(forever);
- assertProperConditionTagTypes(true);
- assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_noAlarm() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
- Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn((long) 0);
-
- mPanel.handleUpdateConditions(forever);
- assertProperConditionTagTypes(false);
- assertEquals(foreverId, mPanel.getConditionTagAt(0).condition.id);
- }
-
- @Test
- public void testHandleUpdateConditions_countdownSelected_alarmExists() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
- Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
- "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
- mPanel.handleUpdateConditions(countdown);
- assertProperConditionTagTypes(true);
- assertTrue(mPanel.getConditionTagAt(1).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_countdownSelected_noAlarm() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
- Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
- "", Condition.STATE_TRUE);
-
- when(mController.getNextAlarm()).thenReturn((long) 0);
-
- mPanel.handleUpdateConditions(countdown);
- assertProperConditionTagTypes(false);
- assertTrue(mPanel.getConditionTagAt(1).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_nextAlarmSelected() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
-
- Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + 1000, true),
- "", Condition.STATE_TRUE);
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 9000);
-
- mPanel.handleUpdateConditions(alarm);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_alarmConditionDoesNotChangeIfAttached() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
- Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
- Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + 9000, true),
- "", Condition.STATE_TRUE);
- when(mController.getNextAlarm()).thenReturn(System.currentTimeMillis() + 1000);
-
- mPanel.handleUpdateConditions(alarm);
- mPanel.setAttached(true);
- mPanel.handleUpdateConditions(forever);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
- }
-
- @Test
- public void testHandleUpdateConditions_foreverSelected_timeConditionDoesNotChangeIfAttached() {
- Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
- Condition forever = new Condition(foreverId, "", Condition.STATE_TRUE);
-
- Condition countdown = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + (3 * 60 * 60 * 1000) + 4000, false),
- "", Condition.STATE_TRUE);
- when(mController.getNextAlarm()).thenReturn((long) 0);
-
- mPanel.handleUpdateConditions(countdown);
- mPanel.setAttached(true);
- mPanel.handleUpdateConditions(forever);
-
- assertProperConditionTagTypes(false);
- assertEquals(countdown, mPanel.getConditionTagAt(1).condition);
- assertTrue(mPanel.getConditionTagAt(0).rb.isChecked());
- }
-
- @Test
- @UiThreadTest
- public void testHandleUpdateManualRule_stillSelectedAfterModeChange() {
- ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
-
- Condition alarm = new Condition(ZenModeConfig.toCountdownConditionId(
- System.currentTimeMillis() + 1000, true),
- "", Condition.STATE_TRUE);
-
- rule.condition = alarm;
- rule.conditionId = alarm.id;
- rule.enabled = true;
- rule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-
- mPanel.handleUpdateManualRule(rule);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
-
- assertEquals(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
- mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF));
-
- rule.zenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
-
- mPanel.handleUpdateManualRule(rule);
-
- assertProperConditionTagTypes(true);
- assertEquals(alarm, mPanel.getConditionTagAt(2).condition);
- assertTrue(mPanel.getConditionTagAt(2).rb.isChecked());
-
- assertEquals(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS,
- mPanel.getSelectedZen(Settings.Global.ZEN_MODE_OFF));
- }
-}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 219facd..0502117 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -34,7 +34,7 @@
2731 power_soft_sleep_requested (savedwaketimems|2)
# Power save state has changed. See BatterySaverController.java for the details.
2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
-27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|6),(total_duration|2|3),(total_battery_drain|1|6)
+27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|1),(delta_battery_drain_percent|1|6),(total_duration|2|3),(total_battery_drain|1|1),(total_battery_drain_percent|1|6)
#
# Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 1dd92f3..65e90bad 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1397,23 +1397,6 @@
}
/**
- * Returns "true" if access to the specified location provider is allowed by the specified
- * user's settings. Access to all location providers is forbidden to non-location-provider
- * processes belonging to background users.
- *
- * @param provider the name of the location provider
- * @param uid the requestor's UID
- * @param userId the user id to query
- */
- private boolean isAllowedByUserSettingsLockedForUser(
- String provider, int uid, int userId) {
- if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
- return false;
- }
- return isLocationProviderEnabledForUser(provider, userId);
- }
-
- /**
* Returns the permission string associated with the specified resolution level.
*
* @param resolutionLevel the resolution level
@@ -2585,143 +2568,6 @@
}
/**
- * Method for enabling or disabling location.
- *
- * @param enabled true to enable location. false to disable location
- * @param userId the user id to set
- */
- @Override
- public void setLocationEnabledForUser(boolean enabled, int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Enable or disable all location providers. Fused provider and passive provider are
- // excluded.
- synchronized (mLock) {
- for(String provider : getAllProvidersForLocationSettings()) {
- setProviderEnabledForUser(provider, enabled, userId);
- }
- }
- }
-
- /**
- * Returns the current enabled/disabled status of location
- *
- * @param userId the user id to query
- * @return true if location is enabled. false if location is disabled.
- */
- @Override
- public boolean isLocationEnabledForUser(int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // If at least one location provider is enabled, return true. Fused provider and passive
- // provider are excluded.
- synchronized (mLock) {
- for (String provider : getAllProvidersForLocationSettings()) {
- if (isProviderEnabledForUser(provider, userId)) {
- return true;
- }
- }
- return false;
- }
- }
-
- @Override
- public boolean isProviderEnabled(String provider) {
- return isProviderEnabledForUser(provider, UserHandle.getCallingUserId());
- }
-
- /**
- * Method for determining if a location provider is enabled.
- *
- * @param provider the location provider to query
- * @param userId the user id to query
- * @return true if the provider is enabled
- */
- @Override
- public boolean isProviderEnabledForUser(String provider, int userId) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
- // so we discourage its use
- if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
- int uid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- return p != null
- && isAllowedByUserSettingsLockedForUser(provider, uid, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Method for enabling or disabling a single location provider.
- *
- * @param provider the name of the provider
- * @param enabled true to enable the provider. false to disable the provider
- * @param userId the user id to set
- * @return true if the value was set successfully. false on failure.
- */
- @Override
- public boolean setProviderEnabledForUser(
- String provider, boolean enabled, int userId) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS,
- "Requires WRITE_SECURE_SETTINGS permission");
-
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- // to ensure thread safety, we write the provider name with a '+' or '-'
- // and let the SettingsProvider handle it rather than reading and modifying
- // the list of enabled providers.
- if (enabled) {
- provider = "+" + provider;
- } else {
- provider = "-" + provider;
- }
- return Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- provider,
- userId);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Return all location providers except fused provider and passive provider. These two
- * providers are not generating location by themselves, but only echo locations from other
- * providers.
- *
- * @return All location providers except fused provider and passive provider, including
- * providers that are not permitted to be accessed by the calling activity or are
- * currently disabled.
- */
- private List<String> getAllProvidersForLocationSettings() {
- List<String> providersForSettings = new ArrayList<>(mProviders.size());
- for (String provider : getAllProviders()) {
- if (provider.equals(LocationManager.PASSIVE_PROVIDER)) {
- continue;
- }
- providersForSettings.add(provider);
- }
- return providersForSettings;
- }
-
- /**
* Read location provider status from Settings.Secure
*
* @param provider the location provider to query
@@ -2742,23 +2588,6 @@
}
/**
- * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
- * current user id
- *
- * @param userId the user id to get or set value
- */
- private void checkInteractAcrossUsersPermission(int userId) {
- int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != userId) {
- if (ActivityManager.checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
- != PERMISSION_GRANTED) {
- throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
- }
- }
- }
-
- /**
* Returns "true" if the UID belongs to a bound location provider.
*
* @param uid the uid
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5b8b691..f370393 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4027,9 +4027,13 @@
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
- if ("true".equals(genDebugInfoProperty)) {
+ if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
}
+ String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
+ if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
+ runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
+ }
if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
}
@@ -4331,7 +4335,9 @@
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
component.userId, component.realActivity.getPackageName(),
- component.realActivity.getShortClassName(), resumed ? 1 : 0);
+ component.realActivity.getShortClassName(), resumed ?
+ StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND :
+ StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND);
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
@@ -12844,7 +12850,8 @@
}
// TODO: Where should the corresponding '1' (start) write go?
- StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED, 0);
+ StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED,
+ StatsLog.DEVICE_ON_STATUS_CHANGED__STATE__OFF);
boolean timedout = false;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 0d96468..ea52782 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -325,39 +325,38 @@
void noteProcessStart(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessStartLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 1);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_STARTED);
}
}
void noteProcessCrash(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessCrashLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 2);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_CRASHED);
}
}
void noteProcessAnr(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessAnrLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 3);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_ANRED);
}
}
void noteProcessFinish(String name, int uid) {
synchronized (mStats) {
mStats.noteProcessFinishLocked(name, uid);
- // TODO: decide where this should be and use a constant instead of a literal.
- StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name, 0);
+ StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
+ StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_FINISHED);
}
}
/** @param state Process state from ActivityManager.java. */
void noteUidProcessState(int uid, int state) {
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
StatsLog.write(StatsLog.UID_PROCESS_STATE_CHANGED, uid,
ActivityManager.processStateAmToProto(state));
@@ -603,7 +602,6 @@
enforceCallingPermission();
if (DBG) Slog.d(TAG, "begin noteScreenState");
synchronized (mStats) {
- // TODO: remove this once we figure out properly where and how
StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, state);
mStats.noteScreenStateLocked(state);
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index e5762d2..21f9135 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -38,7 +38,6 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
@@ -143,6 +142,14 @@
TelecomManager mTelecomManager;
/**
+ * Helper that is responsible for showing the right toast when a disallowed activity operation
+ * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
+ * fully locked mode we only show that unlocking is blocked.
+ */
+ @VisibleForTesting
+ LockTaskNotify mLockTaskNotify;
+
+ /**
* The chain of tasks in LockTask mode, in the order of when they first entered LockTask mode.
*
* The first task in the list, which started the current LockTask session, is called the root
@@ -468,7 +475,7 @@
getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
}
if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
- getStatusBarService().showPinningEnterExitToast(false /* entering */);
+ getLockTaskNotify().showPinningExitToast();
}
} catch (RemoteException ex) {
throw new RuntimeException(ex);
@@ -483,11 +490,7 @@
*/
void showLockTaskToast() {
if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
- try {
- getStatusBarService().showPinningEscapeToast();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to send pinning escape toast", e);
- }
+ mHandler.post(() -> getLockTaskNotify().showEscapeToast());
}
}
@@ -579,7 +582,7 @@
// When lock task starts, we disable the status bars.
try {
if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
- getStatusBarService().showPinningEnterExitToast(true /* entering */);
+ getLockTaskNotify().showPinningStartToast();
}
mLockTaskModeState = lockTaskModeState;
setStatusBarState(lockTaskModeState, userId);
@@ -832,6 +835,15 @@
return mTelecomManager;
}
+ // Should only be called on the handler thread
+ @NonNull
+ private LockTaskNotify getLockTaskNotify() {
+ if (mLockTaskNotify == null) {
+ mLockTaskNotify = new LockTaskNotify(mContext);
+ }
+ return mLockTaskNotify;
+ }
+
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "LockTaskController");
prefix = prefix + " ";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
rename to services/core/java/com/android/server/am/LockTaskNotify.java
index 0d07ad9..1dcb0ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package com.android.server.am;
import android.content.Context;
import android.os.SystemClock;
@@ -22,37 +22,36 @@
import android.view.WindowManager;
import android.widget.Toast;
-import com.android.systemui.R;
-import com.android.systemui.SysUIToast;
+import com.android.internal.R;
/**
* Helper to manage showing/hiding a image to notify them that they are entering or exiting screen
* pinning mode. All exposed methods should be called from a handler thread.
*/
-public class ScreenPinningNotify {
- private static final String TAG = "ScreenPinningNotify";
+public class LockTaskNotify {
+ private static final String TAG = "LockTaskNotify";
private static final long SHOW_TOAST_MINIMUM_INTERVAL = 1000;
private final Context mContext;
private Toast mLastToast;
private long mLastShowToastTime;
- public ScreenPinningNotify(Context context) {
+ public LockTaskNotify(Context context) {
mContext = context;
}
/** Show "Screen pinned" toast. */
void showPinningStartToast() {
- makeAllUserToastAndShow(R.string.screen_pinning_start);
+ makeAllUserToastAndShow(R.string.lock_to_app_start);
}
/** Show "Screen unpinned" toast. */
void showPinningExitToast() {
- makeAllUserToastAndShow(R.string.screen_pinning_exit);
+ makeAllUserToastAndShow(R.string.lock_to_app_exit);
}
/** Show a toast that describes the gesture the user should use to escape pinned mode. */
- void showEscapeToast(boolean isRecentsButtonVisible) {
+ void showEscapeToast() {
long showToastTime = SystemClock.elapsedRealtime();
if ((showToastTime - mLastShowToastTime) < SHOW_TOAST_MINIMUM_INTERVAL) {
Slog.i(TAG, "Ignore toast since it is requested in very short interval.");
@@ -61,14 +60,14 @@
if (mLastToast != null) {
mLastToast.cancel();
}
- mLastToast = makeAllUserToastAndShow(isRecentsButtonVisible
- ? R.string.screen_pinning_toast
- : R.string.screen_pinning_toast_recents_invisible);
+ mLastToast = makeAllUserToastAndShow(R.string.lock_to_app_toast);
mLastShowToastTime = showToastTime;
}
private Toast makeAllUserToastAndShow(int resId) {
- Toast toast = SysUIToast.makeText(mContext, resId, Toast.LENGTH_LONG);
+ Toast toast = Toast.makeText(mContext, resId, Toast.LENGTH_LONG);
+ toast.getWindowParams().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
toast.show();
return toast;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 6816fd1..b27f1ec 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -479,6 +479,7 @@
mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
mTemporaryScreenBrightness = -1;
+ mPendingScreenBrightnessSetting = -1;
mTemporaryAutoBrightnessAdjustment = Float.NaN;
}
@@ -1476,6 +1477,7 @@
mPendingScreenBrightnessSetting = -1;
return false;
}
+ mCurrentScreenBrightnessSetting = mPendingScreenBrightnessSetting;
mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
mPendingScreenBrightnessSetting = -1;
return true;
@@ -1484,7 +1486,8 @@
private void notifyBrightnessChanged(int brightness, boolean userInitiated,
boolean hadUserDataPoint) {
final float brightnessInNits = convertToNits(brightness);
- if (brightnessInNits >= 0.0f && mAutomaticBrightnessController != null) {
+ if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
+ && mAutomaticBrightnessController != null) {
// We only want to track changes on devices that can actually map the display backlight
// values into a physical brightness unit since the value provided by the API is in
// nits and not using the arbitrary backlight units.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index e0baeee..401c05e 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -862,7 +862,8 @@
}
startTrackingJobLocked(jobStatus, toCancel);
StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
- uId, null, jobStatus.getBatteryName(), 2);
+ uId, null, jobStatus.getBatteryName(),
+ StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED);
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index d96671c..79fd496 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -28,7 +28,7 @@
* Helper for creating the recoverable key database.
*/
class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
- private static final int DATABASE_VERSION = 1;
+ private static final int DATABASE_VERSION = 2;
private static final String DATABASE_NAME = "recoverablekeystore.db";
private static final String SQL_CREATE_KEYS_ENTRY =
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index c02331d..5060c4d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -693,7 +693,7 @@
InputStream fileIn = new GZIPInputStream(new FileInputStream(srcFile));
OutputStream fileOut = new FileOutputStream(dstFile, false /*append*/);
) {
- Streams.copy(fileIn, fileOut);
+ FileUtils.copy(fileIn, fileOut);
Os.chmod(dstFile.getAbsolutePath(), 0644);
return PackageManager.INSTALL_SUCCEEDED;
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 9466350..b0b07ea 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -32,6 +32,8 @@
/**
* This class keeps track of battery drain rate.
*
+ * TODO: The use of the terms "percent" and "level" in this class is not standard. Fix it.
+ *
* Test:
atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
*/
@@ -96,8 +98,12 @@
public int startBatteryLevel;
public int endBatteryLevel;
+ public int startBatteryPercent;
+ public int endBatteryPercent;
+
public long totalTimeMillis;
public int totalBatteryDrain;
+ public int totalBatteryDrainPercent;
public long totalMinutes() {
return totalTimeMillis / 60_000;
@@ -110,14 +116,26 @@
return (double) totalBatteryDrain / (totalTimeMillis / (60.0 * 60 * 1000));
}
+ public double drainPercentPerHour() {
+ if (totalTimeMillis == 0) {
+ return 0;
+ }
+ return (double) totalBatteryDrainPercent / (totalTimeMillis / (60.0 * 60 * 1000));
+ }
+
@VisibleForTesting
String toStringForTest() {
return "{" + totalMinutes() + "m," + totalBatteryDrain + ","
- + String.format("%.2f", drainPerHour()) + "}";
+ + String.format("%.2f", drainPerHour()) + "uA/H,"
+ + String.format("%.2f", drainPercentPerHour()) + "%"
+ + "}";
}
}
@VisibleForTesting
+ static final String COUNTER_POWER_PERCENT_PREFIX = "battery_saver_stats_percent_";
+
+ @VisibleForTesting
static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_";
@VisibleForTesting
@@ -166,6 +184,9 @@
private BatteryManagerInternal getBatteryManagerInternal() {
if (mBatteryManagerInternal == null) {
mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+ if (mBatteryManagerInternal == null) {
+ Slog.wtf(TAG, "BatteryManagerInternal not initialized");
+ }
}
return mBatteryManagerInternal;
}
@@ -229,12 +250,20 @@
int injectBatteryLevel() {
final BatteryManagerInternal bmi = getBatteryManagerInternal();
if (bmi == null) {
- Slog.wtf(TAG, "BatteryManagerInternal not initialized");
return 0;
}
return bmi.getBatteryChargeCounter();
}
+ @VisibleForTesting
+ int injectBatteryPercent() {
+ final BatteryManagerInternal bmi = getBatteryManagerInternal();
+ if (bmi == null) {
+ return 0;
+ }
+ return bmi.getBatteryLevel();
+ }
+
/**
* Called from the outside whenever any of the states changes, when the device is not plugged
* in.
@@ -262,33 +291,39 @@
}
final long now = injectCurrentTime();
final int batteryLevel = injectBatteryLevel();
+ final int batteryPercent = injectBatteryPercent();
- endLastStateLocked(now, batteryLevel);
- startNewStateLocked(newState, now, batteryLevel);
- mMetricsLoggerHelper.transitionState(newState, now, batteryLevel);
+ endLastStateLocked(now, batteryLevel, batteryPercent);
+ startNewStateLocked(newState, now, batteryLevel, batteryPercent);
+ mMetricsLoggerHelper.transitionState(newState, now, batteryLevel, batteryPercent);
}
- private void endLastStateLocked(long now, int batteryLevel) {
+ private void endLastStateLocked(long now, int batteryLevel, int batteryPercent) {
if (mCurrentState < 0) {
return;
}
final Stat stat = getStat(mCurrentState);
stat.endBatteryLevel = batteryLevel;
+ stat.endBatteryPercent = batteryPercent;
stat.endTime = now;
final long deltaTime = stat.endTime - stat.startTime;
final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel;
+ final int deltaPercent = stat.startBatteryPercent - stat.endBatteryPercent;
stat.totalTimeMillis += deltaTime;
stat.totalBatteryDrain += deltaDrain;
+ stat.totalBatteryDrainPercent += deltaPercent;
if (DEBUG) {
Slog.d(TAG, "State summary: " + stateToString(mCurrentState)
+ ": " + (deltaTime / 1_000) + "s "
+ "Start level: " + stat.startBatteryLevel + "uA "
+ "End level: " + stat.endBatteryLevel + "uA "
- + deltaDrain + "uA");
+ + "Start percent: " + stat.startBatteryPercent + "% "
+ + "End percent: " + stat.endBatteryPercent + "% "
+ + "Drain " + deltaDrain + "uA");
}
EventLogTags.writeBatterySavingStats(
BatterySaverState.fromIndex(mCurrentState),
@@ -296,12 +331,14 @@
DozeState.fromIndex(mCurrentState),
deltaTime,
deltaDrain,
+ deltaPercent,
stat.totalTimeMillis,
- stat.totalBatteryDrain);
+ stat.totalBatteryDrain,
+ stat.totalBatteryDrainPercent);
}
- private void startNewStateLocked(int newState, long now, int batteryLevel) {
+ private void startNewStateLocked(int newState, long now, int batteryLevel, int batteryPercent) {
if (DEBUG) {
Slog.d(TAG, "New state: " + stateToString(newState));
}
@@ -313,6 +350,7 @@
final Stat stat = getStat(mCurrentState);
stat.startBatteryLevel = batteryLevel;
+ stat.startBatteryPercent = batteryPercent;
stat.startTime = now;
stat.endTime = 0;
}
@@ -325,7 +363,7 @@
indent = indent + " ";
pw.print(indent);
- pw.println("Battery Saver: Off On");
+ pw.println("Battery Saver: Off On");
dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
DozeState.NOT_DOZING, "NonDoze");
dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr",
@@ -357,12 +395,14 @@
final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState);
final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState);
- pw.println(String.format("%6dm %6dmA %8.1fmA/h %6dm %6dmA %8.1fmA/h",
+ pw.println(String.format("%6dm %6dmA (%3d%%) %8.1fmA/h %6dm %6dmA (%3d%%) %8.1fmA/h",
offStat.totalMinutes(),
offStat.totalBatteryDrain / 1000,
+ offStat.totalBatteryDrainPercent,
offStat.drainPerHour() / 1000.0,
onStat.totalMinutes(),
onStat.totalBatteryDrain / 1000,
+ onStat.totalBatteryDrainPercent,
onStat.drainPerHour() / 1000.0));
}
@@ -371,12 +411,13 @@
private int mLastState = STATE_NOT_INITIALIZED;
private long mStartTime;
private int mStartBatteryLevel;
+ private int mStartPercent;
private static final int STATE_CHANGE_DETECT_MASK =
(BatterySaverState.MASK << BatterySaverState.SHIFT) |
(InteractiveState.MASK << InteractiveState.SHIFT);
- public void transitionState(int newState, long now, int batteryLevel) {
+ public void transitionState(int newState, long now, int batteryLevel, int batteryPercent) {
final boolean stateChanging =
((mLastState >= 0) ^ (newState >= 0)) ||
(((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0);
@@ -384,11 +425,13 @@
if (mLastState >= 0) {
final long deltaTime = now - mStartTime;
final int deltaBattery = mStartBatteryLevel - batteryLevel;
+ final int deltaPercent = mStartPercent - batteryPercent;
- report(mLastState, deltaTime, deltaBattery);
+ report(mLastState, deltaTime, deltaBattery, deltaPercent);
}
mStartTime = now;
mStartBatteryLevel = batteryLevel;
+ mStartPercent = batteryPercent;
}
mLastState = newState;
}
@@ -405,9 +448,10 @@
}
}
- void report(int state, long deltaTimeMs, int deltaBatteryUa) {
+ void report(int state, long deltaTimeMs, int deltaBatteryUa, int deltaPercent) {
final String suffix = getCounterSuffix(state);
mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000);
+ mMetricsLogger.count(COUNTER_POWER_PERCENT_PREFIX + suffix, deltaPercent);
mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000));
}
}
diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java
index 591e809..5e0cd03 100644
--- a/services/core/java/com/android/server/slice/SliceFullAccessList.java
+++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java
@@ -63,6 +63,15 @@
pkgs.add(pkg);
}
+ public void removeGrant(String pkg, int userId) {
+ ArraySet<String> pkgs = mFullAccessPkgs.get(userId, null);
+ if (pkgs == null) {
+ pkgs = new ArraySet<>();
+ mFullAccessPkgs.put(userId, pkgs);
+ }
+ pkgs.remove(pkg);
+ }
+
public void writeXml(XmlSerializer out) throws IOException {
out.startTag(null, TAG_LIST);
out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 5db0fc0..c4871df 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -31,9 +31,11 @@
import android.app.slice.ISliceManager;
import android.app.slice.SliceManager;
import android.app.slice.SliceSpec;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
@@ -92,6 +94,7 @@
private final Handler mHandler;
private final ContentObserver mObserver;
private final AtomicFile mSliceAccessFile;
+ @GuardedBy("mAccessList")
private final SliceFullAccessList mAccessList;
public SliceManagerService(Context context) {
@@ -127,11 +130,19 @@
InputStream input = mSliceAccessFile.openRead();
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setInput(input, Encoding.UTF_8.name());
- mAccessList.readXml(parser);
+ synchronized (mAccessList) {
+ mAccessList.readXml(parser);
+ }
} catch (IOException | XmlPullParserException e) {
Slog.d(TAG, "Can't read slice access file", e);
}
}
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
}
/// ----- Lifecycle stuff -----
@@ -223,7 +234,9 @@
getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
"Slice granting requires MANAGE_SLICE_PERMISSIONS");
if (allSlices) {
- mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+ synchronized (mAccessList) {
+ mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
+ }
mHandler.post(mSaveAccessList);
} else {
synchronized (mLock) {
@@ -245,6 +258,13 @@
}
/// ----- internal code -----
+ private void removeFullAccess(String pkg, int userId) {
+ synchronized (mAccessList) {
+ mAccessList.removeGrant(pkg, userId);
+ }
+ mHandler.post(mSaveAccessList);
+ }
+
protected void removePinnedSlice(Uri uri) {
synchronized (mLock) {
mPinnedSlicesByUri.remove(uri).destroy();
@@ -444,7 +464,9 @@
}
private boolean isGrantedFullAccess(String pkg, int userId) {
- return mAccessList.hasFullAccess(pkg, userId);
+ synchronized (mAccessList) {
+ return mAccessList.hasFullAccess(pkg, userId);
+ }
}
private static ServiceThread createHandler() {
@@ -469,7 +491,9 @@
try {
XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
out.setOutput(stream, Encoding.UTF_8.name());
- mAccessList.writeXml(out);
+ synchronized (mAccessList) {
+ mAccessList.writeXml(out);
+ }
out.flush();
mSliceAccessFile.finishWrite(stream);
} catch (IOException | XmlPullParserException e) {
@@ -480,6 +504,35 @@
}
};
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+ return;
+ }
+ Uri data = intent.getData();
+ String pkg = data != null ? data.getSchemeSpecificPart() : null;
+ if (pkg == null) {
+ Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
+ return;
+ }
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_REMOVED:
+ final boolean replacing =
+ intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ if (!replacing) {
+ removeFullAccess(pkg, userId);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_DATA_CLEARED:
+ removeFullAccess(pkg, userId);
+ break;
+ }
+ }
+ };
+
public static class Lifecycle extends SystemService {
private SliceManagerService mService;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 7c170ae..adb368b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -56,6 +56,7 @@
import java.util.ArrayList;
import java.util.List;
+
/**
* A note on locking: We rely on the fact that calls onto mBar are oneway or
* if they are local, that they just enqueue messages to not deadlock.
@@ -524,26 +525,6 @@
}
@Override
- public void showPinningEnterExitToast(boolean entering) throws RemoteException {
- if (mBar != null) {
- try {
- mBar.showPinningEnterExitToast(entering);
- } catch (RemoteException ex) {
- }
- }
- }
-
- @Override
- public void showPinningEscapeToast() throws RemoteException {
- if (mBar != null) {
- try {
- mBar.showPinningEscapeToast();
- } catch (RemoteException ex) {
- }
- }
- }
-
- @Override
public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) {
if (mBar != null) {
try {
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index 3f32079..b00e595 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -72,20 +72,23 @@
}
/** Cancels the notification */
- void cancel() {
+ void cancel(boolean deleteChannel) {
// We can't call into NotificationManager with WM lock held since it might call into AM.
// So, we post a message to do it later.
- mService.mH.post(this::onCancelNotification);
+ mService.mH.post(() -> onCancelNotification(deleteChannel));
}
/** Don't call with the window manager lock held! */
- private void onCancelNotification() {
+ private void onCancelNotification(boolean deleteChannel) {
if (!mPosted) {
// Notification isn't currently posted...
return;
}
mPosted = false;
mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
+ if (deleteChannel) {
+ mNotificationManager.deleteNotificationChannel(mNotificationTag);
+ }
}
/** Don't call with the window manager lock held! */
@@ -146,8 +149,12 @@
final String nameChannel =
context.getString(R.string.alert_windows_notification_channel_name, appName);
- final NotificationChannel channel =
- new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
+
+ NotificationChannel channel = mNotificationManager.getNotificationChannel(mNotificationTag);
+ if (channel != null) {
+ return;
+ }
+ channel = new NotificationChannel(mNotificationTag, nameChannel, IMPORTANCE_MIN);
channel.enableLights(false);
channel.enableVibration(false);
channel.setBlockableSystem(true);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7674b5e..2512dbd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -662,7 +662,7 @@
mWallpaperController.updateWallpaperVisibility();
}
- w.handleWindowMovedIfNeeded(mPendingTransaction);
+ w.handleWindowMovedIfNeeded();
final WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -1542,11 +1542,11 @@
* Callback used to trigger bounds update after configuration change and get ids of stacks whose
* bounds were updated.
*/
- void updateStackBoundsAfterConfigChange(@NonNull List<Integer> changedStackList) {
+ void updateStackBoundsAfterConfigChange(@NonNull List<TaskStack> changedStackList) {
for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
final TaskStack stack = mTaskStackContainers.getChildAt(i);
if (stack.updateBoundsAfterConfigChange()) {
- changedStackList.add(stack.mStackId);
+ changedStackList.add(stack);
}
}
@@ -3599,8 +3599,6 @@
}
private final class AboveAppWindowContainers extends NonAppWindowContainers {
- private final Dimmer mDimmer = new Dimmer(this);
- private final Rect mTmpDimBoundsRect = new Rect();
AboveAppWindowContainers(String name, WindowManagerService service) {
super(name, service);
}
@@ -3632,22 +3630,6 @@
imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
}
}
-
- @Override
- Dimmer getDimmer() {
- return mDimmer;
- }
-
- @Override
- void prepareSurfaces() {
- mDimmer.resetDimStates();
- super.prepareSurfaces();
- getBounds(mTmpDimBoundsRect);
-
- if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
- scheduleAnimation();
- }
- }
}
/**
@@ -3679,6 +3661,9 @@
};
private final String mName;
+ private final Dimmer mDimmer = new Dimmer(this);
+ private final Rect mTmpDimBoundsRect = new Rect();
+
NonAppWindowContainers(String name, WindowManagerService service) {
super(service);
mName = name;
@@ -3722,6 +3707,22 @@
String getName() {
return mName;
}
+
+ @Override
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getBounds(mTmpDimBoundsRect);
+
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
}
private class NonMagnifiableWindowContainers extends NonAppWindowContainers {
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 8269a3b..5bc739e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -92,14 +92,21 @@
onAnimationFinished();
return;
}
- mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
- try {
- mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
- mFinishedCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to start remote animation", e);
- onAnimationFinished();
- }
+
+ // Scale the timeout with the animator scale the controlling app is using.
+ mHandler.postDelayed(mTimeoutRunnable,
+ (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
+
+ final RemoteAnimationTarget[] animations = createAnimations();
+ mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+ try {
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
+ mFinishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to start remote animation", e);
+ onAnimationFinished();
+ }
+ });
}
private RemoteAnimationTarget[] createAnimations() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 68c8995..8d1a822 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -47,6 +47,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -126,7 +127,8 @@
boolean mOrientationChangeComplete = true;
boolean mWallpaperActionPending = false;
- private final ArrayList<Integer> mChangedStackList = new ArrayList();
+ private final ArrayList<TaskStack> mTmpStackList = new ArrayList();
+ private final ArrayList<Integer> mTmpStackIds = new ArrayList<>();
// State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
// instances will be replaced with an instance that writes a binary representation of all
@@ -333,7 +335,8 @@
/**
* Set new display override config and return array of ids of stacks that were changed during
- * update. If called for the default display, global configuration will also be updated.
+ * update. If called for the default display, global configuration will also be updated. Stacks
+ * that are marked for deferred removal are excluded from the returned array.
*/
int[] setDisplayOverrideConfigurationIfNeeded(Configuration newConfiguration, int displayId) {
final DisplayContent displayContent = getDisplayContent(displayId);
@@ -346,24 +349,42 @@
if (!configChanged) {
return null;
}
+
displayContent.onOverrideConfigurationChanged(newConfiguration);
+ mTmpStackList.clear();
if (displayId == DEFAULT_DISPLAY) {
// Override configuration of the default display duplicates global config. In this case
// we also want to update the global config.
- return setGlobalConfigurationIfNeeded(newConfiguration);
+ setGlobalConfigurationIfNeeded(newConfiguration, mTmpStackList);
} else {
- return updateStackBoundsAfterConfigChange(displayId);
+ updateStackBoundsAfterConfigChange(displayId, mTmpStackList);
}
+
+ mTmpStackIds.clear();
+ final int stackCount = mTmpStackList.size();
+
+ for (int i = 0; i < stackCount; ++i) {
+ final TaskStack stack = mTmpStackList.get(i);
+
+ // We only include stacks that are not marked for removal as they do not exist outside
+ // of WindowManager at this point.
+ if (!stack.mDeferRemoval) {
+ mTmpStackIds.add(stack.mStackId);
+ }
+ }
+
+ return mTmpStackIds.isEmpty() ? null : ArrayUtils.convertToIntArray(mTmpStackIds);
}
- private int[] setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
+ private void setGlobalConfigurationIfNeeded(Configuration newConfiguration,
+ List<TaskStack> changedStacks) {
final boolean configChanged = getConfiguration().diff(newConfiguration) != 0;
if (!configChanged) {
- return null;
+ return;
}
onConfigurationChanged(newConfiguration);
- return updateStackBoundsAfterConfigChange();
+ updateStackBoundsAfterConfigChange(changedStacks);
}
@Override
@@ -378,26 +399,18 @@
* Callback used to trigger bounds update after configuration change and get ids of stacks whose
* bounds were updated.
*/
- private int[] updateStackBoundsAfterConfigChange() {
- mChangedStackList.clear();
-
+ private void updateStackBoundsAfterConfigChange(List<TaskStack> changedStacks) {
final int numDisplays = mChildren.size();
for (int i = 0; i < numDisplays; ++i) {
final DisplayContent dc = mChildren.get(i);
- dc.updateStackBoundsAfterConfigChange(mChangedStackList);
+ dc.updateStackBoundsAfterConfigChange(changedStacks);
}
-
- return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
}
/** Same as {@link #updateStackBoundsAfterConfigChange()} but only for a specific display. */
- private int[] updateStackBoundsAfterConfigChange(int displayId) {
- mChangedStackList.clear();
-
+ private void updateStackBoundsAfterConfigChange(int displayId, List<TaskStack> changedStacks) {
final DisplayContent dc = getDisplayContent(displayId);
- dc.updateStackBoundsAfterConfigChange(mChangedStackList);
-
- return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
+ dc.updateStackBoundsAfterConfigChange(changedStacks);
}
private void prepareFreezingTaskBounds() {
@@ -599,6 +612,8 @@
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
+ mService.mAnimator.executeAfterPrepareSurfacesRunnables();
+
final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
// If we are ready to perform an app transition, check through all of the app tokens to be
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 04ae38e..f09a294 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -547,7 +547,7 @@
if (allowed) {
mAlertWindowNotification.post();
} else {
- mAlertWindowNotification.cancel();
+ mAlertWindowNotification.cancel(false /* deleteChannel */);
}
}
}
@@ -586,7 +586,7 @@
if (mAlertWindowNotification == null) {
return;
}
- mAlertWindowNotification.cancel();
+ mAlertWindowNotification.cancel(true /* deleteChannel */);
mAlertWindowNotification = null;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index cec13ab..b0d42f2 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -92,6 +92,7 @@
* executed and the corresponding transaction is closed and applied.
*/
private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
+ private boolean mInExecuteAfterPrepareSurfacesRunnables;
WindowAnimator(final WindowManagerService service) {
mService = service;
@@ -438,7 +439,13 @@
scheduleAnimation();
}
- private void executeAfterPrepareSurfacesRunnables() {
+ void executeAfterPrepareSurfacesRunnables() {
+
+ // Don't even think about to start recursing!
+ if (mInExecuteAfterPrepareSurfacesRunnables) {
+ return;
+ }
+ mInExecuteAfterPrepareSurfacesRunnables = true;
// Traverse in order they were added.
final int size = mAfterPrepareSurfacesRunnables.size();
@@ -446,5 +453,6 @@
mAfterPrepareSurfacesRunnables.get(i).run();
}
mAfterPrepareSurfacesRunnables.clear();
+ mInExecuteAfterPrepareSurfacesRunnables = false;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 066e4e6..d565a6a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,8 +24,6 @@
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_USER_HANDLE;
@@ -125,7 +123,6 @@
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IAssistDataReceiver;
-import android.app.WindowConfiguration;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -2466,6 +2463,7 @@
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
+
return mRoot.setDisplayOverrideConfigurationIfNeeded(overrideConfig, displayId);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 36e3612..a9f2e03 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1758,7 +1758,7 @@
* listeners and optionally animate it. Simply checking a change of position is not enough,
* because being move due to dock divider is not a trigger for animation.
*/
- void handleWindowMovedIfNeeded(Transaction t) {
+ void handleWindowMovedIfNeeded() {
if (!hasMoved()) {
return;
}
@@ -1776,7 +1776,7 @@
&& !isDragResizing() && !adjustedForMinimizedDockOrIme
&& getWindowConfiguration().hasMovementAnimations()
&& !mWinAnimator.mLastHidden) {
- startMoveAnimation(t, left, top);
+ startMoveAnimation(left, top);
}
//TODO (multidisplay): Accessibility supported only for the default display.
@@ -4360,7 +4360,7 @@
commitPendingTransaction();
}
- private void startMoveAnimation(Transaction t, int left, int top) {
+ private void startMoveAnimation(int left, int top) {
if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
final Point oldPosition = new Point();
final Point newPosition = new Point();
@@ -4369,7 +4369,7 @@
final AnimationAdapter adapter = new LocalAnimationAdapter(
new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
mService.mSurfaceAnimationRunner);
- startAnimation(t, adapter);
+ startAnimation(getPendingTransaction(), adapter);
}
private void startAnimation(Transaction t, AnimationAdapter adapter) {
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 1cbb399..d2ae22b 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -90,6 +90,7 @@
@Mock private IStatusBarService mStatusBarService;
@Mock private WindowManagerService mWindowManager;
@Mock private LockPatternUtils mLockPatternUtils;
+ @Mock private LockTaskNotify mLockTaskNotify;
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
@Mock private TelecomManager mTelecomManager;
@Mock private RecentTasks mRecentTasks;
@@ -122,6 +123,7 @@
mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
mLockTaskController.mTelecomManager = mTelecomManager;
mLockTaskController.mLockPatternUtils = mLockPatternUtils;
+ mLockTaskController.mLockTaskNotify = mLockTaskNotify;
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
@@ -206,7 +208,7 @@
// THEN lock task mode should be started
verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
// THEN screen pinning toast should be shown
- verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
+ verify(mLockTaskNotify).showPinningStartToast();
}
@Test
@@ -375,7 +377,7 @@
// THEN the keyguard should be shown
verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
// THEN screen pinning toast should be shown
- verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
+ verify(mLockTaskNotify).showPinningExitToast();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index c3714c8..f7516b2 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -63,6 +63,11 @@
return mBatteryLevel;
}
+ @Override
+ int injectBatteryPercent() {
+ return mBatteryLevel / 10;
+ }
+
void assertDumpable() {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
dump(new PrintWriter(out), ""); // Just make sure it won't crash.
@@ -102,7 +107,7 @@
target.assertDumpable();
target.advanceClock(1);
- target.drainBattery(2);
+ target.drainBattery(200);
target.transitionState(
BatterySaverState.OFF,
@@ -110,7 +115,7 @@
DozeState.NOT_DOZING);
target.advanceClock(4);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -118,7 +123,7 @@
DozeState.NOT_DOZING);
target.advanceClock(2);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.OFF,
@@ -126,7 +131,7 @@
DozeState.NOT_DOZING);
target.advanceClock(4);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -134,7 +139,7 @@
DozeState.NOT_DOZING);
target.advanceClock(2);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.OFF,
@@ -142,7 +147,7 @@
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -150,7 +155,7 @@
DozeState.LIGHT);
target.advanceClock(5);
- target.drainBattery(1);
+ target.drainBattery(100);
target.transitionState(
BatterySaverState.OFF,
@@ -158,7 +163,7 @@
DozeState.DEEP);
target.advanceClock(1);
- target.drainBattery(2);
+ target.drainBattery(200);
target.transitionState(
BatterySaverState.ON,
@@ -166,7 +171,7 @@
DozeState.NOT_DOZING);
target.advanceClock(1);
- target.drainBattery(3);
+ target.drainBattery(300);
target.transitionState(
BatterySaverState.OFF,
@@ -174,7 +179,7 @@
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(5);
+ target.drainBattery(500);
target.transitionState(
BatterySaverState.ON,
@@ -182,12 +187,12 @@
DozeState.NOT_DOZING);
target.advanceClock(3);
- target.drainBattery(5);
+ target.drainBattery(500);
target.startCharging();
target.advanceClock(5);
- target.drainBattery(10);
+ target.drainBattery(1000);
target.transitionState(
BatterySaverState.ON,
@@ -195,25 +200,25 @@
DozeState.NOT_DOZING);
target.advanceClock(5);
- target.drainBattery(1);
+ target.drainBattery(100);
target.startCharging();
target.assertDumpable();
assertEquals(
- "BS=0,I=0,D=0:{4m,10,150.00}\n" +
- "BS=1,I=0,D=0:{0m,0,0.00}\n" +
- "BS=0,I=1,D=0:{14m,8,34.29}\n" +
- "BS=1,I=1,D=0:{9m,9,60.00}\n" +
- "BS=0,I=0,D=1:{5m,1,12.00}\n" +
- "BS=1,I=0,D=1:{0m,0,0.00}\n" +
- "BS=0,I=1,D=1:{0m,0,0.00}\n" +
- "BS=1,I=1,D=1:{0m,0,0.00}\n" +
- "BS=0,I=0,D=2:{1m,2,120.00}\n" +
- "BS=1,I=0,D=2:{0m,0,0.00}\n" +
- "BS=0,I=1,D=2:{0m,0,0.00}\n" +
- "BS=1,I=1,D=2:{0m,0,0.00}",
+ "BS=0,I=0,D=0:{4m,1000,15000.00uA/H,1500.00%}\n" +
+ "BS=1,I=0,D=0:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=0:{14m,800,3428.57uA/H,342.86%}\n" +
+ "BS=1,I=1,D=0:{9m,900,6000.00uA/H,600.00%}\n" +
+ "BS=0,I=0,D=1:{5m,100,1200.00uA/H,120.00%}\n" +
+ "BS=1,I=0,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=1,I=1,D=1:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=0,D=2:{1m,200,12000.00uA/H,1200.00%}\n" +
+ "BS=1,I=0,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=0,I=1,D=2:{0m,0,0.00uA/H,0.00%}\n" +
+ "BS=1,I=1,D=2:{0m,0,0.00uA/H,0.00%}",
target.toDebugString());
}
@@ -245,6 +250,7 @@
DozeState.NOT_DOZING);
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "01", 200);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60);
target.advanceClock(1);
@@ -277,15 +283,17 @@
DozeState.NOT_DOZING);
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "00", 200 * 3);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3);
target.advanceClock(10);
- target.drainBattery(10_000);
+ target.drainBattery(10000);
reset(mMetricsLogger);
target.startCharging();
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "11", 1000);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10);
target.advanceClock(1);
@@ -305,6 +313,7 @@
target.startCharging();
assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2);
+ assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "10", 200);
assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index f860195..26a7313 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -19,12 +19,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.graphics.Point;
import android.graphics.Rect;
-import android.platform.test.annotations.Postsubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -137,6 +137,32 @@
}
@Test
+ public void testTimeout_scaled() throws Exception {
+ sWm.setAnimationScale(2, 5.0f);
+ try{
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150));
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+
+ mClock.fastForward(2500);
+ mHandler.timeAdvance();
+
+ verify(mMockRunner, never()).onAnimationCancelled();
+
+ mClock.fastForward(10000);
+ mHandler.timeAdvance();
+
+ verify(mMockRunner).onAnimationCancelled();
+ verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+ } finally {
+ sWm.setAnimationScale(2, 1.0f);
+ }
+
+ }
+
+ @Test
public void testZeroAnimations() throws Exception {
mController.goodToGo();
verifyZeroInteractions(mMockRunner);
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
new file mode 100644
index 0000000..51b019a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -0,0 +1,50 @@
+package com.android.server.wm;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the {@link RootWindowContainer} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.wm.RootWindowContainerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RootWindowContainerTests extends WindowTestsBase {
+ @Test
+ public void testSetDisplayOverrideConfigurationIfNeeded() throws Exception {
+ // Add first stack we expect to be updated with configuration change.
+ final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+ stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5));
+
+ // Add second task that will be set for deferred removal that should not be returned
+ // with the configuration change.
+ final TaskStack deferredDeletedStack = createTaskStackOnDisplay(mDisplayContent);
+ deferredDeletedStack.getOverrideConfiguration().windowConfiguration.setBounds(
+ new Rect(0, 0, 5, 5));
+ deferredDeletedStack.mDeferRemoval = true;
+
+ final Configuration override = new Configuration(
+ mDisplayContent.getOverrideConfiguration());
+ override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
+
+ // Set display override.
+ final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
+ mDisplayContent.getDisplayId());
+
+ // Ensure only first stack is returned.
+ assertTrue(results.length == 1);
+ assertTrue(results[0] == stack.mStackId);
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
index 7c14d08..b784c60 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceFullAccessListTest.java
@@ -67,6 +67,15 @@
}
@Test
+ public void testRemoveAccess() {
+ mAccessList.grantFullAccess("pkg", 0);
+ assertTrue(mAccessList.hasFullAccess("pkg", 0));
+
+ mAccessList.removeGrant("pkg", 0);
+ assertFalse(mAccessList.hasFullAccess("pkg", 0));
+ }
+
+ @Test
public void testSerialization() throws XmlPullParserException, IOException {
mAccessList.grantFullAccess("pkg", 0);
mAccessList.grantFullAccess("pkg1", 0);
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index dc5ba0c..fe63aa1 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -7,22 +7,21 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
+import android.content.pm.PackageManager;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.WifiRttManager;
import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.util.List;
+
/** @hide */
@SystemApi
@SystemService(Context.WIFI_RTT_SERVICE)
@@ -175,7 +174,8 @@
@Deprecated
@SuppressLint("Doclava125")
public Capabilities getCapabilities() {
- return new Capabilities();
+ throw new UnsupportedOperationException(
+ "getCapabilities is not supported in the adaptation layer");
}
/**
@@ -316,16 +316,7 @@
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
public RttCapabilities getRttCapabilities() {
- synchronized (mCapabilitiesLock) {
- if (mRttCapabilities == null) {
- try {
- mRttCapabilities = mService.getRttCapabilities();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return mRttCapabilities;
- }
+ return mRttCapabilities;
}
/** specifies parameters for RTT request */
@@ -972,69 +963,6 @@
}
}
- private boolean rttParamSanity(RttParams params, int index) {
- if (mRttCapabilities == null) {
- if(getRttCapabilities() == null) {
- Log.e(TAG, "Can not get RTT capabilities");
- throw new IllegalStateException("RTT chip is not working");
- }
- }
-
- if (params.deviceType != RTT_PEER_TYPE_AP) {
- return false;
- } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType !=
- RTT_TYPE_TWO_SIDED) {
- Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType);
- return false;
- } else if (params.requestType == RTT_TYPE_ONE_SIDED &&
- !mRttCapabilities.oneSidedRttSupported) {
- Log.e(TAG, "Request " + index + ": One side RTT is not supported");
- return false;
- } else if (params.requestType == RTT_TYPE_TWO_SIDED &&
- !mRttCapabilities.twoSided11McRttSupported) {
- Log.e(TAG, "Request " + index + ": two side RTT is not supported");
- return false;
- } else if(params.bssid == null || params.bssid.isEmpty()) {
- Log.e(TAG,"No BSSID in params");
- return false;
- } else if ( params.numberBurst != 0 ) {
- Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst);
- return false;
- } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) {
- Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " +
- params.numSamplesPerBurst);
- return false;
- } else if (params.numRetriesPerMeasurementFrame < 0 ||
- params.numRetriesPerMeasurementFrame > 3) {
- Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" +
- params.numRetriesPerMeasurementFrame);
- return false;
- } else if(params.numRetriesPerFTMR < 0 ||
- params.numRetriesPerFTMR > 3) {
- Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" +
- params.numRetriesPerFTMR);
- return false;
- } else if (params.LCIRequest && !mRttCapabilities.lciSupported) {
- Log.e(TAG, "Request " + index + ": LCI is not supported");
- return false;
- } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) {
- Log.e(TAG, "Request " + index + ": LCR is not supported");
- return false;
- } else if (params.burstTimeout < 1 ||
- (params.burstTimeout > 11 && params.burstTimeout != 15)){
- Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout);
- return false;
- } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble);
- return false;
- } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) {
- Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth);
- return false;
- }
-
- return true;
- }
-
/**
* Request to start an RTT ranging
*
@@ -1045,24 +973,72 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void startRanging(RttParams[] params, RttListener listener) {
- int index = 0;
- for(RttParams rttParam : params) {
- if (!rttParamSanity(rttParam, index)) {
- throw new IllegalArgumentException("RTT Request Parameter Illegal");
- }
- index++;
- }
- validateChannel();
- ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
Log.i(TAG, "Send RTT request to RTT Service");
- mAsyncChannel.sendMessage(CMD_OP_START_RANGING,
- 0, putListener(listener), parcelableParams);
+
+ if (!mNewService.isAvailable()) {
+ listener.onFailure(REASON_NOT_AVAILABLE, "");
+ return;
+ }
+
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ for (RttParams rttParams : params) {
+ if (rttParams.deviceType != RTT_PEER_TYPE_AP) {
+ listener.onFailure(REASON_INVALID_REQUEST, "Only AP peers are supported");
+ return;
+ }
+
+ ScanResult reconstructed = new ScanResult();
+ reconstructed.BSSID = rttParams.bssid;
+ if (rttParams.requestType == RTT_TYPE_TWO_SIDED) {
+ reconstructed.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
+ }
+ reconstructed.channelWidth = rttParams.channelWidth;
+ reconstructed.frequency = rttParams.frequency;
+ reconstructed.centerFreq0 = rttParams.centerFreq0;
+ reconstructed.centerFreq1 = rttParams.centerFreq1;
+ builder.addResponder(
+ android.net.wifi.rtt.ResponderConfig.fromScanResult(reconstructed));
+ }
+ try {
+ mNewService.startRanging(builder.build(), new RangingResultCallback() {
+ @Override
+ public void onRangingFailure(int code) {
+ int localCode = REASON_UNSPECIFIED;
+ if (code == STATUS_CODE_FAIL_RTT_NOT_AVAILABLE) {
+ localCode = REASON_NOT_AVAILABLE;
+ }
+ listener.onFailure(localCode, "");
+ }
+
+ @Override
+ public void onRangingResults(List<RangingResult> results) {
+ RttResult[] legacyResults = new RttResult[results.size()];
+ int i = 0;
+ for (RangingResult result : results) {
+ legacyResults[i] = new RttResult();
+ legacyResults[i].status = result.getStatus();
+ legacyResults[i].bssid = result.getMacAddress().toString();
+ legacyResults[i].distance = result.getDistanceMm() / 10;
+ legacyResults[i].distanceStandardDeviation =
+ result.getDistanceStdDevMm() / 10;
+ legacyResults[i].rssi = result.getRssi();
+ legacyResults[i].ts = result.getRangingTimestampUs();
+ }
+ listener.onSuccess(legacyResults);
+ }
+ }, null);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "startRanging: invalid arguments - " + e);
+ listener.onFailure(REASON_INVALID_REQUEST, e.getMessage());
+ } catch (SecurityException e) {
+ Log.e(TAG, "startRanging: security exception - " + e);
+ listener.onFailure(REASON_PERMISSION_DENIED, e.getMessage());
+ }
}
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void stopRanging(RttListener listener) {
- validateChannel();
- mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener));
+ Log.e(TAG, "stopRanging: unsupported operation - nop");
}
/**
@@ -1095,12 +1071,8 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void enableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = putListenerIfAbsent(callback);
- mAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "enableResponder is not supported in the adaptation layer");
}
/**
@@ -1115,16 +1087,8 @@
*/
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void disableResponder(ResponderCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- validateChannel();
- int key = removeListener(callback);
- if (key == INVALID_KEY) {
- Log.e(TAG, "responder not enabled yet");
- return;
- }
- mAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key);
+ throw new UnsupportedOperationException(
+ "disableResponder is not supported in the adaptation layer");
}
/**
@@ -1238,17 +1202,9 @@
/** @hide */
public static final int CMD_OP_REG_BINDER = BASE + 9;
- private static final int INVALID_KEY = 0;
-
private final Context mContext;
- private final IRttManager mService;
- private final SparseArray mListenerMap = new SparseArray();
- private final Object mListenerMapLock = new Object();
- private final Object mCapabilitiesLock = new Object();
-
+ private final WifiRttManager mNewService;
private RttCapabilities mRttCapabilities;
- private int mListenerKey = 1;
- private AsyncChannel mAsyncChannel;
/**
* Create a new WifiScanner instance.
@@ -1263,170 +1219,20 @@
*/
public RttManager(Context context, IRttManager service, Looper looper) {
mContext = context;
- mService = service;
- Messenger messenger = null;
- int[] key = new int[1];
- try {
- Log.d(TAG, "Get the messenger from " + mService);
- messenger = mService.getMessenger(new Binder(), key);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mNewService = (WifiRttManager) mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
- if (messenger == null) {
- throw new IllegalStateException("getMessenger() returned null! This is invalid.");
- }
+ boolean rttSupported = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT);
- mAsyncChannel = new AsyncChannel();
-
- Handler handler = new ServiceHandler(looper);
- mAsyncChannel.connectSync(mContext, handler, messenger);
- // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message
- // synchronously, which causes RttService to receive the wrong replyTo value.
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION,
- new RttClient(context.getPackageName()));
- mAsyncChannel.sendMessage(CMD_OP_REG_BINDER, key[0]);
+ mRttCapabilities = new RttCapabilities();
+ mRttCapabilities.oneSidedRttSupported = rttSupported;
+ mRttCapabilities.twoSided11McRttSupported = rttSupported;
+ mRttCapabilities.lciSupported = false;
+ mRttCapabilities.lcrSupported = false;
+ mRttCapabilities.preambleSupported = PREAMBLE_HT | PREAMBLE_VHT;
+ mRttCapabilities.bwSupported = RTT_BW_40_SUPPORT | RTT_BW_80_SUPPORT;
+ mRttCapabilities.responderSupported = false;
+ mRttCapabilities.secureRttSupported = false;
}
-
- private void validateChannel() {
- if (mAsyncChannel == null) throw new IllegalStateException(
- "No permission to access and change wifi or a bad initialization");
- }
-
- private int putListener(Object listener) {
- if (listener == null) return INVALID_KEY;
- int key;
- synchronized (mListenerMapLock) {
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- }
- return key;
- }
-
- // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener.
- private int putListenerIfAbsent(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int key = getListenerKey(listener);
- if (key != INVALID_KEY) {
- return key;
- }
- do {
- key = mListenerKey++;
- } while (key == INVALID_KEY);
- mListenerMap.put(key, listener);
- return key;
- }
-
- }
-
- private Object getListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- return listener;
- }
- }
-
- private int getListenerKey(Object listener) {
- if (listener == null) return INVALID_KEY;
- synchronized (mListenerMapLock) {
- int index = mListenerMap.indexOfValue(listener);
- if (index == -1) {
- return INVALID_KEY;
- } else {
- return mListenerMap.keyAt(index);
- }
- }
- }
-
- private Object removeListener(int key) {
- if (key == INVALID_KEY) return null;
- synchronized (mListenerMapLock) {
- Object listener = mListenerMap.get(key);
- mListenerMap.remove(key);
- return listener;
- }
- }
-
- private int removeListener(Object listener) {
- int key = getListenerKey(listener);
- if (key == INVALID_KEY) return key;
- synchronized (mListenerMapLock) {
- mListenerMap.remove(key);
- return key;
- }
- }
-
- private class ServiceHandler extends Handler {
- ServiceHandler(Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- Log.i(TAG, "RTT manager get message: " + msg.what);
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
- return;
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- Log.e(TAG, "Channel connection lost");
- // This will cause all further async API calls on the WifiManager
- // to fail and throw an exception
- mAsyncChannel = null;
- getLooper().quit();
- return;
- }
-
- Object listener = getListener(msg.arg2);
- if (listener == null) {
- Log.e(TAG, "invalid listener key = " + msg.arg2 );
- return;
- } else {
- Log.i(TAG, "listener key = " + msg.arg2);
- }
-
- switch (msg.what) {
- /* ActionListeners grouped together */
- case CMD_OP_SUCCEEDED :
- reportSuccess(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_FAILED :
- reportFailure(listener, msg);
- removeListener(msg.arg2);
- break;
- case CMD_OP_ABORTED :
- ((RttListener) listener).onAborted();
- removeListener(msg.arg2);
- break;
- case CMD_OP_ENALBE_RESPONDER_SUCCEEDED:
- ResponderConfig config = (ResponderConfig) msg.obj;
- ((ResponderCallback) (listener)).onResponderEnabled(config);
- break;
- case CMD_OP_ENALBE_RESPONDER_FAILED:
- ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1);
- removeListener(msg.arg2);
- break;
- default:
- if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
- return;
- }
- }
-
- void reportSuccess(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj;
- ((RttListener) listener).onSuccess(parcelableResults.mResults);
- }
-
- void reportFailure(Object listener, Message msg) {
- RttListener rttListener = (RttListener) listener;
- Bundle bundle = (Bundle) msg.obj;
- ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY));
- }
- }
-
}