diff options
96 files changed, 1892 insertions, 928 deletions
diff --git a/Android.mk b/Android.mk index c4f222ea7174..817aa80fde00 100644 --- a/Android.mk +++ b/Android.mk @@ -137,6 +137,7 @@ LOCAL_SRC_FILES += \ ../../system/bt/binder/android/bluetooth/IBluetoothPbap.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothPbapClient.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothSap.aidl \ + ../../system/bt/binder/android/bluetooth/IBluetoothSocketManager.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothStateChangeCallback.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothHeadsetClient.aidl \ ../../system/bt/binder/android/bluetooth/IBluetoothHidDevice.aidl \ diff --git a/api/current.txt b/api/current.txt index 823cc3f42481..4e3e1962a377 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6847,7 +6847,8 @@ package android.app.job { method public static final long getMinFlexMillis(); method public long getMinLatencyMillis(); method public static final long getMinPeriodMillis(); - method public int getNetworkType(); + method public deprecated int getNetworkType(); + method public android.net.NetworkRequest getRequiredNetwork(); method public android.content.ComponentName getService(); method public android.os.Bundle getTransientExtras(); method public long getTriggerContentMaxDelay(); @@ -6867,7 +6868,8 @@ package android.app.job { field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff field public static final int NETWORK_TYPE_ANY = 1; // 0x1 - field public static final int NETWORK_TYPE_METERED = 4; // 0x4 + field public static final int NETWORK_TYPE_CELLULAR = 4; // 0x4 + field public static final deprecated int NETWORK_TYPE_METERED = 4; // 0x4 field public static final int NETWORK_TYPE_NONE = 0; // 0x0 field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3 field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2 @@ -6886,6 +6888,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder setPeriodic(long); method public android.app.job.JobInfo.Builder setPeriodic(long, long); method public android.app.job.JobInfo.Builder setPersisted(boolean); + method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest); method public android.app.job.JobInfo.Builder setRequiredNetworkType(int); method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean); method public android.app.job.JobInfo.Builder setRequiresCharging(boolean); @@ -18273,11 +18276,13 @@ package android.icu.text { method public synchronized void applyLocalizedPattern(java.lang.String); method public synchronized void applyPattern(java.lang.String); method public synchronized boolean areSignificantDigitsUsed(); + method public synchronized boolean equals(java.lang.Object); method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition); + method public synchronized android.icu.util.Currency getCurrency(); method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo(); method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage(); method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols(); @@ -18285,8 +18290,12 @@ package android.icu.text { method public synchronized int getGroupingSize(); method public synchronized java.math.MathContext getMathContext(); method public synchronized android.icu.math.MathContext getMathContextICU(); + method public synchronized int getMaximumFractionDigits(); + method public synchronized int getMaximumIntegerDigits(); method public synchronized int getMaximumSignificantDigits(); method public synchronized byte getMinimumExponentDigits(); + method public synchronized int getMinimumFractionDigits(); + method public synchronized int getMinimumIntegerDigits(); method public synchronized int getMinimumSignificantDigits(); method public synchronized int getMultiplier(); method public synchronized java.lang.String getNegativePrefix(); @@ -18297,13 +18306,19 @@ package android.icu.text { method public synchronized java.lang.String getPositivePrefix(); method public synchronized java.lang.String getPositiveSuffix(); method public synchronized java.math.BigDecimal getRoundingIncrement(); + method public synchronized int getRoundingMode(); method public synchronized int getSecondaryGroupingSize(); + method public synchronized int hashCode(); method public synchronized boolean isDecimalPatternMatchRequired(); method public synchronized boolean isDecimalSeparatorAlwaysShown(); method public synchronized boolean isExponentSignAlwaysShown(); + method public synchronized boolean isGroupingUsed(); method public synchronized boolean isParseBigDecimal(); + method public synchronized boolean isParseIntegerOnly(); + method public synchronized boolean isParseStrict(); method public synchronized boolean isScientificNotation(); method public java.lang.Number parse(java.lang.String, java.text.ParsePosition); + method public synchronized void setCurrency(android.icu.util.Currency); method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo); method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage); method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols); @@ -18312,10 +18327,15 @@ package android.icu.text { method public synchronized void setExponentSignAlwaysShown(boolean); method public synchronized void setFormatWidth(int); method public synchronized void setGroupingSize(int); + method public synchronized void setGroupingUsed(boolean); method public synchronized void setMathContext(java.math.MathContext); method public synchronized void setMathContextICU(android.icu.math.MathContext); + method public synchronized void setMaximumFractionDigits(int); + method public synchronized void setMaximumIntegerDigits(int); method public synchronized void setMaximumSignificantDigits(int); method public synchronized void setMinimumExponentDigits(byte); + method public synchronized void setMinimumFractionDigits(int); + method public synchronized void setMinimumIntegerDigits(int); method public synchronized void setMinimumSignificantDigits(int); method public synchronized void setMultiplier(int); method public synchronized void setNegativePrefix(java.lang.String); @@ -18323,12 +18343,15 @@ package android.icu.text { method public synchronized void setPadCharacter(char); method public synchronized void setPadPosition(int); method public synchronized void setParseBigDecimal(boolean); + method public synchronized void setParseIntegerOnly(boolean); method public deprecated void setParseMaxDigits(int); + method public synchronized void setParseStrict(boolean); method public synchronized void setPositivePrefix(java.lang.String); method public synchronized void setPositiveSuffix(java.lang.String); method public synchronized void setRoundingIncrement(java.math.BigDecimal); method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal); method public synchronized void setRoundingIncrement(double); + method public synchronized void setRoundingMode(int); method public synchronized void setScientificNotation(boolean); method public synchronized void setSecondaryGroupingSize(int); method public synchronized void setSignificantDigitsUsed(boolean); @@ -31875,10 +31898,12 @@ package android.os { public final class SystemClock { method public static long currentThreadTimeMillis(); method public static long elapsedRealtime(); + method public static java.time.Clock elapsedRealtimeClock(); method public static long elapsedRealtimeNanos(); method public static boolean setCurrentTimeMillis(long); method public static void sleep(long); method public static long uptimeMillis(); + method public static java.time.Clock uptimeMillisClock(); } public class TestLooperManager { diff --git a/api/system-current.txt b/api/system-current.txt index 76a12c5c2bee..a77bfef9ea40 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -7290,7 +7290,8 @@ package android.app.job { method public static final long getMinFlexMillis(); method public long getMinLatencyMillis(); method public static final long getMinPeriodMillis(); - method public int getNetworkType(); + method public deprecated int getNetworkType(); + method public android.net.NetworkRequest getRequiredNetwork(); method public android.content.ComponentName getService(); method public android.os.Bundle getTransientExtras(); method public long getTriggerContentMaxDelay(); @@ -7310,7 +7311,8 @@ package android.app.job { field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff field public static final int NETWORK_TYPE_ANY = 1; // 0x1 - field public static final int NETWORK_TYPE_METERED = 4; // 0x4 + field public static final int NETWORK_TYPE_CELLULAR = 4; // 0x4 + field public static final deprecated int NETWORK_TYPE_METERED = 4; // 0x4 field public static final int NETWORK_TYPE_NONE = 0; // 0x0 field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3 field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2 @@ -7329,6 +7331,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder setPeriodic(long); method public android.app.job.JobInfo.Builder setPeriodic(long, long); method public android.app.job.JobInfo.Builder setPersisted(boolean); + method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest); method public android.app.job.JobInfo.Builder setRequiredNetworkType(int); method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean); method public android.app.job.JobInfo.Builder setRequiresCharging(boolean); @@ -19832,11 +19835,13 @@ package android.icu.text { method public synchronized void applyLocalizedPattern(java.lang.String); method public synchronized void applyPattern(java.lang.String); method public synchronized boolean areSignificantDigitsUsed(); + method public synchronized boolean equals(java.lang.Object); method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition); + method public synchronized android.icu.util.Currency getCurrency(); method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo(); method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage(); method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols(); @@ -19844,8 +19849,12 @@ package android.icu.text { method public synchronized int getGroupingSize(); method public synchronized java.math.MathContext getMathContext(); method public synchronized android.icu.math.MathContext getMathContextICU(); + method public synchronized int getMaximumFractionDigits(); + method public synchronized int getMaximumIntegerDigits(); method public synchronized int getMaximumSignificantDigits(); method public synchronized byte getMinimumExponentDigits(); + method public synchronized int getMinimumFractionDigits(); + method public synchronized int getMinimumIntegerDigits(); method public synchronized int getMinimumSignificantDigits(); method public synchronized int getMultiplier(); method public synchronized java.lang.String getNegativePrefix(); @@ -19856,13 +19865,19 @@ package android.icu.text { method public synchronized java.lang.String getPositivePrefix(); method public synchronized java.lang.String getPositiveSuffix(); method public synchronized java.math.BigDecimal getRoundingIncrement(); + method public synchronized int getRoundingMode(); method public synchronized int getSecondaryGroupingSize(); + method public synchronized int hashCode(); method public synchronized boolean isDecimalPatternMatchRequired(); method public synchronized boolean isDecimalSeparatorAlwaysShown(); method public synchronized boolean isExponentSignAlwaysShown(); + method public synchronized boolean isGroupingUsed(); method public synchronized boolean isParseBigDecimal(); + method public synchronized boolean isParseIntegerOnly(); + method public synchronized boolean isParseStrict(); method public synchronized boolean isScientificNotation(); method public java.lang.Number parse(java.lang.String, java.text.ParsePosition); + method public synchronized void setCurrency(android.icu.util.Currency); method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo); method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage); method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols); @@ -19871,10 +19886,15 @@ package android.icu.text { method public synchronized void setExponentSignAlwaysShown(boolean); method public synchronized void setFormatWidth(int); method public synchronized void setGroupingSize(int); + method public synchronized void setGroupingUsed(boolean); method public synchronized void setMathContext(java.math.MathContext); method public synchronized void setMathContextICU(android.icu.math.MathContext); + method public synchronized void setMaximumFractionDigits(int); + method public synchronized void setMaximumIntegerDigits(int); method public synchronized void setMaximumSignificantDigits(int); method public synchronized void setMinimumExponentDigits(byte); + method public synchronized void setMinimumFractionDigits(int); + method public synchronized void setMinimumIntegerDigits(int); method public synchronized void setMinimumSignificantDigits(int); method public synchronized void setMultiplier(int); method public synchronized void setNegativePrefix(java.lang.String); @@ -19882,12 +19902,15 @@ package android.icu.text { method public synchronized void setPadCharacter(char); method public synchronized void setPadPosition(int); method public synchronized void setParseBigDecimal(boolean); + method public synchronized void setParseIntegerOnly(boolean); method public deprecated void setParseMaxDigits(int); + method public synchronized void setParseStrict(boolean); method public synchronized void setPositivePrefix(java.lang.String); method public synchronized void setPositiveSuffix(java.lang.String); method public synchronized void setRoundingIncrement(java.math.BigDecimal); method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal); method public synchronized void setRoundingIncrement(double); + method public synchronized void setRoundingMode(int); method public synchronized void setScientificNotation(boolean); method public synchronized void setSecondaryGroupingSize(int); method public synchronized void setSignificantDigitsUsed(boolean); @@ -34664,10 +34687,12 @@ package android.os { public final class SystemClock { method public static long currentThreadTimeMillis(); method public static long elapsedRealtime(); + method public static java.time.Clock elapsedRealtimeClock(); method public static long elapsedRealtimeNanos(); method public static boolean setCurrentTimeMillis(long); method public static void sleep(long); method public static long uptimeMillis(); + method public static java.time.Clock uptimeMillisClock(); } public class TestLooperManager { diff --git a/api/test-current.txt b/api/test-current.txt index 0cb6055f53ed..d73b3538fc38 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6921,7 +6921,8 @@ package android.app.job { method public static final long getMinFlexMillis(); method public long getMinLatencyMillis(); method public static final long getMinPeriodMillis(); - method public int getNetworkType(); + method public deprecated int getNetworkType(); + method public android.net.NetworkRequest getRequiredNetwork(); method public android.content.ComponentName getService(); method public android.os.Bundle getTransientExtras(); method public long getTriggerContentMaxDelay(); @@ -6941,7 +6942,8 @@ package android.app.job { field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff field public static final int NETWORK_TYPE_ANY = 1; // 0x1 - field public static final int NETWORK_TYPE_METERED = 4; // 0x4 + field public static final int NETWORK_TYPE_CELLULAR = 4; // 0x4 + field public static final deprecated int NETWORK_TYPE_METERED = 4; // 0x4 field public static final int NETWORK_TYPE_NONE = 0; // 0x0 field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3 field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2 @@ -6960,6 +6962,7 @@ package android.app.job { method public android.app.job.JobInfo.Builder setPeriodic(long); method public android.app.job.JobInfo.Builder setPeriodic(long, long); method public android.app.job.JobInfo.Builder setPersisted(boolean); + method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest); method public android.app.job.JobInfo.Builder setRequiredNetworkType(int); method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean); method public android.app.job.JobInfo.Builder setRequiresCharging(boolean); @@ -18419,11 +18422,13 @@ package android.icu.text { method public synchronized void applyLocalizedPattern(java.lang.String); method public synchronized void applyPattern(java.lang.String); method public synchronized boolean areSignificantDigitsUsed(); + method public synchronized boolean equals(java.lang.Object); method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition); method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition); + method public synchronized android.icu.util.Currency getCurrency(); method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo(); method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage(); method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols(); @@ -18431,8 +18436,12 @@ package android.icu.text { method public synchronized int getGroupingSize(); method public synchronized java.math.MathContext getMathContext(); method public synchronized android.icu.math.MathContext getMathContextICU(); + method public synchronized int getMaximumFractionDigits(); + method public synchronized int getMaximumIntegerDigits(); method public synchronized int getMaximumSignificantDigits(); method public synchronized byte getMinimumExponentDigits(); + method public synchronized int getMinimumFractionDigits(); + method public synchronized int getMinimumIntegerDigits(); method public synchronized int getMinimumSignificantDigits(); method public synchronized int getMultiplier(); method public synchronized java.lang.String getNegativePrefix(); @@ -18443,13 +18452,19 @@ package android.icu.text { method public synchronized java.lang.String getPositivePrefix(); method public synchronized java.lang.String getPositiveSuffix(); method public synchronized java.math.BigDecimal getRoundingIncrement(); + method public synchronized int getRoundingMode(); method public synchronized int getSecondaryGroupingSize(); + method public synchronized int hashCode(); method public synchronized boolean isDecimalPatternMatchRequired(); method public synchronized boolean isDecimalSeparatorAlwaysShown(); method public synchronized boolean isExponentSignAlwaysShown(); + method public synchronized boolean isGroupingUsed(); method public synchronized boolean isParseBigDecimal(); + method public synchronized boolean isParseIntegerOnly(); + method public synchronized boolean isParseStrict(); method public synchronized boolean isScientificNotation(); method public java.lang.Number parse(java.lang.String, java.text.ParsePosition); + method public synchronized void setCurrency(android.icu.util.Currency); method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo); method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage); method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols); @@ -18458,10 +18473,15 @@ package android.icu.text { method public synchronized void setExponentSignAlwaysShown(boolean); method public synchronized void setFormatWidth(int); method public synchronized void setGroupingSize(int); + method public synchronized void setGroupingUsed(boolean); method public synchronized void setMathContext(java.math.MathContext); method public synchronized void setMathContextICU(android.icu.math.MathContext); + method public synchronized void setMaximumFractionDigits(int); + method public synchronized void setMaximumIntegerDigits(int); method public synchronized void setMaximumSignificantDigits(int); method public synchronized void setMinimumExponentDigits(byte); + method public synchronized void setMinimumFractionDigits(int); + method public synchronized void setMinimumIntegerDigits(int); method public synchronized void setMinimumSignificantDigits(int); method public synchronized void setMultiplier(int); method public synchronized void setNegativePrefix(java.lang.String); @@ -18469,12 +18489,15 @@ package android.icu.text { method public synchronized void setPadCharacter(char); method public synchronized void setPadPosition(int); method public synchronized void setParseBigDecimal(boolean); + method public synchronized void setParseIntegerOnly(boolean); method public deprecated void setParseMaxDigits(int); + method public synchronized void setParseStrict(boolean); method public synchronized void setPositivePrefix(java.lang.String); method public synchronized void setPositiveSuffix(java.lang.String); method public synchronized void setRoundingIncrement(java.math.BigDecimal); method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal); method public synchronized void setRoundingIncrement(double); + method public synchronized void setRoundingMode(int); method public synchronized void setScientificNotation(boolean); method public synchronized void setSecondaryGroupingSize(int); method public synchronized void setSignificantDigitsUsed(boolean); @@ -32141,10 +32164,12 @@ package android.os { public final class SystemClock { method public static long currentThreadTimeMillis(); method public static long elapsedRealtime(); + method public static java.time.Clock elapsedRealtimeClock(); method public static long elapsedRealtimeNanos(); method public static boolean setCurrentTimeMillis(long); method public static void sleep(long); method public static long uptimeMillis(); + method public static java.time.Clock uptimeMillisClock(); } public class TestLooperManager { diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 87d318bb3493..d99136fb61c1 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -38,7 +38,7 @@ statsd_common_src := \ src/matchers/CombinationLogMatchingTracker.cpp \ src/matchers/matcher_util.cpp \ src/matchers/SimpleLogMatchingTracker.cpp \ - src/metrics/CountAnomalyTracker.cpp \ + src/anomaly/DiscreteAnomalyTracker.cpp \ src/metrics/MetricProducer.cpp \ src/metrics/EventMetricProducer.cpp \ src/metrics/CountMetricProducer.cpp \ @@ -150,6 +150,7 @@ LOCAL_CFLAGS += \ LOCAL_SRC_FILES := \ $(statsd_common_src) \ tests/AnomalyMonitor_test.cpp \ + tests/anomaly/AnomalyTracker_test.cpp \ tests/ConditionTracker_test.cpp \ tests/ConfigManager_test.cpp \ tests/indexed_priority_queue_test.cpp \ diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 8910523aeda3..7ff42b66c5a0 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -15,9 +15,9 @@ */ #include "Log.h" +#include "statslog.h" #include "StatsLogProcessor.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "metrics/CountMetricProducer.h" #include "stats_util.h" @@ -25,6 +25,13 @@ #include <utils/Errors.h> using namespace android; +using android::util::FIELD_TYPE_BOOL; +using android::util::FIELD_TYPE_FLOAT; +using android::util::FIELD_TYPE_INT32; +using android::util::FIELD_TYPE_INT64; +using android::util::FIELD_TYPE_MESSAGE; +using android::util::FIELD_TYPE_STRING; +using android::util::ProtoOutputStream; using std::make_unique; using std::unique_ptr; using std::vector; @@ -33,6 +40,14 @@ namespace android { namespace os { namespace statsd { +// for ConfigMetricsReport +const int FIELD_ID_CONFIG_KEY = 1; +const int FIELD_ID_METRICS = 2; +const int FIELD_ID_UID_MAP = 3; +// for ConfigKey +const int FIELD_ID_UID = 1; +const int FIELD_ID_NAME = 1; + StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, const std::function<void(const vector<uint8_t>&)>& pushLog) : mUidMap(uidMap), mPushLog(pushLog) { @@ -48,6 +63,22 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { pair.second->onLogEvent(msg); flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second); } + + // Hard-coded logic to update the isolated uid's in the uid-map. + // The field numbers need to be currently updated by hand with stats_events.proto + if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) { + status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR; + bool is_create = msg.GetBool(3, &err); + auto parent_uid = int(msg.GetLong(1, &err2)); + auto isolated_uid = int(msg.GetLong(2, &err3)); + if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) { + if (is_create) { + mUidMap->assignIsolatedUid(isolated_uid, parent_uid); + } else { + mUidMap->removeIsolatedUid(isolated_uid, parent_uid); + } + } + } } void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) { @@ -70,27 +101,46 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig } } -ConfigMetricsReport StatsLogProcessor::onDumpReport(const ConfigKey& key) { - ConfigMetricsReport report; - +vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) { auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { ALOGW("Config source %s does not exist", key.ToString().c_str()); - return report; + return vector<uint8_t>(); } - auto set_key = report.mutable_config_key(); - set_key->set_uid(key.GetUid()); - set_key->set_name(key.GetName()); - for (auto m : it->second->onDumpReport()) { - // Transfer the vector of StatsLogReport into a field - // TODO: perhaps we just have bytes being returned from onDumpReport and transfer bytes - auto dest = report.add_metrics(); - *dest = m; + ProtoOutputStream proto; + + // Fill in ConfigKey. + long long configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid()); + proto.write(FIELD_TYPE_STRING | FIELD_ID_NAME, key.GetName()); + proto.end(configKeyToken); + + // Fill in StatsLogReport's. + for (auto& m : it->second->onDumpReport()) { + // Add each vector of StatsLogReport into a repeated field. + proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_METRICS, reinterpret_cast<char*>(m.get()->data()), + m.get()->size()); } - auto temp = mUidMap->getOutput(key); - report.mutable_uid_map()->Swap(&temp); - return report; + + // Fill in UidMap. + auto uidMap = mUidMap->getOutput(key); + const int uidMapSize = uidMap.ByteSize(); + char uidMapBuffer[uidMapSize]; + uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize); + proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize); + + vector<uint8_t> buffer(proto.size()); + size_t pos = 0; + auto iter = proto.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&buffer[pos], iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } + + return buffer; } void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 0083827a347d..f38d71550eda 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -41,8 +41,7 @@ public: void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config); void OnConfigRemoved(const ConfigKey& key); - // TODO: Once we have the ProtoOutputStream in c++, we can just return byte array. - ConfigMetricsReport onDumpReport(const ConfigKey& key); + vector<uint8_t> onDumpReport(const ConfigKey& key); /* Request a flush through a binder call. */ void flush(); diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h index e2ac623c4670..d9207e99dd6f 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.h +++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h @@ -55,6 +55,7 @@ struct AnomalyAlarm : public RefBase { }; }; +// TODO: Rename this file to AnomalyAlarmMonitor. /** * Manages alarms for Anomaly Detection. */ @@ -95,6 +96,15 @@ public: unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> popSoonerThan( uint32_t timestampSec); + // TODO: Function that uses popSoonerThan to get all alarms that have fired, and then + // iterates over all DurationAnomalyTracker, looking for those alarms. When they're found, + // have them declareAnomaly on those alarms. This means that DurationAnomalyTracker + // must be thread-safe (since this is being called on a different thread). There is no + // worry about missing the alarms (due to them being cancelled after this function being called) + // because DurationAnomalyTracker guarantees that it checks for anaomlies when it cancels + // alarms anyway. + // void declareAnomalies(uint32_t timestampSec); + /** * Returns the projected alarm timestamp that is registered with * StatsCompanionService. This may not be equal to the soonest alarm, diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp new file mode 100644 index 000000000000..9c9bde9a459e --- /dev/null +++ b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include "DiscreteAnomalyTracker.h" + +#include <time.h> + +namespace android { +namespace os { +namespace statsd { + +DiscreteAnomalyTracker::DiscreteAnomalyTracker(const Alert& alert) : mAlert(alert) { + VLOG("DiscreteAnomalyTracker() called"); + if (mAlert.number_of_buckets() <= 0) { + ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets", + (long long)mAlert.number_of_buckets()); + return; + } + mPastBuckets.resize(mAlert.number_of_buckets()); + reset(); // initialization +} + +DiscreteAnomalyTracker::~DiscreteAnomalyTracker() { + VLOG("~DiscreteAnomalyTracker() called"); +} + +void DiscreteAnomalyTracker::reset() { + VLOG("reset() called."); + mPastBuckets.clear(); + mPastBuckets.resize(mAlert.number_of_buckets()); + mSumOverPastBuckets.clear(); + mCurrentBucketIndex = -1; + mLastAlarmAtBucketIndex = -1; + mAnomalyDeclared = 0; +} + +size_t DiscreteAnomalyTracker::index(int64_t bucketNum) { + return bucketNum % mAlert.number_of_buckets(); +} + +void DiscreteAnomalyTracker::addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues, + int64_t bucketIndex) { + VLOG("addPastBucket() called."); + if (bucketIndex <= mCurrentBucketIndex - mAlert.number_of_buckets()) { + ALOGE("Cannot add a past bucket %lld units in past", (long long)bucketIndex); + return; + } + + // Empty out old mPastBuckets[i] values and update mSumOverPastBuckets. + if (bucketIndex - mCurrentBucketIndex >= mAlert.number_of_buckets()) { + mPastBuckets.clear(); + mPastBuckets.resize(mAlert.number_of_buckets()); + mSumOverPastBuckets.clear(); + } else { + for (int64_t i = std::max( + 0LL, (long long)(mCurrentBucketIndex - mAlert.number_of_buckets() + 1)); + i < bucketIndex - mAlert.number_of_buckets(); i++) { + const int idx = index(i); + subtractBucketFromSum(mPastBuckets[idx]); + mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket. + } + } + subtractBucketFromSum(mPastBuckets[index(bucketIndex)]); + mPastBuckets[index(bucketIndex)] = nullptr; // release (but not clear) the old bucket. + + // Replace the oldest bucket with the new bucket we are adding. + mPastBuckets[index(bucketIndex)] = BucketValues; + addBucketToSum(BucketValues); + + mCurrentBucketIndex = std::max(mCurrentBucketIndex, bucketIndex); +} + +void DiscreteAnomalyTracker::subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket) { + if (bucket == nullptr) { + return; + } + // For each dimension present in the bucket, subtract its value from its corresponding sum. + for (const auto& keyValuePair : *bucket) { + auto itr = mSumOverPastBuckets.find(keyValuePair.first); + if (itr == mSumOverPastBuckets.end()) { + continue; + } + itr->second -= keyValuePair.second; + // TODO: No need to look up the object twice like this. Use a var. + if (itr->second == 0) { + mSumOverPastBuckets.erase(itr); + } + } +} + +void DiscreteAnomalyTracker::addBucketToSum(const shared_ptr<const DimToValMap>& bucket) { + if (bucket == nullptr) { + return; + } + // For each dimension present in the bucket, add its value to its corresponding sum. + for (const auto& keyValuePair : *bucket) { + mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second; + } +} + +bool DiscreteAnomalyTracker::detectAnomaly() { + for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) { + if (mAlert.has_trigger_if_sum_gt() && itr->second > mAlert.trigger_if_sum_gt()) { + return true; + } + } + return false; +} + +void DiscreteAnomalyTracker::declareAndDeclareAnomaly() { + if (detectAnomaly()) { + declareAnomaly(); + } +} + +void DiscreteAnomalyTracker::declareAnomaly() { + if (mLastAlarmAtBucketIndex >= 0 && mCurrentBucketIndex - mLastAlarmAtBucketIndex <= + (long long)mAlert.refractory_period_in_buckets()) { + VLOG("Skipping anomaly check since within refractory period"); + return; + } + mAnomalyDeclared++; + // TODO(guardrail): Consider guarding against too short refractory periods. + mLastAlarmAtBucketIndex = mCurrentBucketIndex; + + if (mAlert.has_incidentd_details()) { + const Alert_IncidentdDetails& incident = mAlert.incidentd_details(); + if (incident.has_alert_name()) { + ALOGW("An anomaly (%s) has occurred! Informing incidentd.", + incident.alert_name().c_str()); + } else { + // TODO: Can construct a name based on the criteria (and/or relay the criteria). + ALOGW("An anomaly (nameless) has occurred! Informing incidentd."); + } + // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd + } else { + ALOGW("An anomaly has occurred! (But informing incidentd not requested.)"); + } +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h new file mode 100644 index 000000000000..ed7d5d715a6d --- /dev/null +++ b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gtest/gtest_prod.h> +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert +#include "stats_util.h" // HashableDimensionKey and DimToValMap + +#include <memory> // unique_ptr +#include <stdlib.h> + +namespace android { +namespace os { +namespace statsd { + +using std::unordered_map; +using std::shared_ptr; + +// This anomaly track assmues that all values are non-negative. +class DiscreteAnomalyTracker { + public: + DiscreteAnomalyTracker(const Alert& alert); + + virtual ~DiscreteAnomalyTracker(); + + // Adds a new bucket or updates an existing bucket. + // Bucket index starts from 0. + void addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues, int64_t bucketIndex); + + // Returns true if detected anomaly for the existing buckets on one or more dimension keys. + bool detectAnomaly(); + + // Informs incidentd about the detected alert. + void declareAnomaly(); + + // Detects the alert and informs the incidentd when applicable. + void declareAndDeclareAnomaly(); + +private: + // statsd_config.proto Alert message that defines this tracker. + const Alert mAlert; + + // The exisiting bucket list. + std::vector<shared_ptr<const DimToValMap>> mPastBuckets; + + // Sum over all existing buckets cached in mPastBuckets. + DimToValMap mSumOverPastBuckets; + + // Current bucket index of the current anomaly detection window. Bucket index starts from 0. + int64_t mCurrentBucketIndex = -1; + + // The bucket index when the last anomaly was declared. + int64_t mLastAlarmAtBucketIndex = -1; + + // The total number of declared anomalies. + int64_t mAnomalyDeclared = 0; + + // Add the information in the given bucket to mSumOverPastBuckets. + void addBucketToSum(const shared_ptr<const DimToValMap>& bucket); + + // Subtract the information in the given bucket from mSumOverPastBuckets + // and remove any items with value 0. + void subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket); + + // Calculates the corresponding bucket index within the circular array. + size_t index(int64_t bucketNum); + + // Resets all data. For use when all the data gets stale. + void reset(); + + FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); + FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp index 40d41be381fd..2618a217c378 100644 --- a/cmds/statsd/src/condition/condition_util.cpp +++ b/cmds/statsd/src/condition/condition_util.cpp @@ -25,7 +25,6 @@ #include <unordered_map> #include "../matchers/matcher_util.h" #include "ConditionTracker.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h index 47e245e4704b..4167bf922b19 100644 --- a/cmds/statsd/src/condition/condition_util.h +++ b/cmds/statsd/src/condition/condition_util.h @@ -19,7 +19,6 @@ #include <vector> #include "../matchers/matcher_util.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h index a58766d2a382..19ccfcfe7491 100644 --- a/cmds/statsd/src/config/ConfigListener.h +++ b/cmds/statsd/src/config/ConfigListener.h @@ -16,7 +16,6 @@ #pragma once -#include <frameworks/base/cmds/statsd/src/stats_log.pb.h> #include "config/ConfigKey.h" #include <utils/RefBase.h> diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 02e6903e3260..94566ffa2d73 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -165,6 +165,12 @@ static StatsdConfig build_fake_config() { KeyMatcher* keyMatcher = metric->add_dimension(); keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY); + // Anomaly threshold for background count. + alert = metric->add_alerts(); + alert->set_number_of_buckets(4); + alert->set_trigger_if_sum_gt(30); + alert->set_refractory_period_secs(20); + // Count process state changes, slice by uid, while SCREEN_IS_OFF metric = config.add_count_metric(); metric->set_metric_id(3); diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h index 006d74c05b9c..adb691e9568e 100644 --- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h +++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h @@ -22,7 +22,6 @@ #include <unordered_map> #include <vector> #include "LogMatchingTracker.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h index d82da3ba759c..ffbf2488ad2c 100644 --- a/cmds/statsd/src/matchers/LogMatchingTracker.h +++ b/cmds/statsd/src/matchers/LogMatchingTracker.h @@ -17,7 +17,6 @@ #ifndef LOG_MATCHING_TRACKER_H #define LOG_MATCHING_TRACKER_H -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "logd/LogEvent.h" #include "matchers/matcher_util.h" diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h index e110ec87affc..5dca55ed8d45 100644 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h +++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h @@ -23,7 +23,6 @@ #include <unordered_map> #include <vector> #include "LogMatchingTracker.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index 6aa22118b320..cccc9b3317e7 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -16,7 +16,6 @@ #include "Log.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "matchers/LogMatchingTracker.h" #include "matchers/matcher_util.h" diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp deleted file mode 100644 index 7aa748fdeb77..000000000000 --- a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define DEBUG true // STOPSHIP if true -#include "Log.h" - -#include "CountAnomalyTracker.h" - -#include <time.h> - -namespace android { -namespace os { -namespace statsd { - -CountAnomalyTracker::CountAnomalyTracker(const Alert& alert) - : mAlert(alert), - mNumPastBuckets(alert.number_of_buckets() > 0 ? alert.number_of_buckets() - 1 : 0), - mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr) { - - VLOG("CountAnomalyTracker() called"); - if (alert.number_of_buckets() < 1) { - ALOGE("Cannot create CountAnomalyTracker with %d buckets", alert.number_of_buckets()); - } - reset(); // initialization -} - -CountAnomalyTracker::~CountAnomalyTracker() { - VLOG("~CountAnomalyTracker() called"); -} - -void CountAnomalyTracker::addPastBucket(int pastBucketCount, - time_t numberOfBucketsAgo) { - VLOG("addPastBucket() called."); - if (numberOfBucketsAgo < 1) { - ALOGE("Cannot add a past bucket %ld units in past", numberOfBucketsAgo); - return; - } - // If past bucket was ancient, just empty out all past info. - // This always applies if mNumPastBuckets == 0 (i.e. store no past buckets). - if (numberOfBucketsAgo > (time_t) mNumPastBuckets) { - reset(); - return; - } - - // Empty out old mPastBuckets[i] values and update mSumPastCounters. - for (size_t i = mOldestBucketIndex; - i < mOldestBucketIndex + numberOfBucketsAgo; i++) { - mSumPastCounters -= mPastBuckets[index(i)]; - mPastBuckets[index(i)] = 0; - } - - // Replace the oldest bucket with the new bucket we are adding. - mPastBuckets[mOldestBucketIndex] = pastBucketCount; - mSumPastCounters += pastBucketCount; - - // Advance the oldest bucket index by numberOfBucketsAgo units. - mOldestBucketIndex = index(mOldestBucketIndex + numberOfBucketsAgo); - - // TODO: Once dimensions are added to mSumPastCounters: - // iterate through mSumPastCounters and remove any entries that are 0. -} - -void CountAnomalyTracker::reset() { - VLOG("reset() called."); - for (size_t i = 0; i < mNumPastBuckets; i++) { - mPastBuckets[i] = 0; - } - mSumPastCounters = 0; - mOldestBucketIndex = 0; -} - -void CountAnomalyTracker::checkAnomaly(int currentCount) { - // Skip the check if in refractory period. - if (time(nullptr) < mRefractoryPeriodEndsSec) { - VLOG("Skipping anomaly check since within refractory period"); - return; - } - - // TODO: Remove these extremely verbose debugging log. - VLOG("Checking whether %d + %d > %lld", - mSumPastCounters, currentCount, mAlert.trigger_if_sum_gt()); - - // Note that this works even if mNumPastBuckets < 1 (since then - // mSumPastCounters = 0 so the comparison is based only on currentCount). - if (mAlert.has_trigger_if_sum_gt() && - mSumPastCounters + currentCount > mAlert.trigger_if_sum_gt()) { - declareAnomaly(); - } -} - -void CountAnomalyTracker::declareAnomaly() { - // TODO(guardrail): Consider guarding against too short refractory periods. - time_t currTime = time(nullptr); - mRefractoryPeriodEndsSec = currTime + mAlert.refractory_period_secs(); - - // TODO: If we had access to the bucket_size_millis, consider calling reset() - // if (mAlert.refractory_period_secs() > mNumPastBuckets * bucket_size_millis * 1000). - - if (mAlert.has_incidentd_details()) { - const Alert_IncidentdDetails& incident = mAlert.incidentd_details(); - if (incident.has_alert_name()) { - ALOGW("An anomaly (%s) has occurred! Informing incidentd.", - incident.alert_name().c_str()); - } else { - // TODO: Can construct a name based on the criteria (and/or relay the criteria). - ALOGW("An anomaly (nameless) has occurred! Informing incidentd."); - } - // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd - } else { - ALOGW("An anomaly has occurred! (But informing incidentd not requested.)"); - } -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.h b/cmds/statsd/src/metrics/CountAnomalyTracker.h deleted file mode 100644 index 79c47d2a6bab..000000000000 --- a/cmds/statsd/src/metrics/CountAnomalyTracker.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef COUNT_ANOMALY_TRACKER_H -#define COUNT_ANOMALY_TRACKER_H - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert - -#include <memory> // unique_ptr -#include <stdlib.h> - -namespace android { -namespace os { -namespace statsd { - -// TODO: Can probably be used for Count, Value, and Gauge. If so, rename to ValueAnomalyTracker. -// (caveat: currently, the value cannot be negative. Probably fine for P.) -class CountAnomalyTracker { -public: - CountAnomalyTracker(const Alert& alert); - - virtual ~CountAnomalyTracker(); - - - // Adds a new past bucket, holding pastBucketCount, and then advances the - // present by numberOfBucketsAgo buckets (filling any intervening buckets - // with 0s). - // Thus, the newly added bucket (which holds pastBucketCount) is stored - // numberOfBucketsAgo buckets ago. - void addPastBucket(int pastBucketCount, time_t numberOfBucketsAgo); - - // Informs the anomaly tracker of the current bucket's count, so that it can - // determine whether an anomaly has occurred. This value is not stored. - void checkAnomaly(int currentCount); - -private: - // statsd_config.proto Alert message that defines this tracker. - const Alert mAlert; - - // Number of past buckets. One less than the total number of buckets needed - // for the anomaly detection (since the current bucket is not in the past). - const size_t mNumPastBuckets; - - // Count values for each of the past mNumPastBuckets buckets. - // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter. - std::unique_ptr<int[]> mPastBuckets; - - // Sum over all of mPastBuckets (cached). - // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter. - // At that point, mSumPastCounters must never contain entries of 0. - int mSumPastCounters; - - // Index of the oldest bucket (i.e. the next bucket to be overwritten). - size_t mOldestBucketIndex = 0; - - // Timestamp that the refractory period (if this anomaly was declared) ends, in seconds. - // If an anomaly was never declared, set to 0. - time_t mRefractoryPeriodEndsSec = 0; - - void declareAnomaly(); - - // Calculates the corresponding index within the circular array. - size_t index(size_t unsafeIndex) { - return unsafeIndex % mNumPastBuckets; - } - - // Resets all data. For use when all the data gets stale. - void reset(); -}; - -} // namespace statsd -} // namespace os -} // namespace android -#endif // COUNT_ANOMALY_TRACKER_H diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 94f4adf11c79..0c9e522493ad 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -17,7 +17,7 @@ #define DEBUG true // STOPSHIP if true #include "Log.h" -#include "CountAnomalyTracker.h" +#include "../anomaly/DiscreteAnomalyTracker.h" #include "CountMetricProducer.h" #include "stats_util.h" @@ -61,6 +61,7 @@ const int FIELD_ID_END_BUCKET_NANOS = 2; const int FIELD_ID_COUNT = 3; // TODO: add back AnomalyTracker. + CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex, const sp<ConditionWizard>& wizard, const uint64_t startTimeNs) @@ -76,7 +77,7 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int co for (int i = 0; i < metric.alerts_size(); i++) { const Alert& alert = metric.alerts(i); if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) { - mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert)); + mAnomalyTrackers.push_back(std::make_unique<DiscreteAnomalyTracker>(alert)); } else { ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d", alert.trigger_if_sum_gt(), alert.number_of_buckets()); @@ -116,7 +117,7 @@ void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id()); } -StatsLogReport CountMetricProducer::onDumpReport() { +std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() { long long endTime = time(nullptr) * NS_PER_SEC; // Dump current bucket if it's stale. @@ -171,15 +172,13 @@ StatsLogReport CountMetricProducer::onDumpReport() { (long long)mCurrentBucketStartTimeNs); VLOG("metric %lld dump report now...", mMetric.metric_id()); - std::unique_ptr<uint8_t[]> buffer = serializeProto(); + std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(endTime); mPastBuckets.clear(); mByteSize = 0; - // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this: - // return std::move(buffer); - return StatsLogReport(); + return buffer; // TODO: Clear mDimensionKeyMap once the report is dumped. } @@ -201,25 +200,19 @@ void CountMetricProducer::onMatchedLogEventInternal( return; } - auto it = mCurrentSlicedCounter.find(eventKey); + auto it = mCurrentSlicedCounter->find(eventKey); - if (it == mCurrentSlicedCounter.end()) { + if (it == mCurrentSlicedCounter->end()) { // create a counter for the new key - mCurrentSlicedCounter[eventKey] = 1; - + (*mCurrentSlicedCounter)[eventKey] = 1; } else { // increment the existing value auto& count = it->second; count++; } - // TODO: Re-add anomaly detection (similar to): - // for (auto& tracker : mAnomalyTrackers) { - // tracker->checkAnomaly(mCounter); - // } - VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(), - mCurrentSlicedCounter[eventKey]); + (*mCurrentSlicedCounter)[eventKey]); } // When a new matched event comes in, we check if event falls into the current @@ -230,12 +223,13 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) { } // adjust the bucket start time - int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; + // TODO: This (and addPastBucket to which it goes) doesn't really need to be an int64. + uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; CountBucket info; info.mBucketStartNs = mCurrentBucketStartTimeNs; info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs; - for (const auto& counter : mCurrentSlicedCounter) { + for (const auto& counter : *mCurrentSlicedCounter) { info.mCount = counter.second; auto& bucketList = mPastBuckets[counter.first]; bucketList.push_back(info); @@ -244,15 +238,16 @@ void CountMetricProducer::flushCounterIfNeeded(const uint64_t eventTimeNs) { mByteSize += sizeof(info); } - // TODO: Re-add anomaly detection (similar to): - // for (auto& tracker : mAnomalyTrackers) { - // tracker->addPastBucket(mCounter, numBucketsForward); - //} + for (auto& tracker : mAnomalyTrackers) { + tracker->addOrUpdateBucket(mCurrentSlicedCounter, mCurrentBucketNum); + tracker->declareAndDeclareAnomaly(); + } - // Reset counters - mCurrentSlicedCounter.clear(); + // Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers). + mCurrentSlicedCounter = std::make_shared<DimToValMap>(); mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs; + mCurrentBucketNum += numBucketsForward; VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(), (long long)mCurrentBucketStartTimeNs); } diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index c3af00670089..b7e480c4e78d 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -23,14 +23,11 @@ #include <gtest/gtest_prod.h> #include "../condition/ConditionTracker.h" #include "../matchers/matcher_util.h" -#include "CountAnomalyTracker.h" +#include "../anomaly/DiscreteAnomalyTracker.h" #include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" -using namespace std; - namespace android { namespace os { namespace statsd { @@ -54,7 +51,7 @@ public: void finish() override; // TODO: Pass a timestamp as a parameter in onDumpReport. - StatsLogReport onDumpReport() override; + std::unique_ptr<std::vector<uint8_t>> onDumpReport() override; void onSlicedConditionMayChange(const uint64_t eventTime) override; @@ -82,9 +79,9 @@ private: size_t mByteSize; // The current bucket. - std::unordered_map<HashableDimensionKey, int> mCurrentSlicedCounter; + std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>(); - vector<unique_ptr<CountAnomalyTracker>> mAnomalyTrackers; + vector<std::unique_ptr<DiscreteAnomalyTracker>> mAnomalyTrackers; void flushCounterIfNeeded(const uint64_t newEventTime); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index c0a0d9840a90..2783bceada38 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -159,7 +159,7 @@ static void addDurationBucketsToReport(StatsLogReport_DurationMetricDataWrapper& } } -StatsLogReport DurationMetricProducer::onDumpReport() { +std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() { long long endTime = time(nullptr) * NS_PER_SEC; // Dump current bucket if it's stale. @@ -214,14 +214,12 @@ StatsLogReport DurationMetricProducer::onDumpReport() { mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); - std::unique_ptr<uint8_t[]> buffer = serializeProto(); + std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(endTime); mPastBuckets.clear(); - // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this: - // return std::move(buffer); - return StatsLogReport(); + return buffer; } void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 8fdd0d4f7e10..eea00454d5e6 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -26,7 +26,6 @@ #include "duration_helper/DurationTracker.h" #include "duration_helper/MaxDurationTracker.h" #include "duration_helper/OringDurationTracker.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" @@ -50,7 +49,7 @@ public: void finish() override; // TODO: Pass a timestamp as a parameter in onDumpReport. - StatsLogReport onDumpReport() override; + std::unique_ptr<std::vector<uint8_t>> onDumpReport() override; void onSlicedConditionMayChange(const uint64_t eventTime) override; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index ee0bfde6d099..f516cc7a2c7a 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -84,20 +84,18 @@ void EventMetricProducer::finish() { void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { } -StatsLogReport EventMetricProducer::onDumpReport() { +std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() { long long endTime = time(nullptr) * NS_PER_SEC; mProto->end(mProtoToken); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime); size_t bufferSize = mProto->size(); VLOG("metric %lld dump report now... proto size: %zu ", mMetric.metric_id(), bufferSize); - std::unique_ptr<uint8_t[]> buffer = serializeProto(); + std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(endTime); - // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this: - // return std::move(buffer); - return StatsLogReport(); + return buffer; } void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 2ca81819f1d5..0dccdf493869 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -24,7 +24,6 @@ #include "../condition/ConditionTracker.h" #include "../matchers/matcher_util.h" #include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" @@ -50,7 +49,7 @@ public: void finish() override; // TODO: Pass a timestamp as a parameter in onDumpReport. - StatsLogReport onDumpReport() override; + std::unique_ptr<std::vector<uint8_t>> onDumpReport() override; void onSlicedConditionMayChange(const uint64_t eventTime) override; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index dcfcc19ad68a..285d29b9225c 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -106,7 +106,7 @@ void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) { void GaugeMetricProducer::finish() { } -StatsLogReport GaugeMetricProducer::onDumpReport() { +std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() { VLOG("gauge metric %lld dump report now...", mMetric.metric_id()); // Dump current bucket if it's stale. @@ -159,15 +159,13 @@ StatsLogReport GaugeMetricProducer::onDumpReport() { mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); - std::unique_ptr<uint8_t[]> buffer = serializeProto(); + std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(time(nullptr) * NS_PER_SEC); mPastBuckets.clear(); mByteSize = 0; - // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this: - // return std::move(buffer); - return StatsLogReport(); + return buffer; // TODO: Clear mDimensionKeyMap once the report is dumped. } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 3757174652d5..d80672d25957 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -24,7 +24,6 @@ #include "../external/StatsPullerManager.h" #include "../matchers/matcher_util.h" #include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" @@ -60,7 +59,7 @@ public: void finish() override; // TODO: Pass a timestamp as a parameter in onDumpReport. - StatsLogReport onDumpReport() override; + std::unique_ptr<std::vector<uint8_t>> onDumpReport() override; size_t byteSize() override; diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 5ca83b2290da..3c4dd907dc24 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -64,16 +64,16 @@ void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent scheduledPull); } -std::unique_ptr<uint8_t[]> MetricProducer::serializeProto() { +std::unique_ptr<std::vector<uint8_t>> MetricProducer::serializeProto() { size_t bufferSize = mProto->size(); - std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]); + std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize)); size_t pos = 0; auto it = mProto->data(); while (it.readBuffer() != NULL) { size_t toRead = it.currentToRead(); - std::memcpy(&buffer[pos], it.readBuffer(), toRead); + std::memcpy(&buffer.get()[pos], it.readBuffer(), toRead); pos += toRead; it.rp()->move(toRead); } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 7ac97a8a2e8b..c0930e381ce3 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -23,7 +23,6 @@ #include <log/logprint.h> #include <utils/RefBase.h> -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" namespace android { namespace os { @@ -39,6 +38,7 @@ public: const sp<ConditionWizard>& wizard) : mStartTimeNs(startTimeNs), mCurrentBucketStartTimeNs(startTimeNs), + mCurrentBucketNum(0), mCondition(conditionIndex >= 0 ? false : true), mConditionSliced(false), mWizard(wizard), @@ -61,7 +61,7 @@ public: // TODO: Pass a timestamp as a parameter in onDumpReport and update all its // implementations. - virtual StatsLogReport onDumpReport() = 0; + virtual std::unique_ptr<std::vector<uint8_t>> onDumpReport() = 0; virtual bool isConditionSliced() const { return mConditionSliced; @@ -74,6 +74,8 @@ protected: uint64_t mCurrentBucketStartTimeNs; + uint64_t mCurrentBucketNum; + int64_t mBucketSizeNs; bool mCondition; @@ -118,7 +120,7 @@ protected: virtual void startNewProtoOutputStream(long long timestamp) = 0; - std::unique_ptr<uint8_t[]> serializeProto(); + std::unique_ptr<std::vector<uint8_t>> serializeProto(); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 80b325f6e701..19317eefe76c 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -56,10 +56,10 @@ void MetricsManager::finish() { } } -vector<StatsLogReport> MetricsManager::onDumpReport() { +vector<std::unique_ptr<vector<uint8_t>>> MetricsManager::onDumpReport() { VLOG("=========================Metric Reports Start=========================="); // one StatsLogReport per MetricProduer - vector<StatsLogReport> reportList; + vector<std::unique_ptr<vector<uint8_t>>> reportList; for (auto& metric : mAllMetricProducers) { reportList.push_back(metric->onDumpReport()); } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 63e2c338488e..39c79f99daca 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -44,7 +44,7 @@ public: void finish(); // Config source owner can call onDumpReport() to get all the metrics collected. - std::vector<StatsLogReport> onDumpReport(); + std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport(); size_t byteSize(); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 41b7a5d1b457..2a630733284a 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -108,7 +108,7 @@ void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id()); } -StatsLogReport ValueMetricProducer::onDumpReport() { +std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() { VLOG("metric %lld dump report now...", mMetric.metric_id()); for (const auto& pair : mPastBuckets) { @@ -156,15 +156,13 @@ StatsLogReport ValueMetricProducer::onDumpReport() { (long long)mCurrentBucketStartTimeNs); VLOG("metric %lld dump report now...", mMetric.metric_id()); - std::unique_ptr<uint8_t[]> buffer = serializeProto(); + std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(time(nullptr) * NS_PER_SEC); mPastBuckets.clear(); mByteSize = 0; - // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this: - // return std::move(buffer); - return StatsLogReport(); + return buffer; // TODO: Clear mDimensionKeyMap once the report is dumped. } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 843766540844..ef9868b613b2 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -21,9 +21,7 @@ #include "../condition/ConditionTracker.h" #include "../external/PullDataReceiver.h" #include "../external/StatsPullerManager.h" -#include "CountAnomalyTracker.h" #include "MetricProducer.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { @@ -49,7 +47,7 @@ public: void finish() override; // TODO: Pass a timestamp as a parameter in onDumpReport. - StatsLogReport onDumpReport() override; + std::unique_ptr<std::vector<uint8_t>> onDumpReport() override; void onSlicedConditionMayChange(const uint64_t eventTime); diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index aeb234de17fe..5c76d0e6513b 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -20,8 +20,6 @@ #include "condition/ConditionWizard.h" #include "stats_util.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" - namespace android { namespace os { namespace statsd { diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index de53d626c65d..b095884876a8 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -19,8 +19,6 @@ #include "DurationTracker.h" -#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" - namespace android { namespace os { namespace statsd { diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index d83c3a426685..7b276421a259 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -150,6 +150,31 @@ void UidMap::removeListener(sp<PackageInfoListener> producer) { mSubscribers.erase(producer); } +void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) { + lock_guard<mutex> lock(mIsolatedMutex); + + mIsolatedUidMap[isolatedUid] = parentUid; +} + +void UidMap::removeIsolatedUid(int isolatedUid, int parentUid) { + lock_guard<mutex> lock(mIsolatedMutex); + + auto it = mIsolatedUidMap.find(isolatedUid); + if (it != mIsolatedUidMap.end()) { + mIsolatedUidMap.erase(it); + } +} + +int UidMap::getParentUidOrSelf(int uid) { + lock_guard<mutex> lock(mIsolatedMutex); + + auto it = mIsolatedUidMap.find(uid); + if (it != mIsolatedUidMap.end()) { + return it->second; + } + return uid; +} + void UidMap::clearOutput() { mOutput.Clear(); diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index bf120e04d7c8..de68fbceb7a9 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -84,6 +84,12 @@ public: // Informs uid map that a config is removed. Used for keeping mConfigKeys up to date. void OnConfigRemoved(const ConfigKey& key); + void assignIsolatedUid(int isolatedUid, int parentUid); + void removeIsolatedUid(int isolatedUid, int parentUid); + + // Returns the parent uid if it exists. Otherwise, returns the same uid that was passed-in. + int getParentUidOrSelf(int uid); + // Gets the output. If every config key has received the output, then the output is cleared. UidMapping getOutput(const ConfigKey& key); @@ -105,11 +111,16 @@ private: // TODO: Use shared_mutex for improved read-locking if a library can be found in Android. mutable mutex mMutex; + mutable mutex mIsolatedMutex; // Maps uid to application data. This must be multimap since there is a feature in Android for // multiple apps to share the same uid. std::unordered_multimap<int, AppData> mMap; + // Maps isolated uid to the parent uid. Any metrics for an isolated uid will instead contribute + // to the parent uid. + std::unordered_map<int, int> mIsolatedUidMap; + // We prepare the output proto as apps are updated, so that we can grab the current output. UidMapping mOutput; diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto index 82d975914682..8816795ed82f 100644 --- a/cmds/statsd/src/stats_events.proto +++ b/cmds/statsd/src/stats_events.proto @@ -72,7 +72,7 @@ message StatsEvent { PhoneSignalStrengthChanged phone_signal_strength_changed = 40; SettingChanged setting_changed = 41; ActivityForegroundStateChanged activity_foreground_state_changed = 42; - + IsolatedUidChanged isolated_uid_changed = 43; // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. } @@ -695,7 +695,6 @@ message SettingChanged { optional int32 user = 7; } - /* * Logs activity going to foreground or background * @@ -848,3 +847,22 @@ message PowerStateSubsystemSleepStatePulled { optional uint64 last_entry_timestamp_ms = 5; optional bool supported_only_in_suspend = 6; } + +/** + * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky + * behavior in its own uid. However, the metrics of these isolated uid's almost always should be + * attributed back to the parent (host) uid. One example is Chrome. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message IsolatedUidChanged { + // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid. + optional int32 parent_uid = 1; + + 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; +} diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index a9507bf5a5c7..e1d0aceb3bd5 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef STATS_UTIL_H -#define STATS_UTIL_H + +#pragma once #include "logd/LogReader.h" #include "storage/DropboxWriter.h" @@ -22,6 +22,8 @@ #include <log/logprint.h> #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include <unordered_map> + namespace android { namespace os { namespace statsd { @@ -33,6 +35,10 @@ typedef std::string HashableDimensionKey; typedef std::map<std::string, HashableDimensionKey> ConditionKey; +// TODO: For P, change int to int64_t. +// TODO: Should HashableDimensionKey be marked here as const? +typedef std::unordered_map<HashableDimensionKey, int> DimToValMap; + EventMetricData parse(log_msg msg); int getTagId(log_msg msg); @@ -41,5 +47,3 @@ std::string getHashableKey(std::vector<KeyValuePair> key); } // namespace statsd } // namespace os } // namespace android - -#endif // STATS_UTIL_H diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index f3e68946fead..3b8eeaf23d7f 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -117,6 +117,8 @@ message Alert { optional int32 refractory_period_secs = 4; optional int64 trigger_if_sum_gt = 5; + + optional int32 refractory_period_in_buckets = 6; } message EventMetric { @@ -168,7 +170,6 @@ message DurationMetric { repeated Alert alerts = 7; repeated EventConditionLink links = 8; - } message GaugeMetric { @@ -235,4 +236,6 @@ message StatsdConfig { repeated LogEntryMatcher log_entry_matcher = 7; repeated Condition condition = 8; + + repeated Alert alerts = 9; } diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index 671f6d4ac472..c64719ee0c85 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -13,7 +13,10 @@ // limitations under the License. #include "packages/UidMap.h" +#include "StatsLogProcessor.h" #include "config/ConfigKey.h" +#include "logd/LogEvent.h" +#include "statslog.h" #include <gtest/gtest.h> @@ -29,6 +32,31 @@ namespace statsd { const string kApp1 = "app1.sharing.1"; const string kApp2 = "app2.sharing.1"; +TEST(UidMapTest, TestIsolatedUID) { + sp<UidMap> m = new UidMap(); + StatsLogProcessor p(m, nullptr); + LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1); + android_log_event_list* list = addEvent.GetAndroidLogEventList(); + *list << 100; // parent UID + *list << 101; // isolated UID + *list << 1; // Indicates creation. + addEvent.init(); + + EXPECT_EQ(101, m->getParentUidOrSelf(101)); + + p.OnLogEvent(addEvent); + EXPECT_EQ(100, m->getParentUidOrSelf(101)); + + LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1); + list = removeEvent.GetAndroidLogEventList(); + *list << 100; // parent UID + *list << 101; // isolated UID + *list << 0; // Indicates removal. + removeEvent.init(); + p.OnLogEvent(removeEvent); + EXPECT_EQ(101, m->getParentUidOrSelf(101)); +} + TEST(UidMapTest, TestMatching) { UidMap m; vector<int32_t> uids; diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp new file mode 100644 index 000000000000..b8150d04ed07 --- /dev/null +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -0,0 +1,239 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/anomaly/DiscreteAnomalyTracker.h" + +#include <gtest/gtest.h> +#include <stdio.h> +#include <vector> + +using namespace testing; +using android::sp; +using std::set; +using std::unordered_map; +using std::vector; + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +void AddValueToBucket(const std::vector<std::pair<string, long>>& key_value_pair_list, + std::shared_ptr<DimToValMap> bucket) { + for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) { + (*bucket)[itr->first] += itr->second; + } +} + +std::shared_ptr<DimToValMap> MockeBucket( + const std::vector<std::pair<string, long>>& key_value_pair_list) { + std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>(); + AddValueToBucket(key_value_pair_list, bucket); + return bucket; +} + +TEST(AnomalyTrackerTest, TestConsecutiveBuckets) { + Alert alert; + alert.set_number_of_buckets(3); + alert.set_refractory_period_in_buckets(3); + alert.set_trigger_if_sum_gt(2); + + DiscreteAnomalyTracker anomaly_tracker(alert); + + std::shared_ptr<DimToValMap> bucket0 = MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}}); + // Adds bucket #0 + anomaly_tracker.addOrUpdateBucket(bucket0, 0); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1); + EXPECT_FALSE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L); + + // Adds bucket #0 again. The sum does not change. + anomaly_tracker.addOrUpdateBucket(bucket0, 0); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 0L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1); + EXPECT_FALSE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L); + + // Adds bucket #1. + std::shared_ptr<DimToValMap> bucket1 = MockeBucket({{"b", 2}}); + anomaly_tracker.addOrUpdateBucket(bucket1, 1); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1); + // Alarm. + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L); + + // Adds bucket #1 again. The sum does not change. + anomaly_tracker.addOrUpdateBucket(bucket1, 1); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1); + // Alarm. + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L); + + // Adds bucket #2. + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 2); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 2L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1); + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + // Within refractory period. + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L); + + // Adds bucket #3. + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 3); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 3L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2); + EXPECT_FALSE(anomaly_tracker.detectAnomaly()); + + // Adds bucket #3. + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 2}}), 4); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 4L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4); + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + // Within refractory period. + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L); + + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 5); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 5L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4); + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + // Within refractory period. + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 5L); +} + +TEST(AnomalyTrackerTest, TestSparseBuckets) { + Alert alert; + alert.set_number_of_buckets(3); + alert.set_refractory_period_in_buckets(3); + alert.set_trigger_if_sum_gt(2); + + DiscreteAnomalyTracker anomaly_tracker(alert); + + // Add bucket #9 + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}}), 9); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 9L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1); + EXPECT_FALSE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L); + + // Add bucket #16 + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 4}}), 16); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 16L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4); + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L); + + // Add bucket #18 + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1); + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + // Within refractory period. + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L); + + // Add bucket #18 again. + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1); + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L); + + // Add bucket #20 + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 3}, {"d", 1}}), 20); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 20L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1); + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L); + + // Add bucket #25 + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"d", 1}}), 25); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 25L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1L); + EXPECT_FALSE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L); + + // Add bucket #28 + anomaly_tracker.addOrUpdateBucket(MockeBucket({{"e", 5}}), 28); + EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 28L); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL); + EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("e")->second, 5L); + EXPECT_TRUE(anomaly_tracker.detectAnomaly()); + anomaly_tracker.declareAndDeclareAnomaly(); + EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 3L); + EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 28L); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index b640bd5bee2a..530d84b420a3 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -16,6 +16,12 @@ package android.app.job; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.util.TimeUtils.formatDuration; import android.annotation.BytesLong; @@ -25,6 +31,8 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.content.ClipData; import android.content.ComponentName; +import android.net.NetworkRequest; +import android.net.NetworkSpecifier; import android.net.Uri; import android.os.BaseBundle; import android.os.Bundle; @@ -56,6 +64,7 @@ public class JobInfo implements Parcelable { NETWORK_TYPE_ANY, NETWORK_TYPE_UNMETERED, NETWORK_TYPE_NOT_ROAMING, + NETWORK_TYPE_CELLULAR, NETWORK_TYPE_METERED, }) @Retention(RetentionPolicy.SOURCE) @@ -69,8 +78,21 @@ public class JobInfo implements Parcelable { public static final int NETWORK_TYPE_UNMETERED = 2; /** This job requires network connectivity that is not roaming. */ public static final int NETWORK_TYPE_NOT_ROAMING = 3; - /** This job requires metered connectivity such as most cellular data networks. */ - public static final int NETWORK_TYPE_METERED = 4; + /** This job requires network connectivity that is a cellular network. */ + public static final int NETWORK_TYPE_CELLULAR = 4; + + /** + * This job requires metered connectivity such as most cellular data + * networks. + * + * @deprecated Cellular networks may be unmetered, or Wi-Fi networks may be + * metered, so this isn't a good way of selecting a specific + * transport. Instead, use {@link #NETWORK_TYPE_CELLULAR} or + * {@link android.net.NetworkRequest.Builder#addTransportType(int)} + * if your job requires a specific network transport. + */ + @Deprecated + public static final int NETWORK_TYPE_METERED = NETWORK_TYPE_CELLULAR; /** Sentinel value indicating that bytes are unknown. */ public static final int NETWORK_BYTES_UNKNOWN = -1; @@ -253,7 +275,7 @@ public class JobInfo implements Parcelable { private final long triggerContentMaxDelay; private final boolean hasEarlyConstraint; private final boolean hasLateConstraint; - private final int networkType; + private final NetworkRequest networkRequest; private final long networkBytes; private final long minLatencyMillis; private final long maxExecutionDelayMillis; @@ -385,10 +407,37 @@ public class JobInfo implements Parcelable { } /** - * The kind of connectivity requirements that the job has. + * Return the basic description of the kind of network this job requires. + * + * @deprecated This method attempts to map {@link #getRequiredNetwork()} + * into the set of simple constants, which results in a loss of + * fidelity. Callers should move to using + * {@link #getRequiredNetwork()} directly. + * @see Builder#setRequiredNetworkType(int) */ + @Deprecated public @NetworkType int getNetworkType() { - return networkType; + if (networkRequest == null) { + return NETWORK_TYPE_NONE; + } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { + return NETWORK_TYPE_UNMETERED; + } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) { + return NETWORK_TYPE_NOT_ROAMING; + } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + return NETWORK_TYPE_CELLULAR; + } else { + return NETWORK_TYPE_ANY; + } + } + + /** + * Return the detailed description of the kind of network this job requires, + * or {@code null} if no specific kind of network is required. + * + * @see Builder#setRequiredNetwork(NetworkRequest) + */ + public @Nullable NetworkRequest getRequiredNetwork() { + return networkRequest; } /** @@ -438,8 +487,7 @@ public class JobInfo implements Parcelable { * job does not recur periodically. */ public long getIntervalMillis() { - final long minInterval = getMinPeriodMillis(); - return intervalMillis >= minInterval ? intervalMillis : minInterval; + return intervalMillis; } /** @@ -447,10 +495,7 @@ public class JobInfo implements Parcelable { * execute at any time in a window of flex length at the end of the period. */ public long getFlexMillis() { - long interval = getIntervalMillis(); - long percentClamp = 5 * interval / 100; - long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, getMinFlexMillis())); - return clampedFlex <= interval ? clampedFlex : interval; + return flexMillis; } /** @@ -459,8 +504,7 @@ public class JobInfo implements Parcelable { * to 30 seconds, minimum is currently 10 seconds. */ public long getInitialBackoffMillis() { - final long minBackoff = getMinBackoffMillis(); - return initialBackoffMillis >= minBackoff ? initialBackoffMillis : minBackoff; + return initialBackoffMillis; } /** @@ -538,7 +582,7 @@ public class JobInfo implements Parcelable { if (hasLateConstraint != j.hasLateConstraint) { return false; } - if (networkType != j.networkType) { + if (!Objects.equals(networkRequest, j.networkRequest)) { return false; } if (networkBytes != j.networkBytes) { @@ -601,7 +645,9 @@ public class JobInfo implements Parcelable { hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay); hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint); hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint); - hashCode = 31 * hashCode + networkType; + if (networkRequest != null) { + hashCode = 31 * hashCode + networkRequest.hashCode(); + } hashCode = 31 * hashCode + Long.hashCode(networkBytes); hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis); hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis); @@ -632,7 +678,11 @@ public class JobInfo implements Parcelable { triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR); triggerContentUpdateDelay = in.readLong(); triggerContentMaxDelay = in.readLong(); - networkType = in.readInt(); + if (in.readInt() != 0) { + networkRequest = NetworkRequest.CREATOR.createFromParcel(in); + } else { + networkRequest = null; + } networkBytes = in.readLong(); minLatencyMillis = in.readLong(); maxExecutionDelayMillis = in.readLong(); @@ -661,7 +711,7 @@ public class JobInfo implements Parcelable { : null; triggerContentUpdateDelay = b.mTriggerContentUpdateDelay; triggerContentMaxDelay = b.mTriggerContentMaxDelay; - networkType = b.mNetworkType; + networkRequest = b.mNetworkRequest; networkBytes = b.mNetworkBytes; minLatencyMillis = b.mMinLatencyMillis; maxExecutionDelayMillis = b.mMaxExecutionDelayMillis; @@ -699,7 +749,12 @@ public class JobInfo implements Parcelable { out.writeTypedArray(triggerContentUris, flags); out.writeLong(triggerContentUpdateDelay); out.writeLong(triggerContentMaxDelay); - out.writeInt(networkType); + if (networkRequest != null) { + out.writeInt(1); + networkRequest.writeToParcel(out, flags); + } else { + out.writeInt(0); + } out.writeLong(networkBytes); out.writeLong(minLatencyMillis); out.writeLong(maxExecutionDelayMillis); @@ -833,7 +888,7 @@ public class JobInfo implements Parcelable { private int mFlags; // Requirements. private int mConstraintFlags; - private int mNetworkType; + private NetworkRequest mNetworkRequest; private long mNetworkBytes = NETWORK_BYTES_UNKNOWN; private ArrayList<TriggerContentUri> mTriggerContentUris; private long mTriggerContentUpdateDelay = -1; @@ -934,24 +989,84 @@ public class JobInfo implements Parcelable { } /** - * Set some description of the kind of network type your job needs to - * have. Not calling this function means the network is not necessary, - * as the default is {@link #NETWORK_TYPE_NONE}. Bear in mind that - * calling this function defines network as a strict requirement for - * your job. If the network requested is not available your job will - * never run. See {@link #setOverrideDeadline(long)} to change this - * behaviour. + * Set basic description of the kind of network your job requires. If + * you need more precise control over network capabilities, see + * {@link #setRequiredNetwork(NetworkRequest)}. + * <p> + * If your job doesn't need a network connection, you don't need to call + * this method, as the default value is {@link #NETWORK_TYPE_NONE}. + * <p> + * Calling this method defines network as a strict requirement for your + * job. If the network requested is not available your job will never + * run. See {@link #setOverrideDeadline(long)} to change this behavior. + * Calling this method will override any requirements previously defined + * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only + * want to call one of these methods. * <p class="note"> - * Note: When your job executes in + * When your job executes in * {@link JobService#onStartJob(JobParameters)}, be sure to use the * specific network returned by {@link JobParameters#getNetwork()}, * otherwise you'll use the default network which may not meet this * constraint. * + * @see #setRequiredNetwork(NetworkRequest) + * @see JobInfo#getNetworkType() * @see JobParameters#getNetwork() */ public Builder setRequiredNetworkType(@NetworkType int networkType) { - mNetworkType = networkType; + if (networkType == NETWORK_TYPE_NONE) { + return setRequiredNetwork(null); + } else { + final NetworkRequest.Builder builder = new NetworkRequest.Builder(); + + // All types require validated Internet + builder.addCapability(NET_CAPABILITY_INTERNET); + builder.addCapability(NET_CAPABILITY_VALIDATED); + builder.removeCapability(NET_CAPABILITY_NOT_VPN); + + if (networkType == NETWORK_TYPE_ANY) { + // No other capabilities + } else if (networkType == NETWORK_TYPE_UNMETERED) { + builder.addCapability(NET_CAPABILITY_NOT_METERED); + } else if (networkType == NETWORK_TYPE_NOT_ROAMING) { + builder.addCapability(NET_CAPABILITY_NOT_ROAMING); + } else if (networkType == NETWORK_TYPE_CELLULAR) { + builder.addTransportType(TRANSPORT_CELLULAR); + } + + return setRequiredNetwork(builder.build()); + } + } + + /** + * Set detailed description of the kind of network your job requires. + * <p> + * If your job doesn't need a network connection, you don't need to call + * this method, as the default is {@code null}. + * <p> + * Calling this method defines network as a strict requirement for your + * job. If the network requested is not available your job will never + * run. See {@link #setOverrideDeadline(long)} to change this behavior. + * Calling this method will override any requirements previously defined + * by {@link #setRequiredNetworkType(int)}; you typically only want to + * call one of these methods. + * <p class="note"> + * When your job executes in + * {@link JobService#onStartJob(JobParameters)}, be sure to use the + * specific network returned by {@link JobParameters#getNetwork()}, + * otherwise you'll use the default network which may not meet this + * constraint. + * + * @param networkRequest The detailed description of the kind of network + * this job requires, or {@code null} if no specific kind of + * network is required. Defining a {@link NetworkSpecifier} + * is only supported for jobs that aren't persisted. + * @see #setRequiredNetworkType(int) + * @see JobInfo#getRequiredNetwork() + * @see JobParameters#getNetwork() + */ + public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) { + mNetworkRequest = networkRequest; return this; } @@ -1140,6 +1255,21 @@ public class JobInfo implements Parcelable { * higher. */ public Builder setPeriodic(long intervalMillis, long flexMillis) { + final long minPeriod = getMinPeriodMillis(); + if (intervalMillis < minPeriod) { + Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job " + + mJobId + " is too small; raising to " + formatDuration(minPeriod)); + intervalMillis = minPeriod; + } + + final long percentClamp = 5 * intervalMillis / 100; + final long minFlex = Math.max(percentClamp, getMinFlexMillis()); + if (flexMillis < minFlex) { + Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId + + " is too small; raising to " + formatDuration(minFlex)); + flexMillis = minFlex; + } + mIsPeriodic = true; mIntervalMillis = intervalMillis; mFlexMillis = flexMillis; @@ -1189,6 +1319,13 @@ public class JobInfo implements Parcelable { */ public Builder setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy) { + final long minBackoff = getMinBackoffMillis(); + if (initialBackoffMillis < minBackoff) { + Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job " + + mJobId + " is too small; raising to " + formatDuration(minBackoff)); + initialBackoffMillis = minBackoff; + } + mBackoffPolicySet = true; mInitialBackoffMillis = initialBackoffMillis; mBackoffPolicy = backoffPolicy; @@ -1213,16 +1350,22 @@ public class JobInfo implements Parcelable { public JobInfo build() { // Allow jobs with no constraints - What am I, a database? if (!mHasEarlyConstraint && !mHasLateConstraint && mConstraintFlags == 0 && - mNetworkType == NETWORK_TYPE_NONE && + mNetworkRequest == null && mTriggerContentUris == null) { throw new IllegalArgumentException("You're trying to build a job with no " + "constraints, this is not allowed."); } // Check that network estimates require network type - if (mNetworkBytes > 0 && mNetworkType == NETWORK_TYPE_NONE) { + if (mNetworkBytes > 0 && mNetworkRequest == null) { throw new IllegalArgumentException( "Can't provide estimated network usage without requiring a network"); } + // We can't serialize network specifiers + if (mIsPersisted && mNetworkRequest != null + && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) { + throw new IllegalArgumentException( + "Network specifiers aren't supported for persistent jobs"); + } // Check that a deadline was not set on a periodic job. if (mIsPeriodic) { if (mMaxExecutionDelayMillis != 0L) { @@ -1257,31 +1400,7 @@ public class JobInfo implements Parcelable { " back-off policy, so calling setBackoffCriteria with" + " setRequiresDeviceIdle is an error."); } - JobInfo job = new JobInfo(this); - if (job.isPeriodic()) { - if (job.intervalMillis != job.getIntervalMillis()) { - StringBuilder builder = new StringBuilder(); - builder.append("Specified interval for ") - .append(String.valueOf(mJobId)) - .append(" is "); - formatDuration(mIntervalMillis, builder); - builder.append(". Clamped to "); - formatDuration(job.getIntervalMillis(), builder); - Log.w(TAG, builder.toString()); - } - if (job.flexMillis != job.getFlexMillis()) { - StringBuilder builder = new StringBuilder(); - builder.append("Specified flex for ") - .append(String.valueOf(mJobId)) - .append(" is "); - formatDuration(mFlexMillis, builder); - builder.append(". Clamped to "); - formatDuration(job.getFlexMillis(), builder); - Log.w(TAG, builder.toString()); - } - } - return job; + return new JobInfo(this); } } - } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index dfd3bbf04d35..26efda108bb6 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -2105,6 +2105,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration break; case DENSITY_DPI_NONE: parts.add("nodpi"); + break; default: parts.add(config.densityDpi + "dpi"); break; diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl index 5857aae5c599..6bbd99e3f20a 100644 --- a/core/java/android/os/ParcelFileDescriptor.aidl +++ b/core/java/android/os/ParcelFileDescriptor.aidl @@ -17,4 +17,4 @@ package android.os; -parcelable ParcelFileDescriptor; +parcelable ParcelFileDescriptor cpp_header "android/os/parcel_file_descriptor.h"; diff --git a/core/java/android/os/ParcelUuid.aidl b/core/java/android/os/ParcelUuid.aidl index f7e080ac5901..6f3629796bde 100644 --- a/core/java/android/os/ParcelUuid.aidl +++ b/core/java/android/os/ParcelUuid.aidl @@ -16,4 +16,4 @@ package android.os; -parcelable ParcelUuid; +parcelable ParcelUuid cpp_header "android/os/parcel_uuid.h"; diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index d5820b67a8ae..02c82d71d5bd 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -16,11 +16,11 @@ package android.os; import android.animation.ValueAnimator; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.app.ActivityManager; import android.app.ActivityThread; -import android.app.ApplicationErrorReport; import android.app.IActivityManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -75,6 +75,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.HashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicInteger; /** @@ -163,12 +165,14 @@ public final class StrictMode { private static final String CLEARTEXT_PROPERTY = "persist.sys.strictmode.clear"; /** - * Quick feature-flag that can be used to disable the defaults provided by - * {@link #initThreadDefaults(ApplicationInfo)} and - * {@link #initVmDefaults(ApplicationInfo)}. + * Quick feature-flag that can be used to disable the defaults provided by {@link + * #initThreadDefaults(ApplicationInfo)} and {@link #initVmDefaults(ApplicationInfo)}. */ private static final boolean DISABLE = false; + // Only apply VM penalties for the same violation at this interval. + private static final long MIN_VM_INTERVAL_MS = 1000; + // Only log a duplicate stack trace to the logs every second. private static final long MIN_LOG_INTERVAL_MS = 1000; @@ -374,6 +378,32 @@ public final class StrictMode { private static volatile ViolationLogger sLogger = LOGCAT_LOGGER; + private static final ThreadLocal<OnThreadViolationListener> sThreadViolationListener = + new ThreadLocal<>(); + private static final ThreadLocal<Executor> sThreadViolationExecutor = new ThreadLocal<>(); + + /** + * When #{@link ThreadPolicy.Builder#penaltyListener} is enabled, the listener is called on the + * provided executor when a Thread violation occurs. + * + * @hide + */ + public interface OnThreadViolationListener { + /** Called on a thread policy violation. */ + void onThreadViolation(Violation v); + } + + /** + * When #{@link VmPolicy.Builder#penaltyListener} is enabled, the listener is called on the + * provided executor when a VM violation occurs. + * + * @hide + */ + public interface OnVmViolationListener { + /** Called on a VM policy violation. */ + void onVmViolation(Violation v); + } + /** {@hide} */ @TestApi public static void setViolationLogger(ViolationLogger listener) { @@ -403,12 +433,16 @@ public final class StrictMode { */ public static final class ThreadPolicy { /** The default, lax policy which doesn't catch anything. */ - public static final ThreadPolicy LAX = new ThreadPolicy(0); + public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null); final int mask; + final OnThreadViolationListener mListener; + final Executor mCallbackExecutor; - private ThreadPolicy(int mask) { + private ThreadPolicy(int mask, OnThreadViolationListener listener, Executor executor) { this.mask = mask; + mListener = listener; + mCallbackExecutor = executor; } @Override @@ -436,6 +470,8 @@ public final class StrictMode { */ public static final class Builder { private int mMask = 0; + private OnThreadViolationListener mListener; + private Executor mExecutor; /** * Create a Builder that detects nothing and has no violations. (but note that {@link @@ -601,6 +637,22 @@ public final class StrictMode { return enable(PENALTY_DROPBOX); } + /** + * Call #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified + * executor every violation. + * + * @hide + */ + public Builder penaltyListener( + @NonNull OnThreadViolationListener listener, @NonNull Executor executor) { + if (executor == null) { + throw new NullPointerException("executor must not be null"); + } + mListener = listener; + mExecutor = executor; + return this; + } + private Builder enable(int bit) { mMask |= bit; return this; @@ -620,7 +672,8 @@ public final class StrictMode { public ThreadPolicy build() { // If there are detection bits set but no violation bits // set, enable simple logging. - if (mMask != 0 + if (mListener == null + && mMask != 0 && (mMask & (PENALTY_DEATH | PENALTY_LOG @@ -629,7 +682,7 @@ public final class StrictMode { == 0) { penaltyLog(); } - return new ThreadPolicy(mMask); + return new ThreadPolicy(mMask, mListener, mExecutor); } } } @@ -641,19 +694,27 @@ public final class StrictMode { */ public static final class VmPolicy { /** The default, lax policy which doesn't catch anything. */ - public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP); + public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null); final int mask; + final OnVmViolationListener mListener; + final Executor mCallbackExecutor; // Map from class to max number of allowed instances in memory. final HashMap<Class, Integer> classInstanceLimit; - private VmPolicy(int mask, HashMap<Class, Integer> classInstanceLimit) { + private VmPolicy( + int mask, + HashMap<Class, Integer> classInstanceLimit, + OnVmViolationListener listener, + Executor executor) { if (classInstanceLimit == null) { throw new NullPointerException("classInstanceLimit == null"); } this.mask = mask; this.classInstanceLimit = classInstanceLimit; + mListener = listener; + mCallbackExecutor = executor; } @Override @@ -681,6 +742,8 @@ public final class StrictMode { */ public static final class Builder { private int mMask; + private OnVmViolationListener mListener; + private Executor mExecutor; private HashMap<Class, Integer> mClassInstanceLimit; // null until needed private boolean mClassInstanceLimitNeedCow = false; // need copy-on-write @@ -694,6 +757,8 @@ public final class StrictMode { mMask = base.mask; mClassInstanceLimitNeedCow = true; mClassInstanceLimit = base.classInstanceLimit; + mListener = base.mListener; + mExecutor = base.mCallbackExecutor; } /** @@ -910,6 +975,21 @@ public final class StrictMode { return enable(PENALTY_DROPBOX); } + /** + * Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation. + * + * @hide + */ + public Builder penaltyListener( + @NonNull OnVmViolationListener listener, @NonNull Executor executor) { + if (executor == null) { + throw new NullPointerException("executor must not be null"); + } + mListener = listener; + mExecutor = executor; + return this; + } + private Builder enable(int bit) { mMask |= bit; return this; @@ -929,7 +1009,8 @@ public final class StrictMode { public VmPolicy build() { // If there are detection bits set but no violation bits // set, enable simple logging. - if (mMask != 0 + if (mListener == null + && mMask != 0 && (mMask & (PENALTY_DEATH | PENALTY_LOG @@ -940,7 +1021,9 @@ public final class StrictMode { } return new VmPolicy( mMask, - mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP); + mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP, + mListener, + mExecutor); } } } @@ -973,6 +1056,8 @@ public final class StrictMode { */ public static void setThreadPolicy(final ThreadPolicy policy) { setThreadPolicyMask(policy.mask); + sThreadViolationListener.set(policy.mListener); + sThreadViolationExecutor.set(policy.mCallbackExecutor); } /** @hide */ @@ -1029,7 +1114,10 @@ public final class StrictMode { // introduce VmPolicy cleanly) but this isn't particularly // optimal for users who might call this method often. This // should be in a thread-local and not allocate on each call. - return new ThreadPolicy(getThreadPolicyMask()); + return new ThreadPolicy( + getThreadPolicyMask(), + sThreadViolationListener.get(), + sThreadViolationExecutor.get()); } /** @@ -1042,7 +1130,10 @@ public final class StrictMode { * end of a block */ public static ThreadPolicy allowThreadDiskWrites() { - return new ThreadPolicy(allowThreadDiskWritesMask()); + return new ThreadPolicy( + allowThreadDiskWritesMask(), + sThreadViolationListener.get(), + sThreadViolationExecutor.get()); } /** @hide */ @@ -1063,7 +1154,10 @@ public final class StrictMode { * @return the old policy, to be passed to setThreadPolicy to restore the policy. */ public static ThreadPolicy allowThreadDiskReads() { - return new ThreadPolicy(allowThreadDiskReadsMask()); + return new ThreadPolicy( + allowThreadDiskReadsMask(), + sThreadViolationListener.get(), + sThreadViolationExecutor.get()); } /** @hide */ @@ -1076,16 +1170,27 @@ public final class StrictMode { return oldPolicyMask; } + private static ThreadPolicy allowThreadViolations() { + ThreadPolicy oldPolicy = getThreadPolicy(); + setThreadPolicyMask(0); + return oldPolicy; + } + + private static VmPolicy allowVmViolations() { + VmPolicy oldPolicy = getVmPolicy(); + sVmPolicy = VmPolicy.LAX; + return oldPolicy; + } + /** - * Determine if the given app is "bundled" as part of the system image. - * These bundled apps are developed in lock-step with the OS, and they - * aren't updated outside of an OTA, so we want to chase any - * {@link StrictMode} regressions by enabling detection when running on - * {@link Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds. - * <p> - * Unbundled apps included in the system image are expected to detect and - * triage their own {@link StrictMode} issues separate from the OS release - * process, which is why we don't enable them here. + * Determine if the given app is "bundled" as part of the system image. These bundled apps are + * developed in lock-step with the OS, and they aren't updated outside of an OTA, so we want to + * chase any {@link StrictMode} regressions by enabling detection when running on {@link + * Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds. + * + * <p>Unbundled apps included in the system image are expected to detect and triage their own + * {@link StrictMode} issues separate from the OS release process, which is why we don't enable + * them here. * * @hide */ @@ -1122,8 +1227,8 @@ public final class StrictMode { */ public static void initThreadDefaults(ApplicationInfo ai) { final ThreadPolicy.Builder builder = new ThreadPolicy.Builder(); - final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion - : Build.VERSION_CODES.CUR_DEVELOPMENT; + final int targetSdkVersion = + (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT; // Starting in HC, we don't allow network usage on the main thread if (targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { @@ -1162,8 +1267,8 @@ public final class StrictMode { */ public static void initVmDefaults(ApplicationInfo ai) { final VmPolicy.Builder builder = new VmPolicy.Builder(); - final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion - : Build.VERSION_CODES.CUR_DEVELOPMENT; + final int targetSdkVersion = + (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT; // Starting in N, we don't allow file:// Uri exposure if (targetSdkVersion >= Build.VERSION_CODES.N) { @@ -1204,7 +1309,9 @@ public final class StrictMode { sVmPolicy.mask | DETECT_VM_FILE_URI_EXPOSURE | PENALTY_DEATH_ON_FILE_URI_EXPOSURE, - sVmPolicy.classInstanceLimit); + sVmPolicy.classInstanceLimit, + sVmPolicy.mListener, + sVmPolicy.mCallbackExecutor); } /** @@ -1219,7 +1326,9 @@ public final class StrictMode { sVmPolicy.mask & ~(DETECT_VM_FILE_URI_EXPOSURE | PENALTY_DEATH_ON_FILE_URI_EXPOSURE), - sVmPolicy.classInstanceLimit); + sVmPolicy.classInstanceLimit, + sVmPolicy.mListener, + sVmPolicy.mCallbackExecutor); } /** @@ -1409,7 +1518,7 @@ public final class StrictMode { // go into this immediate mode? if (looper == null || (info.mPolicy & THREAD_PENALTY_MASK) == PENALTY_DEATH) { info.durationMillis = -1; // unknown (redundant, already set) - handleViolation(info); + onThreadPolicyViolation(info); return; } @@ -1447,30 +1556,28 @@ public final class StrictMode { THREAD_HANDLER .get() .postAtFrontOfQueue( - new Runnable() { - public void run() { - long loopFinishTime = SystemClock.uptimeMillis(); - - // Note: we do this early, before handling the - // violation below, as handling the violation - // may include PENALTY_DEATH and we don't want - // to keep the red border on. - if (windowManager != null) { - try { - windowManager.showStrictModeViolation(false); - } catch (RemoteException unused) { - } + () -> { + long loopFinishTime = SystemClock.uptimeMillis(); + + // Note: we do this early, before handling the + // violation below, as handling the violation + // may include PENALTY_DEATH and we don't want + // to keep the red border on. + if (windowManager != null) { + try { + windowManager.showStrictModeViolation(false); + } catch (RemoteException unused) { } + } - for (int n = 0; n < records.size(); ++n) { - ViolationInfo v = records.get(n); - v.violationNumThisLoop = n + 1; - v.durationMillis = - (int) (loopFinishTime - v.violationUptimeMillis); - handleViolation(v); - } - records.clear(); + for (int n = 0; n < records.size(); ++n) { + ViolationInfo v = records.get(n); + v.violationNumThisLoop = n + 1; + v.durationMillis = + (int) (loopFinishTime - v.violationUptimeMillis); + onThreadPolicyViolation(v); } + records.clear(); }); } @@ -1479,13 +1586,13 @@ public final class StrictMode { // violation fired and now (after the violating code ran) due // to people who push/pop temporary policy in regions of code, // hence the policy being passed around. - void handleViolation(final ViolationInfo info) { - if (LOG_V) Log.d(TAG, "handleViolation; policy=" + info.mPolicy); + void onThreadPolicyViolation(final ViolationInfo info) { + if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; policy=" + info.mPolicy); if (info.penaltyEnabled(PENALTY_GATHER)) { ArrayList<ViolationInfo> violations = gatheredViolations.get(); if (violations == null) { - violations = new ArrayList<ViolationInfo>(1); + violations = new ArrayList<>(1); gatheredViolations.set(violations); } for (ViolationInfo previous : violations) { @@ -1519,6 +1626,8 @@ public final class StrictMode { sLogger.log(info); } + final Violation violation = info.mViolation; + // The violationMaskSubset, passed to ActivityManager, is a // subset of the original StrictMode policy bitmask, with // only the bit violated and penalty bits to be executed @@ -1552,13 +1661,30 @@ public final class StrictMode { } if ((info.getPolicyMask() & PENALTY_DEATH) != 0) { - executeDeathPenalty(info); + throw new RuntimeException("StrictMode ThreadPolicy violation", violation); } - } - } - private static void executeDeathPenalty(ViolationInfo info) { - throw new RuntimeException("StrictMode death penalty", info.mViolation); + // penaltyDeath will cause penaltyCallback to no-op since we cannot guarantee the + // executor finishes before crashing. + final OnThreadViolationListener listener = sThreadViolationListener.get(); + final Executor executor = sThreadViolationExecutor.get(); + if (listener != null && executor != null) { + try { + executor.execute( + () -> { + // Lift violated policy to prevent infinite recursion. + ThreadPolicy oldPolicy = allowThreadViolations(); + try { + listener.onThreadViolation(violation); + } finally { + setThreadPolicy(oldPolicy); + } + }); + } catch (RejectedExecutionException e) { + Log.e(TAG, "ThreadPolicy penaltyCallback failed", e); + } + } + } } /** @@ -1736,9 +1862,8 @@ public final class StrictMode { * #setThreadPolicy}. */ public static void enableDefaults() { - StrictMode.setThreadPolicy( - new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); - StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); + setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); + setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build()); } /** @hide */ @@ -1857,11 +1982,11 @@ public final class StrictMode { } /** @hide */ - public static void onVmPolicyViolation(Violation originStack, boolean forceDeath) { + public static void onVmPolicyViolation(Violation violation, boolean forceDeath) { final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0; final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath; final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0; - final ViolationInfo info = new ViolationInfo(originStack, sVmPolicy.mask); + final ViolationInfo info = new ViolationInfo(violation, sVmPolicy.mask); // Erase stuff not relevant for process-wide violations info.numAnimationsRunning = 0; @@ -1870,37 +1995,37 @@ public final class StrictMode { final Integer fingerprint = info.hashCode(); final long now = SystemClock.uptimeMillis(); - long lastViolationTime = 0; + long lastViolationTime; long timeSinceLastViolationMillis = Long.MAX_VALUE; synchronized (sLastVmViolationTime) { if (sLastVmViolationTime.containsKey(fingerprint)) { lastViolationTime = sLastVmViolationTime.get(fingerprint); timeSinceLastViolationMillis = now - lastViolationTime; } - if (timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) { + if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) { sLastVmViolationTime.put(fingerprint, now); } } - - if (penaltyLog && sLogger != null) { - sLogger.log(info); + if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) { + // Rate limit all penalties. + return; } - if (penaltyLog && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) { + + if (penaltyLog && sLogger != null && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) { sLogger.log(info); } int violationMaskSubset = PENALTY_DROPBOX | (ALL_VM_DETECT_BITS & sVmPolicy.mask); - if (penaltyDropbox && !penaltyDeath) { - // Common case for userdebug/eng builds. If no death and - // just dropboxing, we can do the ActivityManager call - // asynchronously. - dropboxViolationAsync(violationMaskSubset, info); - return; - } - - if (penaltyDropbox && lastViolationTime == 0) { - handleApplicationStrictModeViolation(violationMaskSubset, info); + if (penaltyDropbox) { + if (penaltyDeath) { + handleApplicationStrictModeViolation(violationMaskSubset, info); + } else { + // Common case for userdebug/eng builds. If no death and + // just dropboxing, we can do the ActivityManager call + // asynchronously. + dropboxViolationAsync(violationMaskSubset, info); + } } if (penaltyDeath) { @@ -1908,6 +2033,26 @@ public final class StrictMode { Process.killProcess(Process.myPid()); System.exit(10); } + + // If penaltyDeath, we can't guarantee this callback finishes before the process dies for + // all executors. penaltyDeath supersedes penaltyCallback. + if (sVmPolicy.mListener != null && sVmPolicy.mCallbackExecutor != null) { + final OnVmViolationListener listener = sVmPolicy.mListener; + try { + sVmPolicy.mCallbackExecutor.execute( + () -> { + // Lift violated policy to prevent infinite recursion. + VmPolicy oldPolicy = allowVmViolations(); + try { + listener.onVmViolation(violation); + } finally { + setVmPolicy(oldPolicy); + } + }); + } catch (RejectedExecutionException e) { + Log.e(TAG, "VmPolicy penaltyCallback failed", e); + } + } } /** Called from Parcel.writeNoException() */ diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java index b3d76d73ac34..c52c22d60ade 100644 --- a/core/java/android/os/SystemClock.java +++ b/core/java/android/os/SystemClock.java @@ -16,12 +16,18 @@ package android.os; +import android.annotation.NonNull; import android.app.IAlarmManager; import android.content.Context; import android.util.Slog; import dalvik.annotation.optimization.CriticalNative; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; + /** * Core timekeeping facilities. * @@ -168,6 +174,31 @@ public final class SystemClock { native public static long uptimeMillis(); /** + * Return {@link Clock} that starts at system boot, not counting time spent + * in deep sleep. + */ + public static @NonNull Clock uptimeMillisClock() { + return new Clock() { + @Override + public ZoneId getZone() { + return ZoneOffset.UTC; + } + @Override + public Clock withZone(ZoneId zone) { + throw new UnsupportedOperationException(); + } + @Override + public long millis() { + return SystemClock.uptimeMillis(); + } + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis()); + } + }; + } + + /** * Returns milliseconds since boot, including time spent in sleep. * * @return elapsed milliseconds since boot. @@ -176,6 +207,31 @@ public final class SystemClock { native public static long elapsedRealtime(); /** + * Return {@link Clock} that starts at system boot, including time spent in + * sleep. + */ + public static @NonNull Clock elapsedRealtimeClock() { + return new Clock() { + @Override + public ZoneId getZone() { + return ZoneOffset.UTC; + } + @Override + public Clock withZone(ZoneId zone) { + throw new UnsupportedOperationException(); + } + @Override + public long millis() { + return SystemClock.elapsedRealtime(); + } + @Override + public Instant instant() { + return Instant.ofEpochMilli(millis()); + } + }; + } + + /** * Returns nanoseconds since boot, including time spent in sleep. * * @return elapsed nanoseconds since boot. diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index 0c2e4b7ada26..cd233b831522 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -97,6 +97,10 @@ public abstract class EuiccService extends Service { public static final String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES"; + /** Ask the user to input carrier confirmation code. */ + public static final String ACTION_RESOLVE_CONFIRMATION_CODE = + "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE"; + /** Intent extra set for resolution requests containing the package name of the calling app. */ public static final String EXTRA_RESOLUTION_CALLING_PACKAGE = "android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE"; @@ -105,6 +109,8 @@ public abstract class EuiccService extends Service { public static final int RESULT_OK = 0; /** Result code indicating that an active SIM must be deactivated to perform the operation. */ public static final int RESULT_MUST_DEACTIVATE_SIM = -1; + /** Result code indicating that the user must input a carrier confirmation code. */ + public static final int RESULT_NEED_CONFIRMATION_CODE = -2; // New predefined codes should have negative values. /** Start of implementation-specific error results. */ @@ -119,10 +125,13 @@ public abstract class EuiccService extends Service { RESOLUTION_ACTIONS = new ArraySet<>(); RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM); RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES); + RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE); } /** Boolean extra for resolution actions indicating whether the user granted consent. */ public static final String RESOLUTION_EXTRA_CONSENT = "consent"; + /** String extra for resolution actions indicating the carrier confirmation code. */ + public static final String RESOLUTION_EXTRA_CONFIRMATION_CODE = "confirmation_code"; private final IEuiccService.Stub mStubWrapper; diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java index 3e992ec36426..080482b0dae9 100644 --- a/core/java/android/service/notification/ConditionProviderService.java +++ b/core/java/android/service/notification/ConditionProviderService.java @@ -18,6 +18,7 @@ package android.service.notification; import android.annotation.SdkConstant; import android.annotation.SystemApi; +import android.app.ActivityManager; import android.app.INotificationManager; import android.app.Service; import android.content.ComponentName; @@ -56,6 +57,8 @@ import android.util.Log; * </meta-data> * </service></pre> * + * <p> Condition providers cannot be bound by the system on + * {@link ActivityManager#isLowRamDevice() low ram} devices</p> */ public abstract class ConditionProviderService extends Service { private final String TAG = ConditionProviderService.class.getSimpleName() diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 08d3118b09f6..dac663e765ea 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.app.ActivityManager; import android.app.INotificationManager; import android.app.Notification; import android.app.Notification.Builder; @@ -82,6 +83,8 @@ import java.util.List; * method is the <i>only</i> one that is safe to call before {@link #onListenerConnected()} * or after {@link #onListenerDisconnected()}. * </p> + * <p> Notification listeners cannot get notification access or be bound by the system on + * {@link ActivityManager#isLowRamDevice() low ram} devices</p> */ public abstract class NotificationListenerService extends Service { diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 2b03ed6c3ae1..cc4a0b60dd0a 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -340,6 +340,14 @@ public class TimeUtils { } /** @hide Just for debugging; not internationalized. */ + public static String formatDuration(long duration) { + synchronized (sFormatSync) { + int len = formatDurationLocked(duration, 0); + return new String(sFormatStr, 0, len); + } + } + + /** @hide Just for debugging; not internationalized. */ public static void formatDuration(long duration, PrintWriter pw) { formatDuration(duration, pw, 0); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c043dcacf11f..e12c0b0d93b8 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -14218,6 +14218,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void setScaleX(float scaleX) { if (scaleX != getScaleX()) { + requireIsFinite(scaleX, "scaleX"); invalidateViewProperty(true, false); mRenderNode.setScaleX(scaleX); invalidateViewProperty(false, true); @@ -14254,6 +14255,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void setScaleY(float scaleY) { if (scaleY != getScaleY()) { + requireIsFinite(scaleY, "scaleY"); invalidateViewProperty(true, false); mRenderNode.setScaleY(scaleY); invalidateViewProperty(false, true); @@ -14803,6 +14805,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + private static void requireIsFinite(float transform, String propertyName) { + if (Float.isNaN(transform)) { + throw new IllegalArgumentException("Cannot set '" + propertyName + "' to Float.NaN"); + } + if (Float.isInfinite(transform)) { + throw new IllegalArgumentException("Cannot set '" + propertyName + "' to infinity"); + } + } + /** * The visual x position of this view, in pixels. This is equivalent to the * {@link #setTranslationX(float) translationX} property plus the current @@ -14889,6 +14900,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void setElevation(float elevation) { if (elevation != getElevation()) { + requireIsFinite(elevation, "elevation"); invalidateViewProperty(true, false); mRenderNode.setElevation(elevation); invalidateViewProperty(false, true); @@ -14981,6 +14993,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public void setTranslationZ(float translationZ) { if (translationZ != getTranslationZ()) { + requireIsFinite(translationZ, "translationZ"); invalidateViewProperty(true, false); mRenderNode.setTranslationZ(translationZ); invalidateViewProperty(false, true); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 2ec64a5ecdc4..f2483c0a71e5 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -3639,6 +3639,7 @@ public class BatteryStatsImpl extends BatteryStats { public void addIsolatedUidLocked(int isolatedUid, int appUid) { mIsolatedUids.put(isolatedUid, appUid); + StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1); } /** @@ -3659,9 +3660,11 @@ public class BatteryStatsImpl extends BatteryStats { * @see #scheduleRemoveIsolatedUidLocked(int, int) */ public void removeIsolatedUidLocked(int isolatedUid) { - mIsolatedUids.delete(isolatedUid); - mKernelUidCpuTimeReader.removeUid(isolatedUid); - mKernelUidCpuFreqTimeReader.removeUid(isolatedUid); + StatsLog.write( + StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0); + mIsolatedUids.delete(isolatedUid); + mKernelUidCpuTimeReader.removeUid(isolatedUid); + mKernelUidCpuFreqTimeReader.removeUid(isolatedUid); } public int mapUid(int uid) { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7cc43a7b2715..1879ab719bb7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -123,6 +123,9 @@ be sent during a change to the audio output device. --> <bool name="config_sendAudioBecomingNoisy">true</bool> + <!-- Flag to disable all transition animations --> + <bool name="config_disableTransitionAnimation">false</bool> + <!-- The duration (in milliseconds) of a short animation. --> <integer name="config_shortAnimTime">200</integer> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d630839db81d..e476d893b900 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -252,6 +252,7 @@ <java-symbol type="bool" name="config_bluetooth_hfp_inband_ringing_support" /> <java-symbol type="bool" name="config_cellBroadcastAppLinks" /> <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" /> + <java-symbol type="bool" name="config_disableTransitionAnimation" /> <java-symbol type="bool" name="config_enableAutoPowerModes" /> <java-symbol type="integer" name="config_autoPowerModeThresholdAngle" /> <java-symbol type="integer" name="config_autoPowerModeAnyMotionSensor" /> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index f7e0b46ca265..161a0c2997e0 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -258,6 +258,11 @@ applications that come with the platform <permission name="android.permission.WRITE_SECURE_SETTINGS"/> </privapp-permissions> + <privapp-permissions package="com.android.settings.intelligence"> + <permission name="android.permission.READ_SEARCH_INDEXABLES"/> + <permission name="android.permission.MODIFY_PHONE_STATE"/> + </privapp-permissions> + <privapp-permissions package="com.android.sharedstoragebackup"> <permission name="android.permission.WRITE_MEDIA_STORAGE"/> </privapp-permissions> diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 5484cf048468..251b2e773cfb 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -98,6 +98,9 @@ cc_library { enabled: true, }, }, + sanitize: { + blacklist: "libandroidfw_blacklist.txt", + }, } common_test_libs = [ diff --git a/libs/androidfw/libandroidfw_blacklist.txt b/libs/androidfw/libandroidfw_blacklist.txt new file mode 100644 index 000000000000..dd17e4d5b1e8 --- /dev/null +++ b/libs/androidfw/libandroidfw_blacklist.txt @@ -0,0 +1 @@ +src:*/ResourceTypes.cpp diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index b7f1fb21d8d7..530e82e65a28 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -31,11 +31,7 @@ namespace android { namespace uirenderer { Extensions::Extensions() { - if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { - // Extensions class is used only by OpenGL and SkiaGL pipelines - // The code below will crash for SkiaVulkan, because OpenGL is not initialized - // TODO: instantiate Extensions class only for OpenGL pipeline - // TODO: remove the only usage of Extensions by SkiaGL in SkiaOpenGLReadback::copyImageInto + if (Properties::isSkiaEnabled()) { return; } const char* version = (const char*)glGetString(GL_VERSION); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp index 6db57cade19b..288d0395370c 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp @@ -66,14 +66,8 @@ CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4 break; } - /* Ideally, we would call grContext->caps()->isConfigRenderable(...). We - * currently can't do that since some devices (i.e. SwiftShader) supports all - * the appropriate half float extensions, but only allow the buffer to be read - * back as full floats. We can relax this extension if Skia implements support - * for reading back float buffers (skbug.com/6945). - */ if (pixelConfig == kRGBA_half_GrPixelConfig && - !DeviceInfo::get()->extensions().hasRenderableFloatTextures()) { + !grContext->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false)) { ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported"); return CopyResult::DestinationInvalid; } diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java index fa50943343e9..478297f2a3c9 100644 --- a/obex/javax/obex/ObexHelper.java +++ b/obex/javax/obex/ObexHelper.java @@ -80,6 +80,9 @@ public final class ObexHelper { // The minimum allowed max packet size is 255 according to the OBEX specification public static final int LOWER_LIMIT_MAX_PACKET_SIZE = 255; + // The length of OBEX Byte Sequency Header Id according to the OBEX specification + public static final int OBEX_BYTE_SEQ_HEADER_LEN = 0x03; + /** * Temporary workaround to be able to push files to Windows 7. * TODO: Should be removed as soon as Microsoft updates their driver. @@ -205,12 +208,15 @@ public final class ObexHelper { case 0x40: boolean trimTail = true; index++; - length = 0xFF & headerArray[index]; - length = length << 8; - index++; - length += 0xFF & headerArray[index]; - length -= 3; - index++; + length = ((0xFF & headerArray[index]) << 8) + + (0xFF & headerArray[index + 1]); + index += 2; + if (length <= OBEX_BYTE_SEQ_HEADER_LEN) { + Log.e(TAG, "Remote sent an OBEX packet with " + + "incorrect header length = " + length); + break; + } + length -= OBEX_BYTE_SEQ_HEADER_LEN; value = new byte[length]; System.arraycopy(headerArray, index, value, 0, length); if (length == 0 || (length > 0 && (value[length - 1] != 0))) { diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java index 386c4e0fc466..88f0d2be311f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java @@ -20,14 +20,16 @@ public abstract class AbstractPreferenceController { * Displays preference in this controller. */ public void displayPreference(PreferenceScreen screen) { + final String prefKey = getPreferenceKey(); if (isAvailable()) { + setVisible(screen, prefKey, true /* visible */); if (this instanceof Preference.OnPreferenceChangeListener) { - final Preference preference = screen.findPreference(getPreferenceKey()); + final Preference preference = screen.findPreference(prefKey); preference.setOnPreferenceChangeListener( (Preference.OnPreferenceChangeListener) this); } } else { - removePreference(screen, getPreferenceKey()); + setVisible(screen, prefKey, false /* visible */); } } @@ -59,13 +61,6 @@ public abstract class AbstractPreferenceController { public abstract String getPreferenceKey(); /** - * Removes preference from screen. - */ - protected final void removePreference(PreferenceScreen screen, String key) { - findAndRemovePreference(screen, key); - } - - /** * Show/hide a preference. */ protected final void setVisible(PreferenceGroup group, String key, boolean isVisible) { @@ -74,25 +69,4 @@ public abstract class AbstractPreferenceController { pref.setVisible(isVisible); } } - - // finds the preference recursively and removes it from its parent - private boolean findAndRemovePreference(PreferenceGroup prefGroup, String key) { - final int preferenceCount = prefGroup.getPreferenceCount(); - for (int i = 0; i < preferenceCount; i++) { - final Preference preference = prefGroup.getPreference(i); - final String curKey = preference.getKey(); - - if (curKey != null && curKey.equals(key)) { - return prefGroup.removePreference(preference); - } - - if (preference instanceof PreferenceGroup) { - if (findAndRemovePreference((PreferenceGroup) preference, key)) { - return true; - } - } - } - return false; - } - } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java index ff7536adb808..90f14ef4c32c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java @@ -18,18 +18,20 @@ package com.android.settingslib.deviceinfo; import android.content.Context; import android.os.Build; +import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; -import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.core.AbstractPreferenceController; /** * Preference controller for displaying device serial number. Wraps {@link Build#getSerial()}. */ public class AbstractSerialNumberPreferenceController extends AbstractPreferenceController { - private static final String KEY_SERIAL_NUMBER = "serial_number"; + + @VisibleForTesting + static final String KEY_SERIAL_NUMBER = "serial_number"; private final String mSerialNumber; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java index c08bc2a193d3..26970e1c7aff 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java @@ -16,17 +16,10 @@ package com.android.settingslib.core; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceGroup; -import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceScreen; import com.android.settingslib.TestConfig; @@ -39,134 +32,58 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowApplication; @RunWith(RobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class AbstractPreferenceControllerTest { @Mock - private Context mContext; - @Mock private PreferenceScreen mScreen; + private Context mContext; private Preference mPreference; private TestPrefController mTestPrefController; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mPreference = new Preference(RuntimeEnvironment.application); + mContext = RuntimeEnvironment.application; + mPreference = new Preference(mContext); mPreference.setKey(TestPrefController.KEY_PREF); + when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference); mTestPrefController = new TestPrefController(mContext); } @Test - public void removeExistingPref_shouldBeRemoved() { - when(mScreen.getPreferenceCount()).thenReturn(1); - when(mScreen.getPreference(0)).thenReturn(mPreference); - - mTestPrefController.removePreference(mScreen, TestPrefController.KEY_PREF); - - verify(mScreen).removePreference(mPreference); - } - - @Test - public void removeNonExistingPref_shouldNotRemoveAnything() { - mTestPrefController.removePreference(mScreen, TestPrefController.KEY_PREF); - - verify(mScreen, never()).removePreference(any(Preference.class)); - } - - @Test public void displayPref_ifAvailable() { mTestPrefController.isAvailable = true; mTestPrefController.displayPreference(mScreen); - verify(mScreen, never()).removePreference(any(Preference.class)); + assertThat(mPreference.isVisible()).isTrue(); } @Test public void setVisible_prefIsVisible_shouldSetToVisible() { - when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference); - mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, true /* visible */); + assertThat(mPreference.isVisible()).isTrue(); } @Test public void setVisible_prefNotVisible_shouldSetToInvisible() { - when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference); - mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, false /* visible */); + assertThat(mPreference.isVisible()).isFalse(); } @Test public void doNotDisplayPref_ifNotAvailable() { - when(mScreen.getPreferenceCount()).thenReturn(1); - when(mScreen.getPreference(0)).thenReturn(mPreference); mTestPrefController.isAvailable = false; mTestPrefController.displayPreference(mScreen); - verify(mScreen).removePreference(any(Preference.class)); - } - - @Test - public void removePreference_shouldRemoveRecursively() { - final Context context = ShadowApplication.getInstance().getApplicationContext(); - final PreferenceManager preferenceManager = mock(PreferenceManager.class); - // Top level - PreferenceScreen prefRoot = spy(new PreferenceScreen(context, null)); - when(prefRoot.getPreferenceManager()).thenReturn(preferenceManager); - Preference pref1 = mock(Preference.class); - when(pref1.getKey()).thenReturn("key1"); - PreferenceGroup prefGroup2 = spy(new PreferenceScreen(context, null)); - when(prefGroup2.getPreferenceManager()).thenReturn(preferenceManager); - when(prefGroup2.getKey()).thenReturn("group2"); - Preference pref3 = mock(Preference.class); - when(pref3.getKey()).thenReturn("key3"); - PreferenceGroup prefGroup4 = spy(new PreferenceScreen(context, null)); - when(prefGroup4.getPreferenceManager()).thenReturn(preferenceManager); - when(prefGroup4.getKey()).thenReturn("group4"); - prefRoot.addPreference(pref1); - prefRoot.addPreference(prefGroup2); - prefRoot.addPreference(pref3); - prefRoot.addPreference(prefGroup4); - - // 2nd level - Preference pref21 = mock(Preference.class); - when(pref21.getKey()).thenReturn("key21"); - Preference pref22 = mock(Preference.class); - when(pref22.getKey()).thenReturn("key22"); - prefGroup2.addPreference(pref21); - prefGroup2.addPreference(pref22); - PreferenceGroup prefGroup41 = spy(new PreferenceScreen(context, null)); - when(prefGroup41.getKey()).thenReturn("group41"); - when(prefGroup41.getPreferenceManager()).thenReturn(preferenceManager); - Preference pref42 = mock(Preference.class); - when(pref42.getKey()).thenReturn("key42"); - prefGroup4.addPreference(prefGroup41); - prefGroup4.addPreference(pref42); - - // 3rd level - Preference pref411 = mock(Preference.class); - when(pref411.getKey()).thenReturn("key411"); - Preference pref412 = mock(Preference.class); - when(pref412.getKey()).thenReturn("key412"); - prefGroup41.addPreference(pref411); - prefGroup41.addPreference(pref412); - - mTestPrefController.removePreference(prefRoot, "key1"); - verify(prefRoot).removePreference(pref1); - - mTestPrefController.removePreference(prefRoot, "key411"); - verify(prefGroup41).removePreference(pref411); - - mTestPrefController.removePreference(prefRoot, "group41"); - verify(prefGroup4).removePreference(prefGroup41); + assertThat(mPreference.isVisible()).isFalse(); } private class TestPrefController extends AbstractPreferenceController { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java index 32fa01c52f21..aac736a94832 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java @@ -17,14 +17,9 @@ package com.android.settingslib.development; import static com.google.common.truth.Truth.assertThat; - import static org.mockito.Answers.RETURNS_DEEP_STUBS; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -67,21 +62,19 @@ public class EnableAdbPreferenceControllerTest { shadowContext.setSystemService(Context.USER_SERVICE, mUserManager); mContext = spy(shadowContext.getApplicationContext()); when(mContext.getPackageManager()).thenReturn(mPackageManager); - mPreference = new SwitchPreference(mContext); - when(mScreen.findPreference(anyString())).thenReturn(mPreference); mController = new ConcreteEnableAdbPreferenceController(mContext); + mPreference = new SwitchPreference(mContext); mPreference.setKey(mController.getPreferenceKey()); + when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference); } @Test public void displayPreference_isNotAdmin_shouldRemovePreference() { when(mUserManager.isAdminUser()).thenReturn(false); - when(mScreen.getPreferenceCount()).thenReturn(1); - when(mScreen.getPreference(0)).thenReturn(mPreference); mController.displayPreference(mScreen); - verify(mScreen).removePreference(any(Preference.class)); + assertThat(mPreference.isVisible()).isFalse(); } @Test @@ -90,7 +83,7 @@ public class EnableAdbPreferenceControllerTest { mController.displayPreference(mScreen); - verify(mScreen, never()).removePreference(any(Preference.class)); + assertThat(mPreference.isVisible()).isTrue(); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java index 4dbb957f2748..34bbf4f14d07 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java @@ -17,13 +17,6 @@ package com.android.settingslib.deviceinfo; import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Answers.RETURNS_DEEP_STUBS; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -38,68 +31,67 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @RunWith(SettingsLibRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class SerialNumberPreferenceControllerTest { - @Mock(answer = RETURNS_DEEP_STUBS) - private Context mContext; - @Mock(answer = RETURNS_DEEP_STUBS) + @Mock private PreferenceScreen mScreen; + private Context mContext; + private Preference mPreference; private AbstractSerialNumberPreferenceController mController; @Before public void setUp() { MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mPreference = new Preference(mContext); + mPreference.setKey(AbstractSerialNumberPreferenceController.KEY_SERIAL_NUMBER); + when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference); } @Test public void testIsAvaiable_noSerial_shouldReturnFalse() { - mController = new ConcreteSerialNumberPreferenceController(mContext, null); + mController = new TestPreferenceController(mContext, null); assertThat(mController.isAvailable()).isFalse(); } @Test - public void testIsAvaiable_hasSerial_shouldReturnTrue() { - mController = new ConcreteSerialNumberPreferenceController(mContext, "123"); + public void testIsAvailable_hasSerial_shouldReturnTrue() { + mController = new TestPreferenceController(mContext, "123"); assertThat(mController.isAvailable()).isTrue(); } @Test public void testDisplay_noSerial_shouldHidePreference() { - final Preference preference = mock(Preference.class); - when(mScreen.getPreferenceCount()).thenReturn(1); - when(mScreen.getPreference(0)).thenReturn(preference); - mController = new ConcreteSerialNumberPreferenceController(mContext, null); - when(preference.getKey()).thenReturn(mController.getPreferenceKey()); + mController = new TestPreferenceController(mContext, null); mController.displayPreference(mScreen); - verify(mScreen).removePreference(any(Preference.class)); + assertThat(mPreference.isVisible()).isFalse(); } @Test public void testDisplay_hasSerial_shouldSetSummary() { final String serial = "123"; - final Preference preference = mock(Preference.class); - when(mScreen.findPreference(anyString())).thenReturn(preference); - mController = new ConcreteSerialNumberPreferenceController(mContext, serial); + mController = new TestPreferenceController(mContext, serial); mController.displayPreference(mScreen); - verify(mScreen, never()).removePreference(any(Preference.class)); - verify(preference).setSummary(serial); + assertThat(mPreference.isVisible()).isTrue(); + assertThat(mPreference.getSummary()).isEqualTo(serial); } - private static class ConcreteSerialNumberPreferenceController + private static class TestPreferenceController extends AbstractSerialNumberPreferenceController { - ConcreteSerialNumberPreferenceController(Context context, String serialNumber) { + TestPreferenceController(Context context, String serialNumber) { super(context, serialNumber); } } diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 9d960a1ec89f..43f4fb95424d 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -32,13 +32,17 @@ import android.view.SurfaceControl; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; +import com.android.systemui.OverviewProxyService.OverviewProxyListener; +import com.android.systemui.statusbar.policy.CallbackController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; +import java.util.ArrayList; +import java.util.List; /** * Class to send information from overview to launcher with a binder. */ -public class OverviewProxyService { +public class OverviewProxyService implements CallbackController<OverviewProxyListener> { private static final String TAG = "OverviewProxyService"; private static final long BACKOFF_MILLIS = 5000; @@ -48,6 +52,7 @@ public class OverviewProxyService { private final Runnable mConnectionRunnable = this::startConnectionToCurrentUser; private final DeviceProvisionedController mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); + private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>(); private IOverviewProxy mOverviewProxy; private int mConnectionBackoffAttempts; @@ -82,6 +87,7 @@ public class OverviewProxyService { } catch (RemoteException e) { Log.e(TAG, "Failed to call onBind()", e); } + notifyConnectionChanged(); } } @@ -144,6 +150,17 @@ public class OverviewProxyService { } } + @Override + public void addCallback(OverviewProxyListener listener) { + mConnectionCallbacks.add(listener); + listener.onConnectionChanged(mOverviewProxy != null); + } + + @Override + public void removeCallback(OverviewProxyListener listener) { + mConnectionCallbacks.remove(listener); + } + public IOverviewProxy getProxy() { return mOverviewProxy; } @@ -152,6 +169,17 @@ public class OverviewProxyService { if (mOverviewProxy != null) { mContext.unbindService(mOverviewServiceConnection); mOverviewProxy = null; + notifyConnectionChanged(); } } + + private void notifyConnectionChanged() { + for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) { + mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null); + } + } + + public interface OverviewProxyListener { + void onConnectionChanged(boolean isConnected); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index 717699b47be4..4180884597f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -112,20 +112,26 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL mIsRTL = isRTL; } - public boolean onInterceptTouchEvent(MotionEvent event) { + private boolean proxyMotionEvents(MotionEvent event) { final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy(); if (overviewProxy != null) { mNavigationBarView.requestUnbufferedDispatch(event); try { overviewProxy.onMotionEvent(event); + return true; } catch (RemoteException e) { Log.e(TAG, "Callback failed", e); } } + return false; + } - // If we move more than a fixed amount, then start capturing for the - // task switcher detector - mTaskSwitcherDetector.onTouchEvent(event); + public boolean onInterceptTouchEvent(MotionEvent event) { + if (!proxyMotionEvents(event)) { + // If we move more than a fixed amount, then start capturing for the + // task switcher detector, disabled when proxying motion events to launcher service + mTaskSwitcherDetector.onTouchEvent(event); + } int action = event.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { @@ -289,7 +295,7 @@ public class NavigationBarGestureHelper extends GestureDetector.SimpleOnGestureL } public boolean onTouchEvent(MotionEvent event) { - boolean result = mTaskSwitcherDetector.onTouchEvent(event); + boolean result = proxyMotionEvents(event) || mTaskSwitcherDetector.onTouchEvent(event); if (mDockWindowEnabled) { result |= handleDockWindowEvent(event); } 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 094129c5793b..4e7f205f167e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -47,6 +47,8 @@ import android.widget.FrameLayout; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.DockedStackExistsListener; +import com.android.systemui.OverviewProxyService; +import com.android.systemui.OverviewProxyService.OverviewProxyListener; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.plugins.PluginListener; @@ -196,6 +198,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } + private final OverviewProxyListener mOverviewProxyListener = + isConnected -> setSlippery(!isConnected); + public NavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -531,6 +536,24 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } } + private void setSlippery(boolean slippery) { + boolean changed = false; + final ViewGroup navbarView = ((ViewGroup) getParent()); + final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView + .getLayoutParams(); + if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) { + lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY; + changed = true; + } else if (!slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0) { + lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY; + changed = true; + } + if (changed) { + WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); + wm.updateViewLayout(navbarView, lp); + } + } + public void setMenuVisibility(final boolean show) { setMenuVisibility(show, false); } @@ -756,6 +779,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav onPluginDisconnected(null); // Create default gesture helper Dependency.get(PluginManager.class).addPluginListener(this, NavGesture.class, false /* Only one */); + Dependency.get(OverviewProxyService.class).addCallback(mOverviewProxyListener); } @Override @@ -765,6 +789,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav if (mGestureHelper != null) { mGestureHelper.destroy(); } + Dependency.get(OverviewProxyService.class).removeCallback(mOverviewProxyListener); } @Override diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index d3dcf5a2f85f..8e93d1982a79 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4680,6 +4680,16 @@ message MetricsEvent { // OS: P DIALOG_FIRMWARE_VERSION = 1247; + // OPEN: Settings > Network & internet > Menu > Private DNS + // CATEGORY: SETTINGS + // OS: P + DIALOG_PRIVATE_DNS = 1248; + + // ACTION: A private dns mode been selected by user + // CATEGORY: SETTINGS + // OS: P + ACTION_PRIVATE_DNS_MODE = 1249; + // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java index 025ff0b08341..296743b57103 100644 --- a/services/core/java/com/android/server/job/JobPackageTracker.java +++ b/services/core/java/com/android/server/job/JobPackageTracker.java @@ -16,15 +16,19 @@ package com.android.server.job; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import static com.android.server.job.JobSchedulerService.sSystemClock; +import static com.android.server.job.JobSchedulerService.sUptimeMillisClock; + import android.app.job.JobInfo; import android.app.job.JobParameters; -import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TimeUtils; + import com.android.internal.util.RingBufferIndices; import com.android.server.job.controllers.JobStatus; @@ -57,7 +61,7 @@ public final class JobPackageTracker { public void addEvent(int cmd, int uid, String tag, int jobId, int stopReason) { int index = mEventIndices.add(); mEventCmds[index] = cmd | ((stopReason<<EVENT_STOP_REASON_SHIFT) & EVENT_STOP_REASON_MASK); - mEventTimes[index] = SystemClock.elapsedRealtime(); + mEventTimes[index] = sElapsedRealtimeClock.millis(); mEventUids[index] = uid; mEventTags[index] = tag; mEventJobIds[index] = jobId; @@ -125,9 +129,9 @@ public final class JobPackageTracker { } public DataSet() { - mStartUptimeTime = SystemClock.uptimeMillis(); - mStartElapsedTime = SystemClock.elapsedRealtime(); - mStartClockTime = System.currentTimeMillis(); + mStartUptimeTime = sUptimeMillisClock.millis(); + mStartElapsedTime = sElapsedRealtimeClock.millis(); + mStartClockTime = sSystemClock.millis(); } private PackageEntry getOrCreateEntry(int uid, String pkg) { @@ -376,20 +380,20 @@ public final class JobPackageTracker { } public void notePending(JobStatus job) { - final long now = SystemClock.uptimeMillis(); + final long now = sUptimeMillisClock.millis(); job.madePending = now; rebatchIfNeeded(now); mCurDataSet.incPending(job.getSourceUid(), job.getSourcePackageName(), now); } public void noteNonpending(JobStatus job) { - final long now = SystemClock.uptimeMillis(); + final long now = sUptimeMillisClock.millis(); mCurDataSet.decPending(job.getSourceUid(), job.getSourcePackageName(), now); rebatchIfNeeded(now); } public void noteActive(JobStatus job) { - final long now = SystemClock.uptimeMillis(); + final long now = sUptimeMillisClock.millis(); job.madeActive = now; rebatchIfNeeded(now); if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { @@ -402,7 +406,7 @@ public final class JobPackageTracker { } public void noteInactive(JobStatus job, int stopReason) { - final long now = SystemClock.uptimeMillis(); + final long now = sUptimeMillisClock.millis(); if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) { mCurDataSet.decActiveTop(job.getSourceUid(), job.getSourcePackageName(), now, stopReason); @@ -431,7 +435,7 @@ public final class JobPackageTracker { if (cur == null && last == null) { return 0; } - final long now = SystemClock.uptimeMillis(); + final long now = sUptimeMillisClock.millis(); long time = 0; if (cur != null) { time += cur.getActiveTime(now) + cur.getPendingTime(now); @@ -445,8 +449,8 @@ public final class JobPackageTracker { } public void dump(PrintWriter pw, String prefix, int filterUid) { - final long now = SystemClock.uptimeMillis(); - final long nowEllapsed = SystemClock.elapsedRealtime(); + final long now = sUptimeMillisClock.millis(); + final long nowEllapsed = sElapsedRealtimeClock.millis(); final DataSet total; if (mLastDataSets[0] != null) { total = new DataSet(mLastDataSets[0]); @@ -470,7 +474,7 @@ public final class JobPackageTracker { return false; } pw.println(" Job history:"); - final long now = SystemClock.elapsedRealtime(); + final long now = sElapsedRealtimeClock.millis(); for (int i=0; i<size; i++) { final int index = mEventIndices.indexOf(i); final int uid = mEventUids[index]; diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 78aa2f946ffc..b5497681a2de 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -19,24 +19,15 @@ package com.android.server.job; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - import android.app.Activity; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.IUidObserver; +import android.app.job.IJobScheduler; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; -import android.app.job.IJobScheduler; import android.app.job.JobWorkItem; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -46,8 +37,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.ServiceInfo; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ServiceInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.BatteryStats; @@ -55,8 +46,8 @@ import android.os.Binder; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.Process; import android.os.PowerManager; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -71,6 +62,7 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TimeUtils; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.util.ArrayUtils; @@ -93,6 +85,16 @@ import com.android.server.job.controllers.TimeController; import libcore.util.EmptyArray; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.time.Clock; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + /** * Responsible for taking jobs representing work to be performed by a client app, and determining * based on the criteria specified when that job should be run against the client application's @@ -117,6 +119,12 @@ public final class JobSchedulerService extends com.android.server.SystemService /** The maximum number of jobs that we allow an unprivileged app to schedule */ private static final int MAX_JOBS_PER_APP = 100; + @VisibleForTesting + public static Clock sSystemClock = Clock.systemUTC(); + @VisibleForTesting + public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock(); + @VisibleForTesting + public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock(); /** Global local for all job scheduler state. */ final Object mLock = new Object(); @@ -960,7 +968,7 @@ public final class JobSchedulerService extends com.android.server.SystemService if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) { // When we reach clock sanity, recalculate the temporal windows // of all affected jobs. - if (mJobs.clockNowValidToInflate(System.currentTimeMillis())) { + if (mJobs.clockNowValidToInflate(sSystemClock.millis())) { Slog.i(TAG, "RTC now valid; recalculating persisted job windows"); // We've done our job now, so stop watching the time. @@ -1068,7 +1076,7 @@ public final class JobSchedulerService extends com.android.server.SystemService if (!jobStatus.isPreparedLocked()) { Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus); } - jobStatus.enqueueTime = SystemClock.elapsedRealtime(); + jobStatus.enqueueTime = sElapsedRealtimeClock.millis(); final boolean update = mJobs.add(jobStatus); if (mReadyToRock) { for (int i = 0; i < mControllers.size(); i++) { @@ -1156,7 +1164,7 @@ public final class JobSchedulerService extends com.android.server.SystemService * @see #maybeQueueReadyJobsForExecutionLocked */ private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) { - final long elapsedNowMillis = SystemClock.elapsedRealtime(); + final long elapsedNowMillis = sElapsedRealtimeClock.millis(); final JobInfo job = failureToReschedule.getJob(); final long initialBackoffMillis = job.getInitialBackoffMillis(); @@ -1200,7 +1208,7 @@ public final class JobSchedulerService extends com.android.server.SystemService Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS); JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis, JobStatus.NO_LATEST_RUNTIME, backoffAttempts, - failureToReschedule.getLastSuccessfulRunTime(), System.currentTimeMillis()); + failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis()); for (int ic=0; ic<mControllers.size(); ic++) { StateController controller = mControllers.get(ic); controller.rescheduleForFailureLocked(newJob, failureToReschedule); @@ -1220,7 +1228,7 @@ public final class JobSchedulerService extends com.android.server.SystemService * recurring job. */ private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { - final long elapsedNow = SystemClock.elapsedRealtime(); + final long elapsedNow = sElapsedRealtimeClock.millis(); // Compute how much of the period is remaining. long runEarly = 0L; @@ -1239,7 +1247,7 @@ public final class JobSchedulerService extends com.android.server.SystemService } return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed, newLatestRuntimeElapsed, 0 /* backoffAttempt */, - System.currentTimeMillis() /* lastSuccessfulRunTime */, + sSystemClock.millis() /* lastSuccessfulRunTime */, periodicToReschedule.getLastFailedRunTime()); } @@ -2367,8 +2375,8 @@ public final class JobSchedulerService extends com.android.server.SystemService } final int filterUidFinal = UserHandle.getAppId(filterUid); - final long nowElapsed = SystemClock.elapsedRealtime(); - final long nowUptime = SystemClock.uptimeMillis(); + final long nowElapsed = sElapsedRealtimeClock.millis(); + final long nowUptime = sUptimeMillisClock.millis(); synchronized (mLock) { mConstants.dump(pw); pw.println(); diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index a7e674b5a3a1..ac7297e611a7 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -16,11 +16,13 @@ package com.android.server.job; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.app.ActivityManager; -import android.app.job.JobInfo; -import android.app.job.JobParameters; import android.app.job.IJobCallback; import android.app.job.IJobService; +import android.app.job.JobInfo; +import android.app.job.JobParameters; import android.app.job.JobWorkItem; import android.content.ComponentName; import android.content.Context; @@ -34,7 +36,6 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; -import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.util.Slog; @@ -202,7 +203,7 @@ public final class JobServiceContext implements ServiceConnection { mRunningCallback = new JobCallback(); final boolean isDeadlineExpired = job.hasDeadlineConstraint() && - (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime()); + (job.getLatestRunTimeElapsed() < sElapsedRealtimeClock.millis()); Uri[] triggeredUris = null; if (job.changedUris != null) { triggeredUris = new Uri[job.changedUris.size()]; @@ -217,7 +218,7 @@ public final class JobServiceContext implements ServiceConnection { mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(), ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(), isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network); - mExecutionStartTimeElapsed = SystemClock.elapsedRealtime(); + mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis(); // Once we'e begun executing a job, we by definition no longer care whether // it was inflated from disk with not-yet-coherent delay/deadline bounds. @@ -428,7 +429,7 @@ public final class JobServiceContext implements ServiceConnection { sb.append("Caller no longer running"); if (cb.mStoppedReason != null) { sb.append(", last stopped "); - TimeUtils.formatDuration(SystemClock.elapsedRealtime() - cb.mStoppedTime, sb); + TimeUtils.formatDuration(sElapsedRealtimeClock.millis() - cb.mStoppedTime, sb); sb.append(" because: "); sb.append(cb.mStoppedReason); } @@ -457,7 +458,7 @@ public final class JobServiceContext implements ServiceConnection { sb.append("Ignoring timeout of no longer active job"); if (jc.mStoppedReason != null) { sb.append(", stopped "); - TimeUtils.formatDuration(SystemClock.elapsedRealtime() + TimeUtils.formatDuration(sElapsedRealtimeClock.millis() - jc.mStoppedTime, sb); sb.append(" because: "); sb.append(jc.mStoppedReason); @@ -740,7 +741,7 @@ public final class JobServiceContext implements ServiceConnection { private void applyStoppedReasonLocked(String reason) { if (reason != null && mStoppedReason == null) { mStoppedReason = reason; - mStoppedTime = SystemClock.elapsedRealtime(); + mStoppedTime = sElapsedRealtimeClock.millis(); if (mRunningCallback != null) { mRunningCallback.mStoppedReason = mStoppedReason; mRunningCallback.mStoppedTime = mStoppedTime; @@ -777,7 +778,7 @@ public final class JobServiceContext implements ServiceConnection { } Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback); mCallbackHandler.sendMessageDelayed(m, timeoutMillis); - mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis; + mTimeoutElapsed = sElapsedRealtimeClock.millis() + timeoutMillis; } diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index aa9f77c839e5..1af3b39e928b 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -16,20 +16,23 @@ package com.android.server.job; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import static com.android.server.job.JobSchedulerService.sSystemClock; + import android.app.ActivityManager; import android.app.IActivityManager; -import android.content.ComponentName; import android.app.job.JobInfo; +import android.content.ComponentName; import android.content.Context; +import android.net.NetworkRequest; import android.os.Environment; import android.os.Handler; import android.os.PersistableBundle; import android.os.Process; -import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateUtils; -import android.util.AtomicFile; import android.util.ArraySet; +import android.util.AtomicFile; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -37,11 +40,16 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.BitUtils; import com.android.internal.util.FastXmlSerializer; import com.android.server.IoThread; import com.android.server.job.JobSchedulerInternal.JobStorePersistStats; import com.android.server.job.controllers.JobStatus; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; @@ -53,10 +61,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - /** * Maintains the master list of jobs that the job scheduler is tracking. These jobs are compared by * reference, so none of the functions in this class should make a copy. @@ -141,7 +145,7 @@ public final class JobStore { // a *correct* timestamp, see a bunch of overdue jobs, and run them; then // settle into normal operation. mXmlTimestamp = mJobsFile.getLastModifiedTime(); - mRtcGood = (System.currentTimeMillis() > mXmlTimestamp); + mRtcGood = (sSystemClock.millis() > mXmlTimestamp); readJobMapFromDisk(mJobSet, mRtcGood); } @@ -161,7 +165,7 @@ public final class JobStore { */ public void getRtcCorrectedJobsLocked(final ArrayList<JobStatus> toAdd, final ArrayList<JobStatus> toRemove) { - final long elapsedNow = SystemClock.elapsedRealtime(); + final long elapsedNow = sElapsedRealtimeClock.millis(); // Find the jobs that need to be fixed up, collecting them for post-iteration // replacement with their new versions @@ -323,7 +327,7 @@ public final class JobStore { private final Runnable mWriteRunnable = new Runnable() { @Override public void run() { - final long startElapsed = SystemClock.elapsedRealtime(); + final long startElapsed = sElapsedRealtimeClock.millis(); final List<JobStatus> storeCopy = new ArrayList<JobStatus>(); synchronized (mLock) { // Clone the jobs so we can release the lock before writing. @@ -338,7 +342,7 @@ public final class JobStore { } writeJobsMapImpl(storeCopy); if (DEBUG) { - Slog.v(TAG, "Finished writing, took " + (SystemClock.elapsedRealtime() + Slog.v(TAG, "Finished writing, took " + (sElapsedRealtimeClock.millis() - startElapsed) + "ms"); } } @@ -454,17 +458,12 @@ public final class JobStore { */ private void writeConstraintsToXml(XmlSerializer out, JobStatus jobStatus) throws IOException { out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS); - if (jobStatus.needsAnyConnectivity()) { - out.attribute(null, "connectivity", Boolean.toString(true)); - } - if (jobStatus.needsMeteredConnectivity()) { - out.attribute(null, "metered", Boolean.toString(true)); - } - if (jobStatus.needsUnmeteredConnectivity()) { - out.attribute(null, "unmetered", Boolean.toString(true)); - } - if (jobStatus.needsNonRoamingConnectivity()) { - out.attribute(null, "not-roaming", Boolean.toString(true)); + if (jobStatus.hasConnectivityConstraint()) { + final NetworkRequest network = jobStatus.getJob().getRequiredNetwork(); + out.attribute(null, "net-capabilities", Long.toString( + BitUtils.packBits(network.networkCapabilities.getCapabilities()))); + out.attribute(null, "net-transport-types", Long.toString( + BitUtils.packBits(network.networkCapabilities.getTransportTypes()))); } if (jobStatus.hasIdleConstraint()) { out.attribute(null, "idle", Boolean.toString(true)); @@ -497,8 +496,8 @@ public final class JobStore { Slog.i(TAG, "storing original UTC timestamps for " + jobStatus); } - final long nowRTC = System.currentTimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowRTC = sSystemClock.millis(); + final long nowElapsed = sElapsedRealtimeClock.millis(); if (jobStatus.hasDeadlineConstraint()) { // Wall clock deadline. final long deadlineWallclock = (utcJobTimes == null) @@ -538,7 +537,7 @@ public final class JobStore { */ private static Pair<Long, Long> convertRtcBoundsToElapsed(Pair<Long, Long> rtcTimes, long nowElapsed) { - final long nowWallclock = System.currentTimeMillis(); + final long nowWallclock = sSystemClock.millis(); final long earliest = (rtcTimes.first > JobStatus.NO_EARLIEST_RUNTIME) ? nowElapsed + Math.max(rtcTimes.first - nowWallclock, 0) : JobStatus.NO_EARLIEST_RUNTIME; @@ -581,7 +580,7 @@ public final class JobStore { synchronized (mLock) { jobs = readJobMapImpl(fis, rtcGood); if (jobs != null) { - long now = SystemClock.elapsedRealtime(); + long now = sElapsedRealtimeClock.millis(); IActivityManager am = ActivityManager.getService(); for (int i=0; i<jobs.size(); i++) { JobStatus js = jobs.get(i); @@ -754,7 +753,7 @@ public final class JobStore { return null; } - final long elapsedNow = SystemClock.elapsedRealtime(); + final long elapsedNow = sElapsedRealtimeClock.millis(); Pair<Long, Long> elapsedRuntimes = convertRtcBoundsToElapsed(rtcRuntimes, elapsedNow); if (XML_TAG_PERIODIC.equals(parser.getName())) { @@ -862,22 +861,38 @@ public final class JobStore { } private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { - String val = parser.getAttributeValue(null, "connectivity"); - if (val != null) { - jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); - } - val = parser.getAttributeValue(null, "metered"); - if (val != null) { - jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_METERED); - } - val = parser.getAttributeValue(null, "unmetered"); - if (val != null) { - jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); - } - val = parser.getAttributeValue(null, "not-roaming"); - if (val != null) { - jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING); + String val; + + final String netCapabilities = parser.getAttributeValue(null, "net-capabilities"); + final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types"); + if (netCapabilities != null && netTransportTypes != null) { + final NetworkRequest request = new NetworkRequest.Builder().build(); + // We're okay throwing NFE here; caught by caller + request.networkCapabilities.setCapabilities( + BitUtils.unpackBits(Long.parseLong(netCapabilities))); + request.networkCapabilities.setTransportTypes( + BitUtils.unpackBits(Long.parseLong(netTransportTypes))); + jobBuilder.setRequiredNetwork(request); + } else { + // Read legacy values + val = parser.getAttributeValue(null, "connectivity"); + if (val != null) { + jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + } + val = parser.getAttributeValue(null, "metered"); + if (val != null) { + jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_METERED); + } + val = parser.getAttributeValue(null, "unmetered"); + if (val != null) { + jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); + } + val = parser.getAttributeValue(null, "not-roaming"); + if (val != null) { + jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING); + } } + val = parser.getAttributeValue(null, "idle"); if (val != null) { jobBuilder.setRequiresDeviceIdle(true); @@ -937,8 +952,8 @@ public final class JobStore { private Pair<Long, Long> buildExecutionTimesFromXml(XmlPullParser parser) throws NumberFormatException { // Pull out execution time data. - final long nowWallclock = System.currentTimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowWallclock = sSystemClock.millis(); + final long nowElapsed = sElapsedRealtimeClock.millis(); long earliestRunTimeElapsed = JobStatus.NO_EARLIEST_RUNTIME; long latestRunTimeElapsed = JobStatus.NO_LATEST_RUNTIME; diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java index 91119690617a..76ff8348fa53 100644 --- a/services/core/java/com/android/server/job/controllers/BatteryController.java +++ b/services/core/java/com/android/server/job/controllers/BatteryController.java @@ -16,13 +16,14 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.BatteryManagerInternal; -import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import android.util.Slog; @@ -203,7 +204,7 @@ public final class BatteryController extends StateController { if (Intent.ACTION_BATTERY_LOW.equals(action)) { if (DEBUG) { Slog.d(TAG, "Battery life too low to do work. @ " - + SystemClock.elapsedRealtime()); + + sElapsedRealtimeClock.millis()); } // If we get this action, the battery is discharging => it isn't plugged in so // there's no work to cancel. We track this variable for the case where it is @@ -213,14 +214,14 @@ public final class BatteryController extends StateController { } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) { if (DEBUG) { Slog.d(TAG, "Battery life healthy enough to do work. @ " - + SystemClock.elapsedRealtime()); + + sElapsedRealtimeClock.millis()); } mBatteryHealthy = true; maybeReportNewChargingStateLocked(); } else if (BatteryManager.ACTION_CHARGING.equals(action)) { if (DEBUG) { Slog.d(TAG, "Received charging intent, fired @ " - + SystemClock.elapsedRealtime()); + + sElapsedRealtimeClock.millis()); } mCharging = true; maybeReportNewChargingStateLocked(); diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index ddee345a1c42..da2876910ee5 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -54,7 +54,6 @@ public final class ConnectivityController extends StateController implements private final ConnectivityManager mConnManager; private final NetworkPolicyManager mNetPolicyManager; private boolean mConnected; - private boolean mValidated; @GuardedBy("mLock") private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>(); @@ -79,7 +78,7 @@ public final class ConnectivityController extends StateController implements mConnManager = mContext.getSystemService(ConnectivityManager.class); mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class); - mConnected = mValidated = false; + mConnected = false; mConnManager.registerDefaultNetworkCallback(mNetworkCallback); mNetPolicyManager.registerListener(mNetPolicyListener); @@ -149,32 +148,21 @@ public final class ConnectivityController extends StateController implements } private boolean updateConstraintsSatisfied(JobStatus jobStatus) { + // TODO: consider matching against non-active networks + final int jobUid = jobStatus.getSourceUid(); final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0; final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked); final NetworkInfo info = mConnManager.getNetworkInfoForUid(network, jobUid, ignoreBlocked); - - final NetworkCapabilities capabilities = (network != null) - ? mConnManager.getNetworkCapabilities(network) : null; + final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network); final boolean connected = (info != null) && info.isConnected(); - final boolean validated = (capabilities != null) - && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); + final boolean satisfied = jobStatus.getJob().getRequiredNetwork().networkCapabilities + .satisfiedByNetworkCapabilities(capabilities); final boolean sane = isSane(jobStatus, capabilities); - final boolean connectionUsable = connected && validated && sane; - - final boolean metered = connected && (capabilities != null) - && !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - final boolean unmetered = connected && (capabilities != null) - && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - final boolean notRoaming = connected && (info != null) - && !info.isRoaming(); - boolean changed = false; - changed |= jobStatus.setConnectivityConstraintSatisfied(connectionUsable); - changed |= jobStatus.setMeteredConstraintSatisfied(metered); - changed |= jobStatus.setUnmeteredConstraintSatisfied(unmetered); - changed |= jobStatus.setNotRoamingConstraintSatisfied(notRoaming); + final boolean changed = jobStatus + .setConnectivityConstraintSatisfied(connected && satisfied && sane); // Pass along the evaluated network for job to use; prevents race // conditions as default routes change over time, and opens the door to @@ -185,17 +173,13 @@ public final class ConnectivityController extends StateController implements // overall state of connectivity constraint satisfiability. if (jobUid == Process.SYSTEM_UID) { mConnected = connected; - mValidated = validated; } if (DEBUG) { Slog.i(TAG, "Connectivity " + (changed ? "CHANGED" : "unchanged") - + " for " + jobStatus + ": usable=" + connectionUsable - + " connected=" + connected - + " validated=" + validated - + " metered=" + metered - + " unmetered=" + unmetered - + " notRoaming=" + notRoaming); + + " for " + jobStatus + ": connected=" + connected + + " satisfied=" + satisfied + + " sane=" + sane); } return changed; } @@ -292,8 +276,6 @@ public final class ConnectivityController extends StateController implements public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { pw.print("Connectivity: connected="); pw.print(mConnected); - pw.print(" validated="); - pw.println(mValidated); pw.print("Tracking "); pw.print(mTrackedJobs.size()); pw.println(":"); @@ -304,10 +286,7 @@ public final class ConnectivityController extends StateController implements js.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, js.getSourceUid()); - pw.print(": C="); pw.print(js.needsAnyConnectivity()); - pw.print(": M="); pw.print(js.needsMeteredConnectivity()); - pw.print(": UM="); pw.print(js.needsUnmeteredConnectivity()); - pw.print(": NR="); pw.println(js.needsNonRoamingConnectivity()); + pw.print(": "); pw.print(js.getJob().getRequiredNetwork()); } } } diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index 9eda046fa1e4..7bde17449af7 100644 --- a/services/core/java/com/android/server/job/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -16,7 +16,7 @@ package com.android.server.job.controllers; -import java.io.PrintWriter; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.app.AlarmManager; import android.app.PendingIntent; @@ -24,7 +24,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import android.util.Slog; @@ -33,6 +32,8 @@ import com.android.server.am.ActivityManagerService; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateChangedListener; +import java.io.PrintWriter; + public final class IdleController extends StateController { private static final String TAG = "IdleController"; @@ -169,7 +170,7 @@ public final class IdleController extends StateController { // when the screen goes off or dreaming starts, we schedule the // alarm that will tell us when we have decided the device is // truly idle. - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = sElapsedRealtimeClock.millis(); final long when = nowElapsed + mInactivityIdleThreshold; if (DEBUG) { Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when=" @@ -182,7 +183,7 @@ public final class IdleController extends StateController { // idle time starts now. Do not set mIdle if screen is on. if (!mIdle && !mScreenOn) { if (DEBUG) { - Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime()); + Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis()); } mIdle = true; reportNewIdleState(mIdle); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 46ed84e599e8..a5906cb1e9a1 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -16,6 +16,8 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.app.AppGlobals; import android.app.IActivityManager; import android.app.job.JobInfo; @@ -25,7 +27,6 @@ import android.content.ComponentName; import android.net.Network; import android.net.Uri; import android.os.RemoteException; -import android.os.SystemClock; import android.os.UserHandle; import android.text.format.Time; import android.util.ArraySet; @@ -64,19 +65,12 @@ public final class JobStatus { static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; static final int CONSTRAINT_TIMING_DELAY = 1<<31; static final int CONSTRAINT_DEADLINE = 1<<30; - static final int CONSTRAINT_UNMETERED = 1<<29; static final int CONSTRAINT_CONNECTIVITY = 1<<28; static final int CONSTRAINT_APP_NOT_IDLE = 1<<27; static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26; static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25; - static final int CONSTRAINT_NOT_ROAMING = 1<<24; - static final int CONSTRAINT_METERED = 1<<23; static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22; - static final int CONNECTIVITY_MASK = - CONSTRAINT_UNMETERED | CONSTRAINT_CONNECTIVITY | - CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED; - // Soft override: ignore constraints like time that don't affect API availability public static final int OVERRIDE_SOFT = 1; // Full override: ignore all constraints including API-affecting like connectivity @@ -264,27 +258,9 @@ public final class JobStatus { int requiredConstraints = job.getConstraintFlags(); - switch (job.getNetworkType()) { - case JobInfo.NETWORK_TYPE_NONE: - // No constraint. - break; - case JobInfo.NETWORK_TYPE_ANY: - requiredConstraints |= CONSTRAINT_CONNECTIVITY; - break; - case JobInfo.NETWORK_TYPE_UNMETERED: - requiredConstraints |= CONSTRAINT_UNMETERED; - break; - case JobInfo.NETWORK_TYPE_NOT_ROAMING: - requiredConstraints |= CONSTRAINT_NOT_ROAMING; - break; - case JobInfo.NETWORK_TYPE_METERED: - requiredConstraints |= CONSTRAINT_METERED; - break; - default: - Slog.w(TAG, "Unrecognized networking constraint " + job.getNetworkType()); - break; + if (job.getRequiredNetwork() != null) { + requiredConstraints |= CONSTRAINT_CONNECTIVITY; } - if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) { requiredConstraints |= CONSTRAINT_TIMING_DELAY; } @@ -365,7 +341,7 @@ public final class JobStatus { */ public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, String tag) { - final long elapsedNow = SystemClock.elapsedRealtime(); + final long elapsedNow = sElapsedRealtimeClock.millis(); final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis; if (job.isPeriodic()) { latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis(); @@ -610,25 +586,9 @@ public final class JobStatus { /** Does this job have any sort of networking constraint? */ public boolean hasConnectivityConstraint() { - return (requiredConstraints&CONNECTIVITY_MASK) != 0; - } - - public boolean needsAnyConnectivity() { return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0; } - public boolean needsUnmeteredConnectivity() { - return (requiredConstraints&CONSTRAINT_UNMETERED) != 0; - } - - public boolean needsMeteredConnectivity() { - return (requiredConstraints&CONSTRAINT_METERED) != 0; - } - - public boolean needsNonRoamingConnectivity() { - return (requiredConstraints&CONSTRAINT_NOT_ROAMING) != 0; - } - public boolean hasChargingConstraint() { return (requiredConstraints&CONSTRAINT_CHARGING) != 0; } @@ -725,18 +685,6 @@ public final class JobStatus { return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state); } - boolean setUnmeteredConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_UNMETERED, state); - } - - boolean setMeteredConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_METERED, state); - } - - boolean setNotRoamingConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_NOT_ROAMING, state); - } - boolean setAppNotIdleConstraintSatisfied(boolean state) { return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state); } @@ -817,12 +765,9 @@ public final class JobStatus { && notRestrictedInBg; } - static final int CONSTRAINTS_OF_INTEREST = - CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW | - CONSTRAINT_TIMING_DELAY | - CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED | - CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED | - CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER; + static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW + | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY + | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER; // Soft override covers all non-"functional" constraints static final int SOFT_OVERRIDE_CONSTRAINTS = @@ -870,15 +815,14 @@ public final class JobStatus { sb.append(getSourceUid()); if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { - long now = SystemClock.elapsedRealtime(); + long now = sElapsedRealtimeClock.millis(); sb.append(" TIME="); formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now); sb.append(":"); formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now); } - if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { - sb.append(" NET="); - sb.append(job.getNetworkType()); + if (job.getRequiredNetwork() != null) { + sb.append(" NET"); } if (job.isRequireCharging()) { sb.append(" CHARGING"); @@ -985,15 +929,6 @@ public final class JobStatus { if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) { pw.print(" CONNECTIVITY"); } - if ((constraints&CONSTRAINT_UNMETERED) != 0) { - pw.print(" UNMETERED"); - } - if ((constraints&CONSTRAINT_NOT_ROAMING) != 0) { - pw.print(" NOT_ROAMING"); - } - if ((constraints&CONSTRAINT_METERED) != 0) { - pw.print(" METERED"); - } if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) { pw.print(" APP_NOT_IDLE"); } @@ -1084,11 +1019,13 @@ public final class JobStatus { pw.print(prefix); pw.println(" Granted URI permissions:"); uriPerms.dump(pw, prefix + " "); } - if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { - pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType()); + if (job.getRequiredNetwork() != null) { + pw.print(prefix); pw.print(" Network type: "); + pw.println(job.getRequiredNetwork()); } if (totalNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { - pw.print(prefix); pw.print(" Network bytes: "); pw.println(totalNetworkBytes); + pw.print(prefix); pw.print(" Network bytes: "); + pw.println(totalNetworkBytes); } if (job.getMinLatencyMillis() != 0) { pw.print(prefix); pw.print(" Minimum latency: "); diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java index c24e563948d4..84782f59d142 100644 --- a/services/core/java/com/android/server/job/controllers/StorageController.java +++ b/services/core/java/com/android/server/job/controllers/StorageController.java @@ -16,11 +16,12 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import android.util.Slog; @@ -154,13 +155,13 @@ public final class StorageController extends StateController { if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) { if (DEBUG) { Slog.d(TAG, "Available storage too low to do work. @ " - + SystemClock.elapsedRealtime()); + + sElapsedRealtimeClock.millis()); } mStorageLow = true; } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { if (DEBUG) { Slog.d(TAG, "Available stoage high enough to do work. @ " - + SystemClock.elapsedRealtime()); + + sElapsedRealtimeClock.millis()); } mStorageLow = false; maybeReportNewStorageState(); diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index ee4c606fa6d3..cb9e43a13684 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -16,10 +16,11 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.app.AlarmManager; import android.app.AlarmManager.OnAlarmListener; import android.content.Context; -import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.util.Slog; @@ -84,7 +85,7 @@ public final class TimeController extends StateController { // pattern of having a job with a 0 deadline constraint ("run immediately"). // Unlike most controllers, once one of our constraints has been satisfied, it // will never be unsatisfied (our time base can not go backwards). - final long nowElapsedMillis = SystemClock.elapsedRealtime(); + final long nowElapsedMillis = sElapsedRealtimeClock.millis(); if (job.hasDeadlineConstraint() && evaluateDeadlineConstraint(job, nowElapsedMillis)) { return; } else if (job.hasTimingDelayConstraint() && evaluateTimingDelayConstraint(job, @@ -157,7 +158,7 @@ public final class TimeController extends StateController { long nextExpiryTime = Long.MAX_VALUE; int nextExpiryUid = 0; String nextExpiryPackageName = null; - final long nowElapsedMillis = SystemClock.elapsedRealtime(); + final long nowElapsedMillis = sElapsedRealtimeClock.millis(); Iterator<JobStatus> it = mTrackedJobs.iterator(); while (it.hasNext()) { @@ -201,7 +202,7 @@ public final class TimeController extends StateController { */ private void checkExpiredDelaysAndResetAlarm() { synchronized (mLock) { - final long nowElapsedMillis = SystemClock.elapsedRealtime(); + final long nowElapsedMillis = sElapsedRealtimeClock.millis(); long nextDelayTime = Long.MAX_VALUE; int nextDelayUid = 0; String nextDelayPackageName = null; @@ -283,7 +284,7 @@ public final class TimeController extends StateController { } private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) { - final long earliestWakeupTimeElapsed = SystemClock.elapsedRealtime(); + final long earliestWakeupTimeElapsed = sElapsedRealtimeClock.millis(); if (proposedAlarmTimeElapsedMillis < earliestWakeupTimeElapsed) { return earliestWakeupTimeElapsed; } @@ -328,9 +329,9 @@ public final class TimeController extends StateController { @Override public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = sElapsedRealtimeClock.millis(); pw.print("Alarms: now="); - pw.print(SystemClock.elapsedRealtime()); + pw.print(sElapsedRealtimeClock.millis()); pw.println(); pw.print("Next delay alarm in "); TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index caf85b0191cd..7837940434aa 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -8084,28 +8084,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) { final WindowState imeWin = mWindowManagerFuncs.getInputMethodWindowLw(); - final boolean isImeShownWithBottomNavBar = - imeWin != null && imeWin.isVisibleLw() && mNavigationBarPosition == NAV_BAR_BOTTOM; - if (isImeShownWithBottomNavBar) { - final int winFlags = PolicyControl.getWindowFlags(imeWin, null); - if ((winFlags & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { - // If the IME window is visible and explicitly requesting custom nav bar background - // rendering, respect its light flag. - vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - vis |= PolicyControl.getSystemUiVisibility(imeWin, null) - & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - return vis; - } + + final WindowState navColorWin; + if (imeWin != null && imeWin.isVisibleLw() && mNavigationBarPosition == NAV_BAR_BOTTOM) { + navColorWin = imeWin; + } else { + navColorWin = opaqueOrDimming; } - if (opaqueOrDimming != null) { - if (opaqueOrDimming == opaque) { - // If the top fullscreen-or-dimming window is also the top fullscreen window, - // respect its light flag. + if (navColorWin != null) { + if (navColorWin == opaque) { + // If the top fullscreen-or-dimming window is also the top fullscreen, respect + // its light flag. vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - vis |= PolicyControl.getSystemUiVisibility(opaqueOrDimming, null) + vis |= PolicyControl.getSystemUiVisibility(navColorWin, null) & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - } else if (opaqueOrDimming.isDimming() || isImeShownWithBottomNavBar) { + } else if (navColorWin.isDimming() || navColorWin == imeWin) { // Otherwise if it's dimming or it's the IME window, clear the light flag. vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 35870b1a4904..06527675349a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -569,6 +569,8 @@ public class WindowManagerService extends IWindowManager.Stub boolean mForceResizableTasks = false; boolean mSupportsPictureInPicture = false; + private boolean mDisableTransitionAnimation = false; + int getDragLayerLocked() { return mPolicy.getWindowLayerFromTypeLw(TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET; } @@ -966,6 +968,8 @@ public class WindowManagerService extends IWindowManager.Stub com.android.internal.R.bool.config_allowAnimationsInLowPowerMode); mMaxUiWidth = context.getResources().getInteger( com.android.internal.R.integer.config_maxUiWidth); + mDisableTransitionAnimation = context.getResources().getBoolean( + com.android.internal.R.bool.config_disableTransitionAnimation); mInputManager = inputManager; // Must be before createDisplayContentLocked. mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mDisplaySettings = new DisplaySettings(); @@ -2279,6 +2283,14 @@ public class WindowManagerService extends IWindowManager.Stub boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) { + if (mDisableTransitionAnimation) { + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { + Slog.v(TAG_WM, + "applyAnimation: transition animation is disabled. atoken=" + atoken); + } + atoken.mAppAnimator.clearAnimation(); + return false; + } // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index 31ed8ba7dd23..bf912dd7e0e1 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -1,50 +1,93 @@ package com.android.server.job; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -import android.content.ComponentName; -import android.content.Context; import android.app.job.JobInfo; import android.app.job.JobInfo.Builder; +import android.content.ComponentName; +import android.content.Context; +import android.net.NetworkRequest; +import android.os.Parcel; +import android.os.Parcelable; import android.os.PersistableBundle; import android.os.SystemClock; -import android.test.AndroidTestCase; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; import android.test.RenamingDelegatingContext; import android.util.Log; import android.util.Pair; +import com.android.internal.util.HexDump; +import com.android.server.IoThread; import com.android.server.job.JobStore.JobSet; import com.android.server.job.controllers.JobStatus; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.time.Clock; +import java.time.ZoneOffset; +import java.util.Arrays; import java.util.Iterator; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Test reading and writing correctly from file. */ -public class JobStoreTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class JobStoreTest { private static final String TAG = "TaskStoreTest"; private static final String TEST_PREFIX = "_test_"; - private static final int SOME_UID = 34234; + private static final int SOME_UID = android.os.Process.FIRST_APPLICATION_UID; private ComponentName mComponent; - private static final long IO_WAIT = 1000L; JobStore mTaskStoreUnderTest; Context mTestContext; - @Override + private Context getContext() { + return InstrumentationRegistry.getContext(); + } + + @Before public void setUp() throws Exception { mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX); Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'"); mTaskStoreUnderTest = JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir()); mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName()); + + // Freeze the clocks at this moment in time + JobSchedulerService.sSystemClock = + Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); + JobSchedulerService.sUptimeMillisClock = + Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC); + JobSchedulerService.sElapsedRealtimeClock = + Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); } - @Override + @After public void tearDown() throws Exception { mTaskStoreUnderTest.clear(); } + private void waitForPendingIo() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + IoThread.getHandler().post(() -> { + latch.countDown(); + }); + latch.await(10, TimeUnit.SECONDS); + } + + @Test public void testMaybeWriteStatusToDisk() throws Exception { int taskId = 5; long runByMillis = 20000L; // 20s @@ -61,7 +104,8 @@ public class JobStoreTest extends AndroidTestCase { .build(); final JobStatus ts = JobStatus.createFromJobInfo(task, SOME_UID, null, -1, null); mTaskStoreUnderTest.add(ts); - Thread.sleep(IO_WAIT); + waitForPendingIo(); + // Manually load tasks from xml file. final JobSet jobStatusSet = new JobSet(); mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); @@ -75,9 +119,9 @@ public class JobStoreTest extends AndroidTestCase { ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime()); compareTimestampsSubjectToIoLatency("Late run-times not the same after read.", ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed()); - } + @Test public void testWritingTwoFilesToDisk() throws Exception { final JobInfo task1 = new Builder(8, mComponent) .setRequiresDeviceIdle(true) @@ -96,7 +140,7 @@ public class JobStoreTest extends AndroidTestCase { final JobStatus taskStatus2 = JobStatus.createFromJobInfo(task2, SOME_UID, null, -1, null); mTaskStoreUnderTest.add(taskStatus1); mTaskStoreUnderTest.add(taskStatus2); - Thread.sleep(IO_WAIT); + waitForPendingIo(); final JobSet jobStatusSet = new JobSet(); mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); @@ -125,9 +169,9 @@ public class JobStoreTest extends AndroidTestCase { taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime()); compareTimestampsSubjectToIoLatency("Late run-times not the same after read.", taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed()); - } + @Test public void testWritingTaskWithExtras() throws Exception { JobInfo.Builder b = new Builder(8, mComponent) .setRequiresDeviceIdle(true) @@ -144,7 +188,7 @@ public class JobStoreTest extends AndroidTestCase { JobStatus taskStatus = JobStatus.createFromJobInfo(task, SOME_UID, null, -1, null); mTaskStoreUnderTest.add(taskStatus); - Thread.sleep(IO_WAIT); + waitForPendingIo(); final JobSet jobStatusSet = new JobSet(); mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); @@ -152,6 +196,8 @@ public class JobStoreTest extends AndroidTestCase { JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); assertTasksEqual(task, loaded.getJob()); } + + @Test public void testWritingTaskWithSourcePackage() throws Exception { JobInfo.Builder b = new Builder(8, mComponent) .setRequiresDeviceIdle(true) @@ -162,7 +208,7 @@ public class JobStoreTest extends AndroidTestCase { "com.google.android.gms", 0, null); mTaskStoreUnderTest.add(taskStatus); - Thread.sleep(IO_WAIT); + waitForPendingIo(); final JobSet jobStatusSet = new JobSet(); mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); @@ -174,6 +220,7 @@ public class JobStoreTest extends AndroidTestCase { taskStatus.getSourceUserId()); } + @Test public void testWritingTaskWithFlex() throws Exception { JobInfo.Builder b = new Builder(8, mComponent) .setRequiresDeviceIdle(true) @@ -183,7 +230,7 @@ public class JobStoreTest extends AndroidTestCase { JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); mTaskStoreUnderTest.add(taskStatus); - Thread.sleep(IO_WAIT); + waitForPendingIo(); final JobSet jobStatusSet = new JobSet(); mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); @@ -195,6 +242,7 @@ public class JobStoreTest extends AndroidTestCase { taskStatus.getJob().getFlexMillis()); } + @Test public void testMassivePeriodClampedOnRead() throws Exception { final long ONE_HOUR = 60*60*1000L; // flex final long TWO_HOURS = 2 * ONE_HOUR; // period @@ -214,7 +262,7 @@ public class JobStoreTest extends AndroidTestCase { persistedExecutionTimesUTC); mTaskStoreUnderTest.add(js); - Thread.sleep(IO_WAIT); + waitForPendingIo(); final JobSet jobStatusSet = new JobSet(); mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); @@ -231,6 +279,7 @@ public class JobStoreTest extends AndroidTestCase { loaded.getEarliestRunTime() <= newNowElapsed + TWO_HOURS + ONE_HOUR); } + @Test public void testPriorityPersisted() throws Exception { JobInfo.Builder b = new Builder(92, mComponent) .setOverrideDeadline(5000) @@ -238,7 +287,8 @@ public class JobStoreTest extends AndroidTestCase { .setPersisted(true); final JobStatus js = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); mTaskStoreUnderTest.add(js); - Thread.sleep(IO_WAIT); + waitForPendingIo(); + final JobSet jobStatusSet = new JobSet(); mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); JobStatus loaded = jobStatusSet.getAllJobs().iterator().next(); @@ -248,6 +298,7 @@ public class JobStoreTest extends AndroidTestCase { /** * Test that non persisted job is not written to disk. */ + @Test public void testNonPersistedTaskIsNotPersisted() throws Exception { JobInfo.Builder b = new Builder(42, mComponent) .setOverrideDeadline(10000) @@ -259,7 +310,8 @@ public class JobStoreTest extends AndroidTestCase { .setPersisted(true); JobStatus jsPersisted = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null); mTaskStoreUnderTest.add(jsPersisted); - Thread.sleep(IO_WAIT); + waitForPendingIo(); + final JobSet jobStatusSet = new JobSet(); mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); assertEquals("Job count is incorrect.", 1, jobStatusSet.size()); @@ -267,6 +319,62 @@ public class JobStoreTest extends AndroidTestCase { assertEquals("Wrong job persisted.", 43, jobStatus.getJobId()); } + @Test + public void testRequiredNetworkType() throws Exception { + assertPersistedEquals(new JobInfo.Builder(0, mComponent) + .setPersisted(true) + .setRequiresDeviceIdle(true) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE).build()); + assertPersistedEquals(new JobInfo.Builder(0, mComponent) + .setPersisted(true) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build()); + assertPersistedEquals(new JobInfo.Builder(0, mComponent) + .setPersisted(true) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED).build()); + assertPersistedEquals(new JobInfo.Builder(0, mComponent) + .setPersisted(true) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING).build()); + assertPersistedEquals(new JobInfo.Builder(0, mComponent) + .setPersisted(true) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR).build()); + } + + @Test + public void testRequiredNetwork() throws Exception { + assertPersistedEquals(new JobInfo.Builder(0, mComponent) + .setPersisted(true) + .setRequiresDeviceIdle(true) + .setRequiredNetwork(null).build()); + assertPersistedEquals(new JobInfo.Builder(0, mComponent) + .setPersisted(true) + .setRequiredNetwork(new NetworkRequest.Builder().build()).build()); + assertPersistedEquals(new JobInfo.Builder(0, mComponent) + .setPersisted(true) + .setRequiredNetwork(new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build()) + .build()); + assertPersistedEquals(new JobInfo.Builder(0, mComponent) + .setPersisted(true) + .setRequiredNetwork(new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_IMS).build()) + .build()); + } + + /** + * Helper function to kick a {@link JobInfo} through a persistence cycle and + * assert that it's unchanged. + */ + private void assertPersistedEquals(JobInfo first) throws Exception { + mTaskStoreUnderTest.clear(); + mTaskStoreUnderTest.add(JobStatus.createFromJobInfo(first, SOME_UID, null, -1, null)); + waitForPendingIo(); + + final JobSet jobStatusSet = new JobSet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true); + final JobStatus second = jobStatusSet.getAllJobs().iterator().next(); + assertTasksEqual(first, second.getJob()); + } + /** * Helper function to throw an error if the provided task and TaskStatus objects are not equal. */ @@ -286,12 +394,10 @@ public class JobStoreTest extends AndroidTestCase { second.isRequireBatteryNotLow()); assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(), second.isRequireDeviceIdle()); - assertEquals("Invalid unmetered constraint.", - first.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED, - second.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED); - assertEquals("Invalid connectivity constraint.", - first.getNetworkType() == JobInfo.NETWORK_TYPE_ANY, - second.getNetworkType() == JobInfo.NETWORK_TYPE_ANY); + assertEquals("Invalid network type.", + first.getNetworkType(), second.getNetworkType()); + assertEquals("Invalid network.", + first.getRequiredNetwork(), second.getRequiredNetwork()); assertEquals("Invalid deadline constraint.", first.hasLateConstraint(), second.hasLateConstraint()); @@ -302,6 +408,26 @@ public class JobStoreTest extends AndroidTestCase { first.getExtras().toString(), second.getExtras().toString()); assertEquals("Transient xtras don't match", first.getTransientExtras().toString(), second.getTransientExtras().toString()); + + // Since people can forget to add tests here for new fields, do one last + // sanity check based on bits-on-wire equality. + final byte[] firstBytes = marshall(first); + final byte[] secondBytes = marshall(second); + if (!Arrays.equals(firstBytes, secondBytes)) { + Log.w(TAG, "First: " + HexDump.dumpHexString(firstBytes)); + Log.w(TAG, "Second: " + HexDump.dumpHexString(secondBytes)); + fail("Raw JobInfo aren't equal; see logs for details"); + } + } + + private static byte[] marshall(Parcelable p) { + final Parcel parcel = Parcel.obtain(); + try { + p.writeToParcel(parcel, 0); + return parcel.marshall(); + } finally { + parcel.recycle(); + } } /** @@ -312,7 +438,7 @@ public class JobStoreTest extends AndroidTestCase { */ private void compareTimestampsSubjectToIoLatency(String error, long ts1, long ts2) { final long DELTA_MILLIS = 700L; // We allow up to 700ms of latency for IO read/writes. - assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS + IO_WAIT); + assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS); } private static class StubClass {} diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 061e55a2e98e..0030ab6f9ef7 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1650,6 +1650,26 @@ public class CarrierConfigManager { public static final String KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL = "identify_high_definition_calls_in_call_log_bool"; + /** + * Flag specifying whether to use the {@link ServiceState} roaming status, which can be + * affected by other carrier configs (e.g. + * {@link #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY}), when setting the SPN display. + * <p> + * If {@code true}, the SPN display uses {@link ServiceState#getRoaming}. + * If {@code false} the SPN display checks if the current MCC/MNC is different from the + * SIM card's MCC/MNC. + * + * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY + * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY + * @see KEY_NON_ROAMING_OPERATOR_STRING_ARRAY + * @see KEY_ROAMING_OPERATOR_STRING_ARRAY + * @see KEY_FORCE_HOME_NETWORK_BOOL + * + * @hide + */ + public static final String KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL = + "spn_display_rule_use_roaming_from_service_state_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -1928,6 +1948,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false); sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null); sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false); + sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false); } /** diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java index b5484e344a1c..01041c8b1360 100644 --- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java +++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java @@ -53,6 +53,8 @@ public final class DownloadableSubscription implements Parcelable { @Nullable public final String encodedActivationCode; + @Nullable private String confirmationCode; + // see getCarrierName and setCarrierName @Nullable private String carrierName; @@ -66,6 +68,7 @@ public final class DownloadableSubscription implements Parcelable { private DownloadableSubscription(Parcel in) { encodedActivationCode = in.readString(); + confirmationCode = in.readString(); carrierName = in.readString(); accessRules = in.createTypedArray(UiccAccessRule.CREATOR); } @@ -83,6 +86,21 @@ public final class DownloadableSubscription implements Parcelable { } /** + * Sets the confirmation code. + */ + public void setConfirmationCode(String confirmationCode) { + this.confirmationCode = confirmationCode; + } + + /** + * Returns the confirmation code. + */ + @Nullable + public String getConfirmationCode() { + return confirmationCode; + } + + /** * Set the user-visible carrier name. * @hide * @@ -134,6 +152,7 @@ public final class DownloadableSubscription implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(encodedActivationCode); + dest.writeString(confirmationCode); dest.writeString(carrierName); dest.writeTypedArray(accessRules, flags); } diff --git a/tests/FeatureSplit/feature1/Android.mk b/tests/FeatureSplit/feature1/Android.mk index b3ea97b03d5d..e6ba5c2d04c9 100644 --- a/tests/FeatureSplit/feature1/Android.mk +++ b/tests/FeatureSplit/feature1/Android.mk @@ -26,6 +26,6 @@ LOCAL_APK_LIBRARIES := FeatureSplitBase LOCAL_RES_LIBRARIES := FeatureSplitBase LOCAL_AAPT_FLAGS += --package-id 0x80 -LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.test.split.feature +LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.one include $(BUILD_PACKAGE) diff --git a/tests/FeatureSplit/feature1/AndroidManifest.xml b/tests/FeatureSplit/feature1/AndroidManifest.xml index 4e7d15102a6e..b87361faac62 100644 --- a/tests/FeatureSplit/feature1/AndroidManifest.xml +++ b/tests/FeatureSplit/feature1/AndroidManifest.xml @@ -15,13 +15,13 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.split.feature.one" + package="com.android.test.split.feature" featureSplit="feature1"> <uses-sdk android:minSdkVersion="21" /> <application> - <activity android:name=".One" android:label="Feature One"> + <activity android:name=".one.One" android:label="Feature One"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/tests/FeatureSplit/feature1/res/values/values.xml b/tests/FeatureSplit/feature1/res/values/values.xml index 0e3e73c32480..7d58865b546e 100644 --- a/tests/FeatureSplit/feature1/res/values/values.xml +++ b/tests/FeatureSplit/feature1/res/values/values.xml @@ -20,7 +20,7 @@ <integer name="test_integer2">200</integer> <color name="test_color2">#00ff00</color> <string-array name="string_array2"> - <item>@*com.android.test.split.feature:string/app_title</item> + <item>@string/app_title</item> </string-array> </resources> diff --git a/tests/FeatureSplit/feature2/Android.mk b/tests/FeatureSplit/feature2/Android.mk index e2fd90384538..c8e860942fa3 100644 --- a/tests/FeatureSplit/feature2/Android.mk +++ b/tests/FeatureSplit/feature2/Android.mk @@ -26,6 +26,6 @@ LOCAL_APK_LIBRARIES := FeatureSplitBase LOCAL_RES_LIBRARIES := FeatureSplitBase LOCAL_AAPT_FLAGS += --package-id 0x81 -LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.test.split.feature +LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.two include $(BUILD_PACKAGE) diff --git a/tests/FeatureSplit/feature2/AndroidManifest.xml b/tests/FeatureSplit/feature2/AndroidManifest.xml index bfe6f38201bd..abd0b5eb6933 100644 --- a/tests/FeatureSplit/feature2/AndroidManifest.xml +++ b/tests/FeatureSplit/feature2/AndroidManifest.xml @@ -15,7 +15,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.test.split.feature.two" + package="com.android.test.split.feature" featureSplit="feature2"> <uses-sdk android:minSdkVersion="21" /> diff --git a/tests/FeatureSplit/feature2/res/values/values.xml b/tests/FeatureSplit/feature2/res/values/values.xml index 2fa6f907b8ab..af5ed1b79b26 100644 --- a/tests/FeatureSplit/feature2/res/values/values.xml +++ b/tests/FeatureSplit/feature2/res/values/values.xml @@ -18,7 +18,7 @@ <integer name="test_integer3">300</integer> <color name="test_color3">#0000ff</color> <string-array name="string_array3"> - <item>@*com.android.test.split.feature:string/app_title</item> + <item>@string/app_title</item> </string-array> </resources> diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp index b38d259d6b25..a782cd3672d1 100644 --- a/tools/aapt2/ResourceValues.cpp +++ b/tools/aapt2/ResourceValues.cpp @@ -118,7 +118,7 @@ bool Reference::Equals(const Value* value) const { bool Reference::Flatten(android::Res_value* out_value) const { const ResourceId resid = id.value_or_default(ResourceId(0)); const bool dynamic = resid.is_valid_dynamic() && resid.package_id() != kFrameworkPackageId && - resid.package_id() != kAppPackageId; + resid.package_id() < kAppPackageId; if (reference_type == Reference::Type::kResource) { if (dynamic) { diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index b372923e4010..66b5a7af3677 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -795,6 +795,9 @@ class LinkCommand { for (auto& entry : asset_source->GetAssignedPackageIds()) { if (entry.first > kFrameworkPackageId && entry.first < kAppPackageId) { final_table_.included_packages_[entry.first] = entry.second; + } else if (entry.first == kAppPackageId) { + // Capture the included base feature package. + included_feature_base_ = entry.second; } } @@ -1465,11 +1468,48 @@ class LinkCommand { return false; } - if (!FlattenTable(table, options_.output_format, writer)) { + // Hack to fix b/68820737. + // We need to modify the ResourceTable's package name, but that should NOT affect + // anything else being generated, which includes the Java classes. + // If required, the package name is modifed before flattening, and then modified back + // to its original name. + ResourceTablePackage* package_to_rewrite = nullptr; + if (context_->GetPackageId() > kAppPackageId && + included_feature_base_ == make_value(context_->GetCompilationPackage())) { + // The base APK is included, and this is a feature split. If the base package is + // the same as this package, then we are building an old style Android Instant Apps feature + // split and must apply this workaround to avoid requiring namespaces support. + package_to_rewrite = table->FindPackage(context_->GetCompilationPackage()); + if (package_to_rewrite != nullptr) { + CHECK_EQ(1u, table->packages.size()) << "can't change name of package when > 1 package"; + + std::string new_package_name = + StringPrintf("%s.%s", package_to_rewrite->name.c_str(), + app_info_.split_name.value_or_default("feature").c_str()); + + if (context_->IsVerbose()) { + context_->GetDiagnostics()->Note( + DiagMessage() << "rewriting resource package name for feature split to '" + << new_package_name << "'"); + } + package_to_rewrite->name = new_package_name; + } + } + + bool success = FlattenTable(table, options_.output_format, writer); + + if (package_to_rewrite != nullptr) { + // Change the name back. + package_to_rewrite->name = context_->GetCompilationPackage(); + if (package_to_rewrite->id) { + table->included_packages_.erase(package_to_rewrite->id.value()); + } + } + + if (!success) { context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resource table"); - return false; } - return true; + return success; } int Run(const std::vector<std::string>& input_files) { @@ -1498,8 +1538,8 @@ class LinkCommand { return 1; } - const AppInfo& app_info = maybe_app_info.value(); - context_->SetMinSdkVersion(app_info.min_sdk_version.value_or_default(0)); + app_info_ = maybe_app_info.value(); + context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0)); context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()}); @@ -1699,7 +1739,7 @@ class LinkCommand { // Generate an AndroidManifest.xml for each split. std::unique_ptr<xml::XmlResource> split_manifest = - GenerateSplitManifest(app_info, *split_constraints_iter); + GenerateSplitManifest(app_info_, *split_constraints_iter); XmlReferenceLinker linker; if (!linker.Consume(context_, split_manifest.get())) { @@ -1799,6 +1839,8 @@ class LinkCommand { LinkContext* context_; ResourceTable final_table_; + AppInfo app_info_; + std::unique_ptr<TableMerger> table_merger_; // A pointer to the FileCollection representing the filesystem (not archives). @@ -1816,6 +1858,9 @@ class LinkCommand { // The set of shared libraries being used, mapping their assigned package ID to package name. std::map<size_t, std::string> shared_libs_; + + // The package name of the base application, if it is included. + Maybe<std::string> included_feature_base_; }; int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) { diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 4a1b46c99d4b..2a51df3912e3 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -23,6 +23,7 @@ #include "android-base/logging.h" #include "android-base/macros.h" +#include "android-base/stringprintf.h" #include "ResourceTable.h" #include "ResourceValues.h" @@ -579,7 +580,15 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) { // Write a self mapping entry for this package if the ID is non-standard (0x7f). const uint8_t package_id = package->id.value(); if (package_id != kFrameworkPackageId && package_id != kAppPackageId) { - table->included_packages_[package_id] = package->name; + auto result = table->included_packages_.insert({package_id, package->name}); + if (!result.second && result.first->second != package->name) { + // A mapping for this package ID already exists, and is a different package. Error! + context->GetDiagnostics()->Error( + DiagMessage() << android::base::StringPrintf( + "can't map package ID %02x to '%s'. Already mapped to '%s'", package_id, + package->name.c_str(), result.first->second.c_str())); + return false; + } } } |