diff options
30 files changed, 769 insertions, 1515 deletions
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 8b39f5f3f37c..42d9e96c8cab 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -170,6 +170,12 @@ public: // Vendor pulled atom start id. static const int32_t kVendorPulledAtomStartTag = 150000; + // Beginning of range for timestamp truncation. + static const int32_t kTimestampTruncationStartTag = 300000; + + // End of range for timestamp truncation. + static const int32_t kTimestampTruncationEndTag = 304999; + // Max accepted atom id. static const int32_t kMaxAtomTag = 200000; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 69816cbbd92f..96133bd0a38d 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -146,16 +146,9 @@ void EventMetricProducer::onMatchedLogEventInternalLocked( uint64_t wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - const bool truncateTimestamp = - android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) == - android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end(); - if (truncateTimestamp) { - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, - (long long)truncateTimestampNsToFiveMinutes(event.GetElapsedTimestampNs())); - } else { - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, - (long long)event.GetElapsedTimestampNs()); - } + const int64_t elapsedTimeNs = truncateTimestampIfNecessary( + event.GetTagId(), event.GetElapsedTimestampNs()); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_ELAPSED_TIMESTAMP_NANOS, (long long) elapsedTimeNs); uint64_t eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS); event.ToProto(*mProto); diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 7a87f0381676..a64bbc1056e0 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -283,14 +283,9 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, writeFieldValueTreeToStream(mAtomId, *(atom.mFields), protoOutput); protoOutput->end(atomsToken); } - const bool truncateTimestamp = - android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find( - mAtomId) == - android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end(); for (const auto& atom : bucket.mGaugeAtoms) { - const int64_t elapsedTimestampNs = truncateTimestamp ? - truncateTimestampNsToFiveMinutes(atom.mElapsedTimestamps) : - atom.mElapsedTimestamps; + const int64_t elapsedTimestampNs = + truncateTimestampIfNecessary(mAtomId, atom.mElapsedTimestamps); protoOutput->write( FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP, (long long)elapsedTimestampNs); diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 31f160d99944..67625eb82454 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -545,8 +545,15 @@ int64_t getWallClockMillis() { return time(nullptr) * MS_PER_SEC; } -int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs) { - return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60); +int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs) { + if (AtomsInfo::kTruncatingTimestampAtomBlackList.find(atomId) != + AtomsInfo::kTruncatingTimestampAtomBlackList.end() || + (atomId >= StatsdStats::kTimestampTruncationStartTag && + atomId <= StatsdStats::kTimestampTruncationEndTag)) { + return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60); + } else { + return timestampNs; + } } int64_t NanoToMillis(const int64_t nano) { diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index 53dd5b792539..bfb84cf4d1b9 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -89,8 +89,9 @@ bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) { return message->ParseFromArray(pbBytes.c_str(), pbBytes.size()); } -// Returns the truncated timestamp. -int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs); +// Checks the blacklist of atoms as well as the blacklisted range of 300,000 - 304,999. +// Returns the truncated timestamp to the nearest 5 minutes if needed. +int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs); inline bool isVendorPulledAtom(int atomId) { return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag; diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index bb344e289657..e892b650bf40 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -18,7 +18,6 @@ package android.net; import static android.os.Process.CLAT_UID; -import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -34,7 +33,6 @@ import libcore.util.EmptyArray; import java.io.CharArrayWriter; import java.io.PrintWriter; import java.util.Arrays; -import java.util.function.Predicate; import java.util.HashSet; import java.util.Map; import java.util.Objects; @@ -995,33 +993,23 @@ public class NetworkStats implements Parcelable { if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) { return; } - filter(e -> (limitUid == UID_ALL || limitUid == e.uid) - && (limitTag == TAG_ALL || limitTag == e.tag) - && (limitIfaces == INTERFACES_ALL - || ArrayUtils.contains(limitIfaces, e.iface))); - } - - /** - * Only keep entries with {@link #set} value less than {@link #SET_DEBUG_START}. - * - * <p>This mutates the original structure in place. - */ - public void filterDebugEntries() { - filter(e -> e.set < SET_DEBUG_START); - } - private void filter(Predicate<Entry> predicate) { Entry entry = new Entry(); int nextOutputEntry = 0; for (int i = 0; i < size; i++) { entry = getValues(i, entry); - if (predicate.test(entry)) { - if (nextOutputEntry != i) { - setValues(nextOutputEntry, entry); - } + final boolean matches = + (limitUid == UID_ALL || limitUid == entry.uid) + && (limitTag == TAG_ALL || limitTag == entry.tag) + && (limitIfaces == INTERFACES_ALL + || ArrayUtils.contains(limitIfaces, entry.iface)); + + if (matches) { + setValues(nextOutputEntry, entry); nextOutputEntry++; } } + size = nextOutputEntry; } @@ -1187,221 +1175,133 @@ public class NetworkStats implements Parcelable { /** * VPN accounting. Move some VPN's underlying traffic to other UIDs that use tun0 iface. * - * <p>This method should only be called on delta NetworkStats. Do not call this method on a - * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may change - * over time. + * This method should only be called on delta NetworkStats. Do not call this method on a + * snapshot {@link NetworkStats} object because the tunUid and/or the underlyingIface may + * change over time. + * + * This method performs adjustments for one active VPN package and one VPN iface at a time. * - * <p>This method performs adjustments for one active VPN package and one VPN iface at a time. + * It is possible for the VPN software to use multiple underlying networks. This method + * only migrates traffic for the primary underlying network. * * @param tunUid uid of the VPN application * @param tunIface iface of the vpn tunnel - * @param underlyingIfaces underlying network ifaces used by the VPN application + * @param underlyingIface the primary underlying network iface used by the VPN application + * @return true if it successfully adjusts the accounting for VPN, false otherwise */ - public void migrateTun(int tunUid, @NonNull String tunIface, - @NonNull String[] underlyingIfaces) { - // Combined usage by all apps using VPN. - final Entry tunIfaceTotal = new Entry(); - // Usage by VPN, grouped by its {@code underlyingIfaces}. - final Entry[] perInterfaceTotal = new Entry[underlyingIfaces.length]; - // Usage by VPN, summed across all its {@code underlyingIfaces}. - final Entry underlyingIfacesTotal = new Entry(); - - for (int i = 0; i < perInterfaceTotal.length; i++) { - perInterfaceTotal[i] = new Entry(); - } + public boolean migrateTun(int tunUid, String tunIface, String underlyingIface) { + Entry tunIfaceTotal = new Entry(); + Entry underlyingIfaceTotal = new Entry(); - tunAdjustmentInit(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, perInterfaceTotal, - underlyingIfacesTotal); + tunAdjustmentInit(tunUid, tunIface, underlyingIface, tunIfaceTotal, underlyingIfaceTotal); - // If tunIface < underlyingIfacesTotal, it leaves the overhead traffic in the VPN app. - // If tunIface > underlyingIfacesTotal, the VPN app doesn't get credit for data compression. + // If tunIface < underlyingIface, it leaves the overhead traffic in the VPN app. + // If tunIface > underlyingIface, the VPN app doesn't get credit for data compression. // Negative stats should be avoided. - final Entry[] moved = - addTrafficToApplications(tunUid, tunIface, underlyingIfaces, tunIfaceTotal, - perInterfaceTotal, underlyingIfacesTotal); - deductTrafficFromVpnApp(tunUid, underlyingIfaces, moved); + Entry pool = tunGetPool(tunIfaceTotal, underlyingIfaceTotal); + if (pool.isEmpty()) { + return true; + } + Entry moved = + addTrafficToApplications(tunUid, tunIface, underlyingIface, tunIfaceTotal, pool); + deductTrafficFromVpnApp(tunUid, underlyingIface, moved); + + if (!moved.isEmpty()) { + Slog.wtf(TAG, "Failed to deduct underlying network traffic from VPN package. Moved=" + + moved); + return false; + } + return true; } /** * Initializes the data used by the migrateTun() method. * - * <p>This is the first pass iteration which does the following work: - * - * <ul> - * <li>Adds up all the traffic through the tunUid's underlyingIfaces (both foreground and - * background). - * <li>Adds up all the traffic through tun0 excluding traffic from the vpn app itself. - * </ul> - * - * @param tunUid uid of the VPN application - * @param tunIface iface of the vpn tunnel - * @param underlyingIfaces underlying network ifaces used by the VPN application - * @param tunIfaceTotal output parameter; combined data usage by all apps using VPN - * @param perInterfaceTotal output parameter; data usage by VPN app, grouped by its {@code - * underlyingIfaces} - * @param underlyingIfacesTotal output parameter; data usage by VPN, summed across all of its - * {@code underlyingIfaces} + * This is the first pass iteration which does the following work: + * (1) Adds up all the traffic through the tunUid's underlyingIface + * (both foreground and background). + * (2) Adds up all the traffic through tun0 excluding traffic from the vpn app itself. */ - private void tunAdjustmentInit(int tunUid, @NonNull String tunIface, - @NonNull String[] underlyingIfaces, @NonNull Entry tunIfaceTotal, - @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) { - final Entry recycle = new Entry(); + private void tunAdjustmentInit(int tunUid, String tunIface, String underlyingIface, + Entry tunIfaceTotal, Entry underlyingIfaceTotal) { + Entry recycle = new Entry(); for (int i = 0; i < size; i++) { getValues(i, recycle); if (recycle.uid == UID_ALL) { throw new IllegalStateException( "Cannot adjust VPN accounting on an iface aggregated NetworkStats."); - } - if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) { + } if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) { throw new IllegalStateException( "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*"); } - if (recycle.tag != TAG_NONE) { - // TODO(b/123666283): Take all tags for tunUid into account. - continue; + + if (recycle.uid == tunUid && recycle.tag == TAG_NONE + && Objects.equals(underlyingIface, recycle.iface)) { + underlyingIfaceTotal.add(recycle); } - if (recycle.uid == tunUid) { - // Add up traffic through tunUid's underlying interfaces. - for (int j = 0; j < underlyingIfaces.length; j++) { - if (Objects.equals(underlyingIfaces[j], recycle.iface)) { - perInterfaceTotal[j].add(recycle); - underlyingIfacesTotal.add(recycle); - break; - } - } - } else if (tunIface.equals(recycle.iface)) { + if (recycle.uid != tunUid && recycle.tag == TAG_NONE + && Objects.equals(tunIface, recycle.iface)) { // Add up all tunIface traffic excluding traffic from the vpn app itself. tunIfaceTotal.add(recycle); } } } - /** - * Distributes traffic across apps that are using given {@code tunIface}, and returns the total - * traffic that should be moved off of {@code tunUid} grouped by {@code underlyingIfaces}. - * - * @param tunUid uid of the VPN application - * @param tunIface iface of the vpn tunnel - * @param underlyingIfaces underlying network ifaces used by the VPN application - * @param tunIfaceTotal combined data usage across all apps using {@code tunIface} - * @param perInterfaceTotal data usage by VPN app, grouped by its {@code underlyingIfaces} - * @param underlyingIfacesTotal data usage by VPN, summed across all of its {@code - * underlyingIfaces} - */ - private Entry[] addTrafficToApplications(int tunUid, @NonNull String tunIface, - @NonNull String[] underlyingIfaces, @NonNull Entry tunIfaceTotal, - @NonNull Entry[] perInterfaceTotal, @NonNull Entry underlyingIfacesTotal) { - // Traffic that should be moved off of each underlying interface for tunUid (see - // deductTrafficFromVpnApp below). - final Entry[] moved = new Entry[underlyingIfaces.length]; - for (int i = 0; i < underlyingIfaces.length; i++) { - moved[i] = new Entry(); - } + private static Entry tunGetPool(Entry tunIfaceTotal, Entry underlyingIfaceTotal) { + Entry pool = new Entry(); + pool.rxBytes = Math.min(tunIfaceTotal.rxBytes, underlyingIfaceTotal.rxBytes); + pool.rxPackets = Math.min(tunIfaceTotal.rxPackets, underlyingIfaceTotal.rxPackets); + pool.txBytes = Math.min(tunIfaceTotal.txBytes, underlyingIfaceTotal.txBytes); + pool.txPackets = Math.min(tunIfaceTotal.txPackets, underlyingIfaceTotal.txPackets); + pool.operations = Math.min(tunIfaceTotal.operations, underlyingIfaceTotal.operations); + return pool; + } - final Entry tmpEntry = new Entry(); - final int origSize = size; - for (int i = 0; i < origSize; i++) { - if (!Objects.equals(iface[i], tunIface)) { - // Consider only entries that go onto the VPN interface. - continue; - } - if (uid[i] == tunUid) { - // Exclude VPN app from the redistribution, as it can choose to create packet - // streams by writing to itself. - continue; - } - tmpEntry.uid = uid[i]; - tmpEntry.tag = tag[i]; - tmpEntry.metered = metered[i]; - tmpEntry.roaming = roaming[i]; - tmpEntry.defaultNetwork = defaultNetwork[i]; - - // In a first pass, compute this entry's total share of data across all - // underlyingIfaces. This is computed on the basis of the share of this entry's usage - // over tunIface. - // TODO: Consider refactoring first pass into a separate helper method. - long totalRxBytes = 0; - if (tunIfaceTotal.rxBytes > 0) { - // Note - The multiplication below should not overflow since NetworkStatsService - // processes this every time device has transmitted/received amount equivalent to - // global threshold alert (~ 2MB) across all interfaces. - final long rxBytesAcrossUnderlyingIfaces = - underlyingIfacesTotal.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes; - // app must not be blamed for more than it consumed on tunIface - totalRxBytes = Math.min(rxBytes[i], rxBytesAcrossUnderlyingIfaces); - } - long totalRxPackets = 0; - if (tunIfaceTotal.rxPackets > 0) { - final long rxPacketsAcrossUnderlyingIfaces = - underlyingIfacesTotal.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets; - totalRxPackets = Math.min(rxPackets[i], rxPacketsAcrossUnderlyingIfaces); - } - long totalTxBytes = 0; - if (tunIfaceTotal.txBytes > 0) { - final long txBytesAcrossUnderlyingIfaces = - underlyingIfacesTotal.txBytes * txBytes[i] / tunIfaceTotal.txBytes; - totalTxBytes = Math.min(txBytes[i], txBytesAcrossUnderlyingIfaces); - } - long totalTxPackets = 0; - if (tunIfaceTotal.txPackets > 0) { - final long txPacketsAcrossUnderlyingIfaces = - underlyingIfacesTotal.txPackets * txPackets[i] / tunIfaceTotal.txPackets; - totalTxPackets = Math.min(txPackets[i], txPacketsAcrossUnderlyingIfaces); - } - long totalOperations = 0; - if (tunIfaceTotal.operations > 0) { - final long operationsAcrossUnderlyingIfaces = - underlyingIfacesTotal.operations * operations[i] / tunIfaceTotal.operations; - totalOperations = Math.min(operations[i], operationsAcrossUnderlyingIfaces); - } - // In a second pass, distribute these values across interfaces in the proportion that - // each interface represents of the total traffic of the underlying interfaces. - for (int j = 0; j < underlyingIfaces.length; j++) { - tmpEntry.iface = underlyingIfaces[j]; - tmpEntry.rxBytes = 0; - // Reset 'set' to correct value since it gets updated when adding debug info below. - tmpEntry.set = set[i]; - if (underlyingIfacesTotal.rxBytes > 0) { - tmpEntry.rxBytes = - totalRxBytes - * perInterfaceTotal[j].rxBytes - / underlyingIfacesTotal.rxBytes; + private Entry addTrafficToApplications(int tunUid, String tunIface, String underlyingIface, + Entry tunIfaceTotal, Entry pool) { + Entry moved = new Entry(); + Entry tmpEntry = new Entry(); + tmpEntry.iface = underlyingIface; + for (int i = 0; i < size; i++) { + // the vpn app is excluded from the redistribution but all moved traffic will be + // deducted from the vpn app (see deductTrafficFromVpnApp below). + if (Objects.equals(iface[i], tunIface) && uid[i] != tunUid) { + if (tunIfaceTotal.rxBytes > 0) { + tmpEntry.rxBytes = pool.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes; + } else { + tmpEntry.rxBytes = 0; } - tmpEntry.rxPackets = 0; - if (underlyingIfacesTotal.rxPackets > 0) { - tmpEntry.rxPackets = - totalRxPackets - * perInterfaceTotal[j].rxPackets - / underlyingIfacesTotal.rxPackets; + if (tunIfaceTotal.rxPackets > 0) { + tmpEntry.rxPackets = pool.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets; + } else { + tmpEntry.rxPackets = 0; } - tmpEntry.txBytes = 0; - if (underlyingIfacesTotal.txBytes > 0) { - tmpEntry.txBytes = - totalTxBytes - * perInterfaceTotal[j].txBytes - / underlyingIfacesTotal.txBytes; + if (tunIfaceTotal.txBytes > 0) { + tmpEntry.txBytes = pool.txBytes * txBytes[i] / tunIfaceTotal.txBytes; + } else { + tmpEntry.txBytes = 0; } - tmpEntry.txPackets = 0; - if (underlyingIfacesTotal.txPackets > 0) { - tmpEntry.txPackets = - totalTxPackets - * perInterfaceTotal[j].txPackets - / underlyingIfacesTotal.txPackets; + if (tunIfaceTotal.txPackets > 0) { + tmpEntry.txPackets = pool.txPackets * txPackets[i] / tunIfaceTotal.txPackets; + } else { + tmpEntry.txPackets = 0; } - tmpEntry.operations = 0; - if (underlyingIfacesTotal.operations > 0) { + if (tunIfaceTotal.operations > 0) { tmpEntry.operations = - totalOperations - * perInterfaceTotal[j].operations - / underlyingIfacesTotal.operations; + pool.operations * operations[i] / tunIfaceTotal.operations; + } else { + tmpEntry.operations = 0; } - // tmpEntry now contains the migrated data of the i-th entry for the j-th underlying - // interface. Add that data usage to this object. + tmpEntry.uid = uid[i]; + tmpEntry.tag = tag[i]; + tmpEntry.set = set[i]; + tmpEntry.metered = metered[i]; + tmpEntry.roaming = roaming[i]; + tmpEntry.defaultNetwork = defaultNetwork[i]; combineValues(tmpEntry); if (tag[i] == TAG_NONE) { - // Add the migrated data to moved so it is deducted from the VPN app later. - moved[j].add(tmpEntry); + moved.add(tmpEntry); // Add debug info tmpEntry.set = SET_DBG_VPN_IN; combineValues(tmpEntry); @@ -1411,45 +1311,38 @@ public class NetworkStats implements Parcelable { return moved; } - private void deductTrafficFromVpnApp( - int tunUid, - @NonNull String[] underlyingIfaces, - @NonNull Entry[] moved) { - for (int i = 0; i < underlyingIfaces.length; i++) { - moved[i].uid = tunUid; - // Add debug info - moved[i].set = SET_DBG_VPN_OUT; - moved[i].tag = TAG_NONE; - moved[i].iface = underlyingIfaces[i]; - moved[i].metered = METERED_ALL; - moved[i].roaming = ROAMING_ALL; - moved[i].defaultNetwork = DEFAULT_NETWORK_ALL; - combineValues(moved[i]); - - // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than - // the TAG_NONE traffic. - // - // Relies on the fact that the underlying traffic only has state ROAMING_NO and - // METERED_NO, which should be the case as it comes directly from the /proc file. - // We only blend in the roaming data after applying these adjustments, by checking the - // NetworkIdentity of the underlying iface. - final int idxVpnBackground = findIndex(underlyingIfaces[i], tunUid, SET_DEFAULT, - TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); - if (idxVpnBackground != -1) { - // Note - tunSubtract also updates moved[i]; whatever traffic that's left is removed - // from foreground usage. - tunSubtract(idxVpnBackground, this, moved[i]); - } + private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) { + // Add debug info + moved.uid = tunUid; + moved.set = SET_DBG_VPN_OUT; + moved.tag = TAG_NONE; + moved.iface = underlyingIface; + moved.metered = METERED_ALL; + moved.roaming = ROAMING_ALL; + moved.defaultNetwork = DEFAULT_NETWORK_ALL; + combineValues(moved); + + // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than + // the TAG_NONE traffic. + // + // Relies on the fact that the underlying traffic only has state ROAMING_NO and METERED_NO, + // which should be the case as it comes directly from the /proc file. We only blend in the + // roaming data after applying these adjustments, by checking the NetworkIdentity of the + // underlying iface. + int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); + if (idxVpnBackground != -1) { + tunSubtract(idxVpnBackground, this, moved); + } - final int idxVpnForeground = findIndex(underlyingIfaces[i], tunUid, SET_FOREGROUND, - TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); - if (idxVpnForeground != -1) { - tunSubtract(idxVpnForeground, this, moved[i]); - } + int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, + METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO); + if (idxVpnForeground != -1) { + tunSubtract(idxVpnForeground, this, moved); } } - private static void tunSubtract(int i, @NonNull NetworkStats left, @NonNull Entry right) { + private static void tunSubtract(int i, NetworkStats left, Entry right) { long rxBytes = Math.min(left.rxBytes[i], right.rxBytes); left.rxBytes[i] -= rxBytes; right.rxBytes -= rxBytes; diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS new file mode 100644 index 000000000000..8b7d6ad851f9 --- /dev/null +++ b/core/java/android/provider/OWNERS @@ -0,0 +1,4 @@ +per-file DeviceConfig.java = svetoslavganov@google.com +per-file DeviceConfig.java = hackbod@google.com + + diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 3ec21e39e514..e02fd9fc5413 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -42,7 +42,6 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Build; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -53,7 +52,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.Log; import android.widget.RemoteViews; @@ -64,8 +62,8 @@ import com.android.internal.os.SomeArgs; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Objects; /** * A service that receives calls from the system when new notifications are @@ -1442,7 +1440,7 @@ public abstract class NotificationListenerService extends Service { */ @GuardedBy("mLock") public final void applyUpdateLocked(NotificationRankingUpdate update) { - mRankingMap = new RankingMap(update); + mRankingMap = update.getRankingMap(); } /** @hide */ @@ -1480,14 +1478,14 @@ public abstract class NotificationListenerService extends Service { */ public static final int USER_SENTIMENT_POSITIVE = 1; - /** @hide */ + /** @hide */ @IntDef(prefix = { "USER_SENTIMENT_" }, value = { USER_SENTIMENT_NEGATIVE, USER_SENTIMENT_NEUTRAL, USER_SENTIMENT_POSITIVE }) @Retention(RetentionPolicy.SOURCE) public @interface UserSentiment {} - private String mKey; + private @NonNull String mKey; private int mRank = -1; private boolean mIsAmbient; private boolean mMatchesInterruptionFilter; @@ -1512,7 +1510,70 @@ public abstract class NotificationListenerService extends Service { private ArrayList<CharSequence> mSmartReplies; private boolean mCanBubble; - public Ranking() {} + private static final int PARCEL_VERSION = 2; + + public Ranking() { } + + // You can parcel it, but it's not Parcelable + /** @hide */ + @VisibleForTesting + public void writeToParcel(Parcel out, int flags) { + final long start = out.dataPosition(); + out.writeInt(PARCEL_VERSION); + out.writeString(mKey); + out.writeInt(mRank); + out.writeBoolean(mIsAmbient); + out.writeBoolean(mMatchesInterruptionFilter); + out.writeInt(mVisibilityOverride); + out.writeInt(mSuppressedVisualEffects); + out.writeInt(mImportance); + out.writeCharSequence(mImportanceExplanation); + out.writeString(mOverrideGroupKey); + out.writeParcelable(mChannel, flags); + out.writeStringList(mOverridePeople); + out.writeTypedList(mSnoozeCriteria, flags); + out.writeBoolean(mShowBadge); + out.writeInt(mUserSentiment); + out.writeBoolean(mHidden); + out.writeLong(mLastAudiblyAlertedMs); + out.writeBoolean(mNoisy); + out.writeTypedList(mSmartActions, flags); + out.writeCharSequenceList(mSmartReplies); + out.writeBoolean(mCanBubble); + } + + /** @hide */ + @VisibleForTesting + public Ranking(Parcel in) { + final ClassLoader cl = getClass().getClassLoader(); + + final int version = in.readInt(); + if (version != PARCEL_VERSION) { + throw new IllegalArgumentException("malformed Ranking parcel: " + in + " version " + + version + ", expected " + PARCEL_VERSION); + } + mKey = in.readString(); + mRank = in.readInt(); + mIsAmbient = in.readBoolean(); + mMatchesInterruptionFilter = in.readBoolean(); + mVisibilityOverride = in.readInt(); + mSuppressedVisualEffects = in.readInt(); + mImportance = in.readInt(); + mImportanceExplanation = in.readCharSequence(); // may be null + mOverrideGroupKey = in.readString(); // may be null + mChannel = (NotificationChannel) in.readParcelable(cl); // may be null + mOverridePeople = in.createStringArrayList(); + mSnoozeCriteria = in.createTypedArrayList(SnoozeCriterion.CREATOR); + mShowBadge = in.readBoolean(); + mUserSentiment = in.readInt(); + mHidden = in.readBoolean(); + mLastAudiblyAlertedMs = in.readLong(); + mNoisy = in.readBoolean(); + mSmartActions = in.createTypedArrayList(Notification.Action.CREATOR); + mSmartReplies = in.readCharSequenceList(); + mCanBubble = in.readBoolean(); + } + /** * Returns the key of the notification this Ranking applies to. @@ -1737,6 +1798,31 @@ public abstract class NotificationListenerService extends Service { } /** + * @hide + */ + public void populate(Ranking other) { + populate(other.mKey, + other.mRank, + other.mMatchesInterruptionFilter, + other.mVisibilityOverride, + other.mSuppressedVisualEffects, + other.mImportance, + other.mImportanceExplanation, + other.mOverrideGroupKey, + other.mChannel, + other.mOverridePeople, + other.mSnoozeCriteria, + other.mShowBadge, + other.mUserSentiment, + other.mHidden, + other.mLastAudiblyAlertedMs, + other.mNoisy, + other.mSmartActions, + other.mSmartReplies, + other.mCanBubble); + } + + /** * {@hide} */ public static String importanceToString(int importance) { @@ -1758,6 +1844,35 @@ public abstract class NotificationListenerService extends Service { return "UNKNOWN(" + String.valueOf(importance) + ")"; } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Ranking other = (Ranking) o; + return Objects.equals(mKey, other.mKey) + && Objects.equals(mRank, other.mRank) + && Objects.equals(mMatchesInterruptionFilter, other.mMatchesInterruptionFilter) + && Objects.equals(mVisibilityOverride, other.mVisibilityOverride) + && Objects.equals(mSuppressedVisualEffects, other.mSuppressedVisualEffects) + && Objects.equals(mImportance, other.mImportance) + && Objects.equals(mImportanceExplanation, other.mImportanceExplanation) + && Objects.equals(mOverrideGroupKey, other.mOverrideGroupKey) + && Objects.equals(mChannel, other.mChannel) + && Objects.equals(mOverridePeople, other.mOverridePeople) + && Objects.equals(mSnoozeCriteria, other.mSnoozeCriteria) + && Objects.equals(mShowBadge, other.mShowBadge) + && Objects.equals(mUserSentiment, other.mUserSentiment) + && Objects.equals(mHidden, other.mHidden) + && Objects.equals(mLastAudiblyAlertedMs, other.mLastAudiblyAlertedMs) + && Objects.equals(mNoisy, other.mNoisy) + // Action.equals() doesn't exist so let's just compare list lengths + && ((mSmartActions == null ? 0 : mSmartActions.size()) + == (other.mSmartActions == null ? 0 : other.mSmartActions.size())) + && Objects.equals(mSmartReplies, other.mSmartReplies) + && Objects.equals(mCanBubble, other.mCanBubble); + } } /** @@ -1769,413 +1884,66 @@ public abstract class NotificationListenerService extends Service { * notifications active at the time of retrieval. */ public static class RankingMap implements Parcelable { - private final NotificationRankingUpdate mRankingUpdate; - private ArrayMap<String,Integer> mRanks; - private ArraySet<Object> mIntercepted; - private ArrayMap<String, Integer> mVisibilityOverrides; - private ArrayMap<String, Integer> mSuppressedVisualEffects; - private ArrayMap<String, Integer> mImportance; - private ArrayMap<String, String> mImportanceExplanation; - private ArrayMap<String, String> mOverrideGroupKeys; - private ArrayMap<String, NotificationChannel> mChannels; - private ArrayMap<String, ArrayList<String>> mOverridePeople; - private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria; - private ArrayMap<String, Boolean> mShowBadge; - private ArrayMap<String, Integer> mUserSentiment; - private ArrayMap<String, Boolean> mHidden; - private ArrayMap<String, Long> mLastAudiblyAlerted; - private ArrayMap<String, Boolean> mNoisy; - private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions; - private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies; - private boolean[] mCanBubble; - - private RankingMap(NotificationRankingUpdate rankingUpdate) { - mRankingUpdate = rankingUpdate; - } - - /** - * Request the list of notification keys in their current ranking - * order. - * - * @return An array of active notification keys, in their ranking order. - */ - public String[] getOrderedKeys() { - return mRankingUpdate.getOrderedKeys(); - } + private ArrayList<String> mOrderedKeys = new ArrayList<>(); + // Note: all String keys should be intern'd as pointers into mOrderedKeys + private ArrayMap<String, Ranking> mRankings = new ArrayMap<>(); /** - * Populates outRanking with ranking information for the notification - * with the given key. - * - * @return true if a valid key has been passed and outRanking has - * been populated; false otherwise + * @hide */ - public boolean getRanking(String key, Ranking outRanking) { - int rank = getRank(key); - outRanking.populate(key, rank, !isIntercepted(key), - getVisibilityOverride(key), getSuppressedVisualEffects(key), - getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key), - getChannel(key), getOverridePeople(key), getSnoozeCriteria(key), - getShowBadge(key), getUserSentiment(key), getHidden(key), - getLastAudiblyAlerted(key), getNoisy(key), getSmartActions(key), - getSmartReplies(key), canBubble(key)); - return rank >= 0; - } - - private int getRank(String key) { - synchronized (this) { - if (mRanks == null) { - buildRanksLocked(); - } - } - Integer rank = mRanks.get(key); - return rank != null ? rank : -1; - } - - private boolean isIntercepted(String key) { - synchronized (this) { - if (mIntercepted == null) { - buildInterceptedSetLocked(); - } - } - return mIntercepted.contains(key); - } - - private int getVisibilityOverride(String key) { - synchronized (this) { - if (mVisibilityOverrides == null) { - buildVisibilityOverridesLocked(); - } - } - Integer override = mVisibilityOverrides.get(key); - if (override == null) { - return Ranking.VISIBILITY_NO_OVERRIDE; - } - return override.intValue(); - } - - private int getSuppressedVisualEffects(String key) { - synchronized (this) { - if (mSuppressedVisualEffects == null) { - buildSuppressedVisualEffectsLocked(); - } - } - Integer suppressed = mSuppressedVisualEffects.get(key); - if (suppressed == null) { - return 0; - } - return suppressed.intValue(); - } - - private int getImportance(String key) { - synchronized (this) { - if (mImportance == null) { - buildImportanceLocked(); - } - } - Integer importance = mImportance.get(key); - if (importance == null) { - return NotificationManager.IMPORTANCE_DEFAULT; - } - return importance.intValue(); - } - - private String getImportanceExplanation(String key) { - synchronized (this) { - if (mImportanceExplanation == null) { - buildImportanceExplanationLocked(); - } - } - return mImportanceExplanation.get(key); - } - - private String getOverrideGroupKey(String key) { - synchronized (this) { - if (mOverrideGroupKeys == null) { - buildOverrideGroupKeys(); - } - } - return mOverrideGroupKeys.get(key); - } - - private NotificationChannel getChannel(String key) { - synchronized (this) { - if (mChannels == null) { - buildChannelsLocked(); - } - } - return mChannels.get(key); - } - - private ArrayList<String> getOverridePeople(String key) { - synchronized (this) { - if (mOverridePeople == null) { - buildOverridePeopleLocked(); - } - } - return mOverridePeople.get(key); - } - - private ArrayList<SnoozeCriterion> getSnoozeCriteria(String key) { - synchronized (this) { - if (mSnoozeCriteria == null) { - buildSnoozeCriteriaLocked(); - } - } - return mSnoozeCriteria.get(key); - } - - private boolean getShowBadge(String key) { - synchronized (this) { - if (mShowBadge == null) { - buildShowBadgeLocked(); - } - } - Boolean showBadge = mShowBadge.get(key); - return showBadge == null ? false : showBadge.booleanValue(); - } - - private int getUserSentiment(String key) { - synchronized (this) { - if (mUserSentiment == null) { - buildUserSentimentLocked(); - } - } - Integer userSentiment = mUserSentiment.get(key); - return userSentiment == null - ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue(); - } - - private boolean getHidden(String key) { - synchronized (this) { - if (mHidden == null) { - buildHiddenLocked(); - } - } - Boolean hidden = mHidden.get(key); - return hidden == null ? false : hidden.booleanValue(); - } - - private long getLastAudiblyAlerted(String key) { - synchronized (this) { - if (mLastAudiblyAlerted == null) { - buildLastAudiblyAlertedLocked(); - } - } - Long lastAudibleAlerted = mLastAudiblyAlerted.get(key); - return lastAudibleAlerted == null ? -1 : lastAudibleAlerted.longValue(); - } - - private boolean getNoisy(String key) { - synchronized (this) { - if (mNoisy == null) { - buildNoisyLocked(); - } - } - Boolean noisy = mNoisy.get(key); - return noisy == null ? false : noisy.booleanValue(); - } - - private ArrayList<Notification.Action> getSmartActions(String key) { - synchronized (this) { - if (mSmartActions == null) { - buildSmartActions(); - } - } - return mSmartActions.get(key); - } - - private ArrayList<CharSequence> getSmartReplies(String key) { - synchronized (this) { - if (mSmartReplies == null) { - buildSmartReplies(); - } - } - return mSmartReplies.get(key); - } - - private boolean canBubble(String key) { - synchronized (this) { - if (mRanks == null) { - buildRanksLocked(); - } - if (mCanBubble == null) { - mCanBubble = mRankingUpdate.getCanBubble(); - } - } - int keyIndex = mRanks.getOrDefault(key, -1); - return keyIndex >= 0 ? mCanBubble[keyIndex] : false; - } - - // Locked by 'this' - private void buildRanksLocked() { - String[] orderedKeys = mRankingUpdate.getOrderedKeys(); - mRanks = new ArrayMap<>(orderedKeys.length); - for (int i = 0; i < orderedKeys.length; i++) { - String key = orderedKeys[i]; - mRanks.put(key, i); - } - } - - // Locked by 'this' - private void buildInterceptedSetLocked() { - String[] dndInterceptedKeys = mRankingUpdate.getInterceptedKeys(); - mIntercepted = new ArraySet<>(dndInterceptedKeys.length); - Collections.addAll(mIntercepted, dndInterceptedKeys); - } - - private ArrayMap<String, Integer> buildIntMapFromBundle(Bundle bundle) { - ArrayMap<String, Integer> newMap = new ArrayMap<>(bundle.size()); - for (String key : bundle.keySet()) { - newMap.put(key, bundle.getInt(key)); - } - return newMap; - } - - private ArrayMap<String, String> buildStringMapFromBundle(Bundle bundle) { - ArrayMap<String, String> newMap = new ArrayMap<>(bundle.size()); - for (String key : bundle.keySet()) { - newMap.put(key, bundle.getString(key)); - } - return newMap; - } - - private ArrayMap<String, Boolean> buildBooleanMapFromBundle(Bundle bundle) { - ArrayMap<String, Boolean> newMap = new ArrayMap<>(bundle.size()); - for (String key : bundle.keySet()) { - newMap.put(key, bundle.getBoolean(key)); - } - return newMap; - } - - private ArrayMap<String, Long> buildLongMapFromBundle(Bundle bundle) { - ArrayMap<String, Long> newMap = new ArrayMap<>(bundle.size()); - for (String key : bundle.keySet()) { - newMap.put(key, bundle.getLong(key)); - } - return newMap; - } - - // Locked by 'this' - private void buildVisibilityOverridesLocked() { - mVisibilityOverrides = buildIntMapFromBundle(mRankingUpdate.getVisibilityOverrides()); - } - - // Locked by 'this' - private void buildSuppressedVisualEffectsLocked() { - mSuppressedVisualEffects = - buildIntMapFromBundle(mRankingUpdate.getSuppressedVisualEffects()); - } - - // Locked by 'this' - private void buildImportanceLocked() { - String[] orderedKeys = mRankingUpdate.getOrderedKeys(); - int[] importance = mRankingUpdate.getImportance(); - mImportance = new ArrayMap<>(orderedKeys.length); - for (int i = 0; i < orderedKeys.length; i++) { - String key = orderedKeys[i]; - mImportance.put(key, importance[i]); - } - } - - // Locked by 'this' - private void buildImportanceExplanationLocked() { - mImportanceExplanation = - buildStringMapFromBundle(mRankingUpdate.getImportanceExplanation()); - } - - // Locked by 'this' - private void buildOverrideGroupKeys() { - mOverrideGroupKeys = buildStringMapFromBundle(mRankingUpdate.getOverrideGroupKeys()); - } - - // Locked by 'this' - private void buildChannelsLocked() { - Bundle channels = mRankingUpdate.getChannels(); - mChannels = new ArrayMap<>(channels.size()); - for (String key : channels.keySet()) { - mChannels.put(key, channels.getParcelable(key)); + public RankingMap(Ranking[] rankings) { + for (int i = 0; i < rankings.length; i++) { + final String key = rankings[i].getKey(); + mOrderedKeys.add(key); + mRankings.put(key, rankings[i]); } } - // Locked by 'this' - private void buildOverridePeopleLocked() { - Bundle overridePeople = mRankingUpdate.getOverridePeople(); - mOverridePeople = new ArrayMap<>(overridePeople.size()); - for (String key : overridePeople.keySet()) { - mOverridePeople.put(key, overridePeople.getStringArrayList(key)); - } - } + // -- parcelable interface -- - // Locked by 'this' - private void buildSnoozeCriteriaLocked() { - Bundle snoozeCriteria = mRankingUpdate.getSnoozeCriteria(); - mSnoozeCriteria = new ArrayMap<>(snoozeCriteria.size()); - for (String key : snoozeCriteria.keySet()) { - mSnoozeCriteria.put(key, snoozeCriteria.getParcelableArrayList(key)); + private RankingMap(Parcel in) { + final ClassLoader cl = getClass().getClassLoader(); + final int count = in.readInt(); + mOrderedKeys.ensureCapacity(count); + mRankings.ensureCapacity(count); + for (int i = 0; i < count; i++) { + final Ranking r = new Ranking(in); + final String key = r.getKey(); + mOrderedKeys.add(key); + mRankings.put(key, r); } } - // Locked by 'this' - private void buildShowBadgeLocked() { - mShowBadge = buildBooleanMapFromBundle(mRankingUpdate.getShowBadge()); - } - - // Locked by 'this' - private void buildUserSentimentLocked() { - mUserSentiment = buildIntMapFromBundle(mRankingUpdate.getUserSentiment()); - } - - // Locked by 'this' - private void buildHiddenLocked() { - mHidden = buildBooleanMapFromBundle(mRankingUpdate.getHidden()); - } - - // Locked by 'this' - private void buildLastAudiblyAlertedLocked() { - mLastAudiblyAlerted = buildLongMapFromBundle(mRankingUpdate.getLastAudiblyAlerted()); - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - // Locked by 'this' - private void buildNoisyLocked() { - mNoisy = buildBooleanMapFromBundle(mRankingUpdate.getNoisy()); - } + RankingMap other = (RankingMap) o; - // Locked by 'this' - private void buildSmartActions() { - Bundle smartActions = mRankingUpdate.getSmartActions(); - mSmartActions = new ArrayMap<>(smartActions.size()); - for (String key : smartActions.keySet()) { - mSmartActions.put(key, smartActions.getParcelableArrayList(key)); - } - } + return mOrderedKeys.equals(other.mOrderedKeys) + && mRankings.equals(other.mRankings); - // Locked by 'this' - private void buildSmartReplies() { - Bundle smartReplies = mRankingUpdate.getSmartReplies(); - mSmartReplies = new ArrayMap<>(smartReplies.size()); - for (String key : smartReplies.keySet()) { - mSmartReplies.put(key, smartReplies.getCharSequenceArrayList(key)); - } } - // ----------- Parcelable - @Override public int describeContents() { return 0; } @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(mRankingUpdate, flags); + public void writeToParcel(Parcel out, int flags) { + final int count = mOrderedKeys.size(); + out.writeInt(count); + for (int i = 0; i < count; i++) { + mRankings.get(mOrderedKeys.get(i)).writeToParcel(out, flags); + } } public static final @android.annotation.NonNull Creator<RankingMap> CREATOR = new Creator<RankingMap>() { @Override public RankingMap createFromParcel(Parcel source) { - NotificationRankingUpdate rankingUpdate = source.readParcelable(null); - return new RankingMap(rankingUpdate); + return new RankingMap(source); } @Override @@ -2183,6 +1951,42 @@ public abstract class NotificationListenerService extends Service { return new RankingMap[size]; } }; + + /** + * Request the list of notification keys in their current ranking + * order. + * + * @return An array of active notification keys, in their ranking order. + */ + public String[] getOrderedKeys() { + return mOrderedKeys.toArray(new String[0]); + } + + /** + * Populates outRanking with ranking information for the notification + * with the given key. + * + * @return true if a valid key has been passed and outRanking has + * been populated; false otherwise + */ + public boolean getRanking(String key, Ranking outRanking) { + if (mRankings.containsKey(key)) { + outRanking.populate(mRankings.get(key)); + return true; + } + return false; + } + + /** + * Get a reference to the actual Ranking object corresponding to the key. + * Used only by unit tests. + * + * @hide + */ + @VisibleForTesting + public Ranking getRawRankingObject(String key) { + return mRankings.get(key); + } } private final class MyHandler extends Handler { diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index c5c70f808325..675c5cd63100 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -15,7 +15,6 @@ */ package android.service.notification; -import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -23,73 +22,18 @@ import android.os.Parcelable; * @hide */ public class NotificationRankingUpdate implements Parcelable { - // TODO: Support incremental updates. - private final String[] mKeys; - private final String[] mInterceptedKeys; - private final Bundle mVisibilityOverrides; - private final Bundle mSuppressedVisualEffects; - private final int[] mImportance; - private final Bundle mImportanceExplanation; - private final Bundle mOverrideGroupKeys; - private final Bundle mChannels; - private final Bundle mOverridePeople; - private final Bundle mSnoozeCriteria; - private final Bundle mShowBadge; - private final Bundle mUserSentiment; - private final Bundle mHidden; - private final Bundle mSmartActions; - private final Bundle mSmartReplies; - private final Bundle mLastAudiblyAlerted; - private final Bundle mNoisy; - private final boolean[] mCanBubble; + private final NotificationListenerService.RankingMap mRankingMap; - public NotificationRankingUpdate(String[] keys, String[] interceptedKeys, - Bundle visibilityOverrides, Bundle suppressedVisualEffects, - int[] importance, Bundle explanation, Bundle overrideGroupKeys, - Bundle channels, Bundle overridePeople, Bundle snoozeCriteria, - Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions, - Bundle smartReplies, Bundle lastAudiblyAlerted, Bundle noisy, boolean[] canBubble) { - mKeys = keys; - mInterceptedKeys = interceptedKeys; - mVisibilityOverrides = visibilityOverrides; - mSuppressedVisualEffects = suppressedVisualEffects; - mImportance = importance; - mImportanceExplanation = explanation; - mOverrideGroupKeys = overrideGroupKeys; - mChannels = channels; - mOverridePeople = overridePeople; - mSnoozeCriteria = snoozeCriteria; - mShowBadge = showBadge; - mUserSentiment = userSentiment; - mHidden = hidden; - mSmartActions = smartActions; - mSmartReplies = smartReplies; - mLastAudiblyAlerted = lastAudiblyAlerted; - mNoisy = noisy; - mCanBubble = canBubble; + public NotificationRankingUpdate(NotificationListenerService.Ranking[] rankings) { + mRankingMap = new NotificationListenerService.RankingMap(rankings); } public NotificationRankingUpdate(Parcel in) { - mKeys = in.readStringArray(); - mInterceptedKeys = in.readStringArray(); - mVisibilityOverrides = in.readBundle(); - mSuppressedVisualEffects = in.readBundle(); - mImportance = new int[mKeys.length]; - in.readIntArray(mImportance); - mImportanceExplanation = in.readBundle(); - mOverrideGroupKeys = in.readBundle(); - mChannels = in.readBundle(); - mOverridePeople = in.readBundle(); - mSnoozeCriteria = in.readBundle(); - mShowBadge = in.readBundle(); - mUserSentiment = in.readBundle(); - mHidden = in.readBundle(); - mSmartActions = in.readBundle(); - mSmartReplies = in.readBundle(); - mLastAudiblyAlerted = in.readBundle(); - mNoisy = in.readBundle(); - mCanBubble = new boolean[mKeys.length]; - in.readBooleanArray(mCanBubble); + mRankingMap = in.readParcelable(getClass().getClassLoader()); + } + + public NotificationListenerService.RankingMap getRankingMap() { + return mRankingMap; } @Override @@ -98,25 +42,17 @@ public class NotificationRankingUpdate implements Parcelable { } @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NotificationRankingUpdate other = (NotificationRankingUpdate) o; + return mRankingMap.equals(other.mRankingMap); + } + + @Override public void writeToParcel(Parcel out, int flags) { - out.writeStringArray(mKeys); - out.writeStringArray(mInterceptedKeys); - out.writeBundle(mVisibilityOverrides); - out.writeBundle(mSuppressedVisualEffects); - out.writeIntArray(mImportance); - out.writeBundle(mImportanceExplanation); - out.writeBundle(mOverrideGroupKeys); - out.writeBundle(mChannels); - out.writeBundle(mOverridePeople); - out.writeBundle(mSnoozeCriteria); - out.writeBundle(mShowBadge); - out.writeBundle(mUserSentiment); - out.writeBundle(mHidden); - out.writeBundle(mSmartActions); - out.writeBundle(mSmartReplies); - out.writeBundle(mLastAudiblyAlerted); - out.writeBundle(mNoisy); - out.writeBooleanArray(mCanBubble); + out.writeParcelable(mRankingMap, flags); } public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR @@ -129,76 +65,4 @@ public class NotificationRankingUpdate implements Parcelable { return new NotificationRankingUpdate[size]; } }; - - public String[] getOrderedKeys() { - return mKeys; - } - - public String[] getInterceptedKeys() { - return mInterceptedKeys; - } - - public Bundle getVisibilityOverrides() { - return mVisibilityOverrides; - } - - public Bundle getSuppressedVisualEffects() { - return mSuppressedVisualEffects; - } - - public int[] getImportance() { - return mImportance; - } - - public Bundle getImportanceExplanation() { - return mImportanceExplanation; - } - - public Bundle getOverrideGroupKeys() { - return mOverrideGroupKeys; - } - - public Bundle getChannels() { - return mChannels; - } - - public Bundle getOverridePeople() { - return mOverridePeople; - } - - public Bundle getSnoozeCriteria() { - return mSnoozeCriteria; - } - - public Bundle getShowBadge() { - return mShowBadge; - } - - public Bundle getUserSentiment() { - return mUserSentiment; - } - - public Bundle getHidden() { - return mHidden; - } - - public Bundle getSmartActions() { - return mSmartActions; - } - - public Bundle getSmartReplies() { - return mSmartReplies; - } - - public Bundle getLastAudiblyAlerted() { - return mLastAudiblyAlerted; - } - - public Bundle getNoisy() { - return mNoisy; - } - - public boolean[] getCanBubble() { - return mCanBubble; - } } diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 365638f5a96a..efc5eb373e00 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -28,7 +28,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -555,6 +557,12 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } /** + * Config diff flags for which the cache should be reset + */ + private static final int CACHE_RESET_CONFIG_FLAGS = ActivityInfo.CONFIG_FONT_SCALE + | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_DENSITY + | ActivityInfo.CONFIG_ASSETS_PATHS; + /** * */ private static class FixedSizeRemoteViewsCache { @@ -587,7 +595,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback // farthest items from when we hit the memory limit private int mLastRequestedIndex; - // The lower and upper bounds of the preloaded range private int mPreloadLowerBound; private int mPreloadUpperBound; @@ -602,12 +609,17 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback private static final float sMaxCountSlackPercent = 0.75f; private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024; - public FixedSizeRemoteViewsCache(int maxCacheSize) { + // Configuration for which the cache was created + private final Configuration mConfiguration; + + FixedSizeRemoteViewsCache(int maxCacheSize, Configuration configuration) { mMaxCount = maxCacheSize; mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2)); mPreloadLowerBound = 0; mPreloadUpperBound = -1; mLastRequestedIndex = -1; + + mConfiguration = new Configuration(configuration); } public void insert(int position, RemoteViews v, long itemId, int[] visibleWindow) { @@ -852,7 +864,12 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback mAppWidgetId); synchronized(sCachedRemoteViewsCaches) { - if (sCachedRemoteViewsCaches.containsKey(key)) { + FixedSizeRemoteViewsCache cache = sCachedRemoteViewsCaches.get(key); + Configuration config = context.getResources().getConfiguration(); + if (cache == null + || (cache.mConfiguration.diff(config) & CACHE_RESET_CONFIG_FLAGS) != 0) { + mCache = new FixedSizeRemoteViewsCache(DEFAULT_CACHE_SIZE, config); + } else { mCache = sCachedRemoteViewsCaches.get(key); synchronized (mCache.mMetaData) { if (mCache.mMetaData.count > 0) { @@ -861,8 +878,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback mDataReady = true; } } - } else { - mCache = new FixedSizeRemoteViewsCache(DEFAULT_CACHE_SIZE); } if (!mDataReady) { requestBindService(); diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/com/android/internal/net/VpnInfo.java index e74af5eb50de..b1a412871bd2 100644 --- a/core/java/com/android/internal/net/VpnInfo.java +++ b/core/java/com/android/internal/net/VpnInfo.java @@ -19,8 +19,6 @@ package com.android.internal.net; import android.os.Parcel; import android.os.Parcelable; -import java.util.Arrays; - /** * A lightweight container used to carry information of the ongoing VPN. * Internal use only.. @@ -30,14 +28,14 @@ import java.util.Arrays; public class VpnInfo implements Parcelable { public int ownerUid; public String vpnIface; - public String[] underlyingIfaces; + public String primaryUnderlyingIface; @Override public String toString() { return "VpnInfo{" + "ownerUid=" + ownerUid + ", vpnIface='" + vpnIface + '\'' - + ", underlyingIfaces='" + Arrays.toString(underlyingIfaces) + '\'' + + ", primaryUnderlyingIface='" + primaryUnderlyingIface + '\'' + '}'; } @@ -50,7 +48,7 @@ public class VpnInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(ownerUid); dest.writeString(vpnIface); - dest.writeStringArray(underlyingIfaces); + dest.writeString(primaryUnderlyingIface); } public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() { @@ -59,7 +57,7 @@ public class VpnInfo implements Parcelable { VpnInfo info = new VpnInfo(); info.ownerUid = source.readInt(); info.vpnIface = source.readString(); - info.underlyingIfaces = source.readStringArray(); + info.primaryUnderlyingIface = source.readString(); return info; } diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 27a4a46c20dc..641f223c7a8f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -766,8 +766,6 @@ <java-symbol type="string" name="display_manager_hdmi_display_name" /> <java-symbol type="string" name="display_manager_overlay_display_name" /> <java-symbol type="string" name="display_manager_overlay_display_secure_suffix" /> - <java-symbol type="string" name="activity_starter_block_bg_activity_starts_permissive" /> - <java-symbol type="string" name="activity_starter_block_bg_activity_starts_enforcing" /> <java-symbol type="string" name="display_manager_overlay_display_title" /> <java-symbol type="string" name="double_tap_toast" /> <java-symbol type="string" name="elapsed_time_short_format_h_mm_ss" /> diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java index 707d7b30e09b..1b6560322a13 100644 --- a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java +++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java @@ -19,22 +19,13 @@ package android.net; import com.google.caliper.BeforeExperiment; import com.google.caliper.Param; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - public class NetworkStatsBenchmark { - private static final String[] UNDERLYING_IFACES = {"wlan0", "rmnet0"}; + private static final String UNDERLYING_IFACE = "wlan0"; private static final String TUN_IFACE = "tun0"; private static final int TUN_UID = 999999999; @Param({"100", "1000"}) private int mSize; - /** - * Should not be more than the length of {@link #UNDERLYING_IFACES}. - */ - @Param({"1", "2"}) - private int mNumUnderlyingIfaces; private NetworkStats mNetworkStats; @BeforeExperiment @@ -42,10 +33,8 @@ public class NetworkStatsBenchmark { mNetworkStats = new NetworkStats(0, mSize + 2); int uid = 0; NetworkStats.Entry recycle = new NetworkStats.Entry(); - final List<String> allIfaces = getAllIfacesForBenchmark(); // also contains TUN_IFACE. - final int totalIfaces = allIfaces.size(); for (int i = 0; i < mSize; i++) { - recycle.iface = allIfaces.get(i % totalIfaces); + recycle.iface = (i < mSize / 2) ? TUN_IFACE : UNDERLYING_IFACE; recycle.uid = uid; recycle.set = i % 2; recycle.tag = NetworkStats.TAG_NONE; @@ -59,39 +48,22 @@ public class NetworkStatsBenchmark { uid++; } } - - for (int i = 0; i < mNumUnderlyingIfaces; i++) { - recycle.iface = UNDERLYING_IFACES[i]; - recycle.uid = TUN_UID; - recycle.set = NetworkStats.SET_FOREGROUND; - recycle.tag = NetworkStats.TAG_NONE; - recycle.rxBytes = 90000 * mSize; - recycle.rxPackets = 40 * mSize; - recycle.txBytes = 180000 * mSize; - recycle.txPackets = 1200 * mSize; - recycle.operations = 0; - mNetworkStats.addValues(recycle); - } - } - - private String[] getVpnUnderlyingIfaces() { - return Arrays.copyOf(UNDERLYING_IFACES, mNumUnderlyingIfaces); - } - - /** - * Same as {@link #getVpnUnderlyingIfaces}, but also contains {@link #TUN_IFACE}. - */ - private List<String> getAllIfacesForBenchmark() { - List<String> ifaces = new ArrayList<>(); - ifaces.add(TUN_IFACE); - ifaces.addAll(Arrays.asList(getVpnUnderlyingIfaces())); - return ifaces; + recycle.iface = UNDERLYING_IFACE; + recycle.uid = TUN_UID; + recycle.set = NetworkStats.SET_FOREGROUND; + recycle.tag = NetworkStats.TAG_NONE; + recycle.rxBytes = 90000 * mSize; + recycle.rxPackets = 40 * mSize; + recycle.txBytes = 180000 * mSize; + recycle.txPackets = 1200 * mSize; + recycle.operations = 0; + mNetworkStats.addValues(recycle); } public void timeMigrateTun(int reps) { for (int i = 0; i < reps; i++) { NetworkStats stats = mNetworkStats.clone(); - stats.migrateTun(TUN_UID, TUN_IFACE, getVpnUnderlyingIfaces()); + stats.migrateTun(TUN_UID, TUN_IFACE, UNDERLYING_IFACE); } } diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index a4337cc8ce79..a305d48c4633 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -54,6 +54,7 @@ <permission name="android.permission.TETHER_PRIVILEGED"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> <permission name="android.permission.USE_RESERVED_DISK"/> + <permission name="android.permission.WATCH_APPOPS"/> <permission name="android.permission.WRITE_DREAM_STATE"/> <permission name="android.permission.WRITE_MEDIA_STORAGE"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 7db8969e2e04..4b4912cb1e8a 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -222,6 +222,9 @@ <!-- to change themes - light or dark --> <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" /> + <!-- Listen app op changes --> + <uses-permission android:name="android.permission.WATCH_APPOPS" /> + <!-- to read and change hvac values in a car --> <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" /> diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java index 95c136f062b5..662de3a5b5f5 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java @@ -21,7 +21,6 @@ import static com.android.systemui.assist.AssistManager.DISMISS_REASON_INVOCATIO import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; -import android.annotation.ColorInt; import android.content.Context; import android.graphics.PixelFormat; import android.metrics.LogMaker; @@ -52,6 +51,7 @@ public class DefaultUiController implements AssistManager.UiController { private static final long ANIM_DURATION_MS = 200; protected final FrameLayout mRoot; + protected InvocationLightsView mInvocationLightsView; private final WindowManager mWindowManager; private final WindowManager.LayoutParams mLayoutParams; @@ -62,7 +62,6 @@ public class DefaultUiController implements AssistManager.UiController { private float mLastInvocationProgress = 0; private ValueAnimator mInvocationAnimator = new ValueAnimator(); - private InvocationLightsView mInvocationLightsView; public DefaultUiController(Context context) { mRoot = new FrameLayout(context); @@ -129,14 +128,6 @@ public class DefaultUiController implements AssistManager.UiController { updateAssistHandleVisibility(); } - /** - * Sets the colors of the four invocation lights, from left to right. - */ - public void setInvocationColors(@ColorInt int color1, @ColorInt int color2, - @ColorInt int color3, @ColorInt int color4) { - mInvocationLightsView.setColors(color1, color2, color3, color4); - } - protected static void logInvocationProgressMetrics( int type, float progress, boolean invocationWasInProgress) { // Logs assistant invocation start. diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java index de1d7c8c0a04..178f4c32bb75 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java +++ b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java @@ -16,24 +16,34 @@ package com.android.systemui.assist.ui; +import android.animation.ArgbEvaluator; import android.annotation.ColorInt; +import android.annotation.Nullable; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; +import android.view.ContextThemeWrapper; import android.view.View; +import com.android.settingslib.Utils; +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.statusbar.phone.NavigationBarFragment; +import com.android.systemui.statusbar.phone.NavigationBarTransitions; import java.util.ArrayList; /** * Shows lights at the bottom of the phone, marking the invocation progress. */ -public class InvocationLightsView extends View { +public class InvocationLightsView extends View + implements NavigationBarTransitions.DarkIntensityListener { private static final String TAG = "InvocationLightsView"; @@ -49,9 +59,16 @@ public class InvocationLightsView extends View { // allocation on each frame. private final Path mPath = new Path(); private final int mViewHeight; + private final int mStrokeWidth; + @ColorInt + private final int mLightColor; + @ColorInt + private final int mDarkColor; // Allocate variable for screen location lookup to avoid memory alloc onDraw() private int[] mScreenLocation = new int[2]; + private boolean mRegistered = false; + private boolean mUseNavBarColor = true; public InvocationLightsView(Context context) { this(context, null); @@ -69,8 +86,8 @@ public class InvocationLightsView extends View { int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - int strokeWidth = DisplayUtils.convertDpToPx(LIGHT_HEIGHT_DP, context); - mPaint.setStrokeWidth(strokeWidth); + mStrokeWidth = DisplayUtils.convertDpToPx(LIGHT_HEIGHT_DP, context); + mPaint.setStrokeWidth(mStrokeWidth); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.MITER); mPaint.setAntiAlias(true); @@ -82,13 +99,19 @@ public class InvocationLightsView extends View { CircularCornerPathRenderer cornerPathRenderer = new CircularCornerPathRenderer( cornerRadiusBottom, cornerRadiusTop, displayWidth, displayHeight); mGuide = new PerimeterPathGuide(context, cornerPathRenderer, - strokeWidth / 2, displayWidth, displayHeight); + mStrokeWidth / 2, displayWidth, displayHeight); mViewHeight = Math.max(cornerRadiusBottom, cornerRadiusTop); - @ColorInt int lightColor = getResources().getColor(R.color.default_invocation_lights_color); + final int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme); + final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); + Context lightContext = new ContextThemeWrapper(mContext, dualToneLightTheme); + Context darkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme); + mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor); + mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor); + for (int i = 0; i < 4; i++) { - mAssistInvocationLights.add(new EdgeLight(lightColor, 0, 0)); + mAssistInvocationLights.add(new EdgeLight(Color.TRANSPARENT, 0, 0)); } } @@ -100,6 +123,8 @@ public class InvocationLightsView extends View { if (progress == 0) { setVisibility(View.GONE); } else { + attemptRegisterNavBarListener(); + float cornerLengthNormalized = mGuide.getRegionWidth(PerimeterPathGuide.Region.BOTTOM_LEFT); float arcLengthNormalized = cornerLengthNormalized * MINIMUM_CORNER_RATIO; @@ -131,6 +156,21 @@ public class InvocationLightsView extends View { for (EdgeLight light : mAssistInvocationLights) { light.setLength(0); } + attemptUnregisterNavBarListener(); + } + + /** + * Sets all invocation lights to a single color. If color is null, uses the navigation bar + * color (updated when the nav bar color changes). + */ + public void setColors(@Nullable @ColorInt Integer color) { + if (color == null) { + mUseNavBarColor = true; + mPaint.setStrokeCap(Paint.Cap.BUTT); + attemptRegisterNavBarListener(); + } else { + setColors(color, color, color, color); + } } /** @@ -138,12 +178,25 @@ public class InvocationLightsView extends View { */ public void setColors(@ColorInt int color1, @ColorInt int color2, @ColorInt int color3, @ColorInt int color4) { + mUseNavBarColor = false; + attemptUnregisterNavBarListener(); mAssistInvocationLights.get(0).setColor(color1); mAssistInvocationLights.get(1).setColor(color2); mAssistInvocationLights.get(2).setColor(color3); mAssistInvocationLights.get(3).setColor(color4); } + /** + * Reacts to changes in the navigation bar color + * + * @param darkIntensity 0 is the lightest color, 1 is the darkest. + */ + @Override // NavigationBarTransitions.DarkIntensityListener + public void onDarkIntensity(float darkIntensity) { + updateDarkness(darkIntensity); + } + + @Override protected void onFinishInflate() { getLayoutParams().height = mViewHeight; @@ -166,15 +219,19 @@ public class InvocationLightsView extends View { getLocationOnScreen(mScreenLocation); canvas.translate(-mScreenLocation[0], -mScreenLocation[1]); - // if the lights are different colors, the inner ones need to be drawn last and with a - // square cap so that the join between lights is straight - mPaint.setStrokeCap(Paint.Cap.ROUND); - renderLight(mAssistInvocationLights.get(0), canvas); - renderLight(mAssistInvocationLights.get(3), canvas); + if (mUseNavBarColor) { + for (EdgeLight light : mAssistInvocationLights) { + renderLight(light, canvas); + } + } else { + mPaint.setStrokeCap(Paint.Cap.ROUND); + renderLight(mAssistInvocationLights.get(0), canvas); + renderLight(mAssistInvocationLights.get(3), canvas); - mPaint.setStrokeCap(Paint.Cap.SQUARE); - renderLight(mAssistInvocationLights.get(1), canvas); - renderLight(mAssistInvocationLights.get(2), canvas); + mPaint.setStrokeCap(Paint.Cap.BUTT); + renderLight(mAssistInvocationLights.get(1), canvas); + renderLight(mAssistInvocationLights.get(2), canvas); + } } protected void setLight(int index, float offset, float length) { @@ -185,10 +242,58 @@ public class InvocationLightsView extends View { mAssistInvocationLights.get(index).setLength(length); } + /** + * Receives an intensity from 0 (lightest) to 1 (darkest) and sets the handle color + * appropriately. Intention is to match the home handle color. + */ + protected void updateDarkness(float darkIntensity) { + if (mUseNavBarColor) { + @ColorInt int invocationColor = (int) ArgbEvaluator.getInstance().evaluate( + darkIntensity, mLightColor, mDarkColor); + for (EdgeLight light : mAssistInvocationLights) { + light.setColor(invocationColor); + } + invalidate(); + } + } + private void renderLight(EdgeLight light, Canvas canvas) { mGuide.strokeSegment(mPath, light.getOffset(), light.getOffset() + light.getLength()); mPaint.setColor(light.getColor()); canvas.drawPath(mPath, mPaint); } + private void attemptRegisterNavBarListener() { + if (!mRegistered) { + NavigationBarController controller = Dependency.get(NavigationBarController.class); + if (controller == null) { + return; + } + + NavigationBarFragment navBar = controller.getDefaultNavigationBarFragment(); + if (navBar == null) { + return; + } + + updateDarkness(navBar.getBarTransitions().addDarkIntensityListener(this)); + mRegistered = true; + } + } + + private void attemptUnregisterNavBarListener() { + if (mRegistered) { + NavigationBarController controller = Dependency.get(NavigationBarController.class); + if (controller == null) { + return; + } + + NavigationBarFragment navBar = controller.getDefaultNavigationBarFragment(); + if (navBar == null) { + return; + } + + navBar.getBarTransitions().removeDarkIntensityListener(this); + mRegistered = false; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 3c4898c7658b..af7de0e6b234 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -19,6 +19,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; @@ -48,6 +49,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import com.android.systemui.util.leak.GarbageMonitor; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -364,6 +366,10 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D if (tile.equals("default")) { if (!addedDefault) { tiles.addAll(Arrays.asList(defaultTileList.split(","))); + if (Build.IS_DEBUGGABLE + && GarbageMonitor.MemoryTile.ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) { + tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC); + } addedDefault = true; } } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java index 2b5a28e60082..23cc0fc51ec0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java @@ -213,9 +213,12 @@ public final class NavigationBarTransitions extends BarTransitions implements /** * Register {@code listener} to be notified when the color of nav bar elements changes. + * + * Returns the current nav bar color. */ - public void addDarkIntensityListener(DarkIntensityListener listener) { + public float addDarkIntensityListener(DarkIntensityListener listener) { mDarkIntensityListeners.add(listener); + return mLightTransitionsController.getCurrentDarkIntensity(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java index b590e7738f08..aa3fd5f69802 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java @@ -66,7 +66,11 @@ public class GarbageMonitor { private static final String FORCE_ENABLE_LEAK_REPORTING = "sysui_force_enable_leak_reporting"; private static final boolean HEAP_TRACKING_ENABLED = Build.IS_DEBUGGABLE; - private static final boolean ENABLE_AM_HEAP_LIMIT = true; // use ActivityManager.setHeapLimit + + // whether to use ActivityManager.setHeapLimit + private static final boolean ENABLE_AM_HEAP_LIMIT = Build.IS_DEBUGGABLE; + // heap limit value, in KB (overrides R.integer.watch_heap_limit) + private static final String SETTINGS_KEY_AM_HEAP_LIMIT = "systemui_am_heap_limit"; private static final String TAG = "GarbageMonitor"; @@ -112,7 +116,9 @@ public class GarbageMonitor { mDumpTruck = new DumpTruck(mContext); if (ENABLE_AM_HEAP_LIMIT) { - mHeapLimit = mContext.getResources().getInteger(R.integer.watch_heap_limit); + mHeapLimit = Settings.Global.getInt(context.getContentResolver(), + SETTINGS_KEY_AM_HEAP_LIMIT, + mContext.getResources().getInteger(R.integer.watch_heap_limit)); } } @@ -343,6 +349,9 @@ public class GarbageMonitor { public static class MemoryTile extends QSTileImpl<QSTile.State> { public static final String TILE_SPEC = "dbg:mem"; + // Tell QSTileHost.java to toss this into the default tileset? + public static final boolean ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS = true; + private final GarbageMonitor gm; private ProcessMemInfo pmi; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index a2abf26891e9..5027a124e075 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -4383,7 +4383,7 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * @return VPN information for accounting, or null if we can't retrieve all required - * information, e.g underlying ifaces. + * information, e.g primary underlying iface. */ @Nullable private VpnInfo createVpnInfo(Vpn vpn) { @@ -4395,24 +4395,17 @@ public class ConnectivityService extends IConnectivityManager.Stub // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret // the underlyingNetworks list. if (underlyingNetworks == null) { - NetworkAgentInfo defaultNai = getDefaultNetwork(); - if (defaultNai != null) { - underlyingNetworks = new Network[] { defaultNai.network }; + NetworkAgentInfo defaultNetwork = getDefaultNetwork(); + if (defaultNetwork != null && defaultNetwork.linkProperties != null) { + info.primaryUnderlyingIface = getDefaultNetwork().linkProperties.getInterfaceName(); } - } - if (underlyingNetworks != null && underlyingNetworks.length > 0) { - List<String> interfaces = new ArrayList<>(); - for (Network network : underlyingNetworks) { - LinkProperties lp = getLinkProperties(network); - if (lp != null && !TextUtils.isEmpty(lp.getInterfaceName())) { - interfaces.add(lp.getInterfaceName()); - } - } - if (!interfaces.isEmpty()) { - info.underlyingIfaces = interfaces.toArray(new String[interfaces.size()]); + } else if (underlyingNetworks.length > 0) { + LinkProperties linkProperties = getLinkProperties(underlyingNetworks[0]); + if (linkProperties != null) { + info.primaryUnderlyingIface = linkProperties.getInterfaceName(); } } - return info.underlyingIfaces == null ? null : info; + return info.primaryUnderlyingIface == null ? null : info; } /** diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 7a947f192263..0d19a67e86e9 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -116,6 +116,7 @@ public class VibratorService extends IVibratorService.Stub private final LinkedList<VibrationInfo> mPreviousRingVibrations; private final LinkedList<VibrationInfo> mPreviousNotificationVibrations; private final LinkedList<VibrationInfo> mPreviousAlarmVibrations; + private final LinkedList<ExternalVibration> mPreviousExternalVibrations; private final LinkedList<VibrationInfo> mPreviousVibrations; private final int mPreviousVibrationsLimit; private final boolean mAllowPriorityVibrationsInLowPowerMode; @@ -368,6 +369,7 @@ public class VibratorService extends IVibratorService.Stub mPreviousNotificationVibrations = new LinkedList<>(); mPreviousAlarmVibrations = new LinkedList<>(); mPreviousVibrations = new LinkedList<>(); + mPreviousExternalVibrations = new LinkedList<>(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); @@ -1418,6 +1420,12 @@ public class VibratorService extends IVibratorService.Stub pw.print(" "); pw.println(info.toString()); } + + pw.println(" Previous external vibrations:"); + for (ExternalVibration vib : mPreviousExternalVibrations) { + pw.print(" "); + pw.println(vib.toString()); + } } } @@ -1462,6 +1470,10 @@ public class VibratorService extends IVibratorService.Stub // Note that this doesn't support multiple concurrent external controls, as we // would need to mute the old one still if it came from a different controller. mCurrentExternalVibration = vib; + if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) { + mPreviousExternalVibrations.removeFirst(); + } + mPreviousExternalVibrations.addLast(vib); if (DEBUG) { Slog.e(TAG, "Playing external vibration: " + vib); } diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index 473cc97b7edc..69efd02dea9c 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -263,10 +263,6 @@ public class NetworkStatsFactory { return stats; } - /** - * @deprecated Use NetworkStatsService#getDetailedUidStats which also accounts for - * VPN traffic - */ public NetworkStats readNetworkStatsDetail() throws IOException { return readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null); } diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java index bdff50053fae..a2e7e0cae96b 100644 --- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java +++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java @@ -41,10 +41,10 @@ import com.android.internal.net.VpnInfo; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; -import com.google.android.collect.Sets; - import libcore.io.IoUtils; +import com.google.android.collect.Sets; + import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; @@ -234,7 +234,7 @@ public class NetworkStatsRecorder { if (vpnArray != null) { for (VpnInfo info : vpnArray) { - delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces); + delta.migrateTun(info.ownerUid, info.vpnIface, info.primaryUnderlyingIface); } } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index a13368ff9de4..f34ace55a72e 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -293,22 +293,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** Data layer operation counters for splicing into other structures. */ private NetworkStats mUidOperations = new NetworkStats(0L, 10); - /** - * Snapshot containing most recent network stats for all UIDs across all interfaces and tags - * since boot. - * - * <p>Maintains migrated VPN stats which are result of performing TUN migration on {@link - * #mLastUidDetailSnapshot}. - */ - @GuardedBy("mStatsLock") - private NetworkStats mTunAdjustedStats; - /** - * Used by {@link #mTunAdjustedStats} to migrate VPN traffic over delta between this snapshot - * and latest snapshot. - */ - @GuardedBy("mStatsLock") - private NetworkStats mLastUidDetailSnapshot; - /** Must be set in factory by calling #setHandler. */ private Handler mHandler; private Handler.Callback mHandlerCallback; @@ -828,39 +812,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getDetailedUidStats(String[] requiredIfaces) { try { - // Get the latest snapshot from NetworkStatsFactory. - // TODO: Querying for INTERFACES_ALL may incur performance penalty. Consider restricting - // this to limited set of ifaces. - NetworkStats uidDetailStats = getNetworkStatsUidDetail(INTERFACES_ALL); - - // Migrate traffic from VPN UID over delta and update mTunAdjustedStats. - NetworkStats result; - synchronized (mStatsLock) { - migrateTunTraffic(uidDetailStats, mVpnInfos); - result = mTunAdjustedStats.clone(); - } - - // Apply filter based on ifacesToQuery. final String[] ifacesToQuery = NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces); - result.filter(UID_ALL, ifacesToQuery, TAG_ALL); - return result; + return getNetworkStatsUidDetail(ifacesToQuery); } catch (RemoteException e) { Log.wtf(TAG, "Error compiling UID stats", e); return new NetworkStats(0L, 0); } } - @VisibleForTesting - NetworkStats getTunAdjustedStats() { - synchronized (mStatsLock) { - if (mTunAdjustedStats == null) { - return null; - } - return mTunAdjustedStats.clone(); - } - } - @Override public String[] getMobileIfaces() { return mMobileIfaces; @@ -1335,34 +1295,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // a race condition between the service handler thread and the observer's mStatsObservers.updateStats(xtSnapshot, uidSnapshot, new ArrayMap<>(mActiveIfaces), new ArrayMap<>(mActiveUidIfaces), vpnArray, currentTime); - - migrateTunTraffic(uidSnapshot, vpnArray); - } - - /** - * Updates {@link #mTunAdjustedStats} with the delta containing traffic migrated off of VPNs. - */ - @GuardedBy("mStatsLock") - private void migrateTunTraffic(NetworkStats uidDetailStats, VpnInfo[] vpnInfoArray) { - if (mTunAdjustedStats == null) { - // Either device booted or system server restarted, hence traffic cannot be migrated - // correctly without knowing the past state of VPN's underlying networks. - mTunAdjustedStats = uidDetailStats; - mLastUidDetailSnapshot = uidDetailStats; - return; - } - // Migrate delta traffic from VPN to other apps. - NetworkStats delta = uidDetailStats.subtract(mLastUidDetailSnapshot); - for (VpnInfo info : vpnInfoArray) { - delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces); - } - // Filter out debug entries as that may lead to over counting. - delta.filterDebugEntries(); - // Update #mTunAdjustedStats with migrated delta. - mTunAdjustedStats.combineAllValues(delta); - mTunAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime()); - // Update last snapshot. - mLastUidDetailSnapshot = uidDetailStats; } /** diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 8485f46aefd4..82b16dea5b49 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7234,72 +7234,42 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { final int N = mNotificationList.size(); - ArrayList<String> keys = new ArrayList<String>(N); - ArrayList<String> interceptedKeys = new ArrayList<String>(N); - ArrayList<Integer> importance = new ArrayList<>(N); - Bundle overrideGroupKeys = new Bundle(); - Bundle visibilityOverrides = new Bundle(); - Bundle suppressedVisualEffects = new Bundle(); - Bundle explanation = new Bundle(); - Bundle channels = new Bundle(); - Bundle overridePeople = new Bundle(); - Bundle snoozeCriteria = new Bundle(); - Bundle showBadge = new Bundle(); - Bundle userSentiment = new Bundle(); - Bundle hidden = new Bundle(); - Bundle systemGeneratedSmartActions = new Bundle(); - Bundle smartReplies = new Bundle(); - Bundle lastAudiblyAlerted = new Bundle(); - Bundle noisy = new Bundle(); - ArrayList<Boolean> canBubble = new ArrayList<>(N); + final ArrayList<NotificationListenerService.Ranking> rankings = new ArrayList<>(); + for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); if (!isVisibleToListener(record.sbn, info)) { continue; } final String key = record.sbn.getKey(); - keys.add(key); - importance.add(record.getImportance()); - if (record.getImportanceExplanation() != null) { - explanation.putCharSequence(key, record.getImportanceExplanation()); - } - if (record.isIntercepted()) { - interceptedKeys.add(key); - - } - suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects()); - if (record.getPackageVisibilityOverride() - != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) { - visibilityOverrides.putInt(key, record.getPackageVisibilityOverride()); - } - overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey()); - channels.putParcelable(key, record.getChannel()); - overridePeople.putStringArrayList(key, record.getPeopleOverride()); - snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria()); - showBadge.putBoolean(key, record.canShowBadge()); - userSentiment.putInt(key, record.getUserSentiment()); - hidden.putBoolean(key, record.isHidden()); - systemGeneratedSmartActions.putParcelableArrayList(key, - record.getSystemGeneratedSmartActions()); - smartReplies.putCharSequenceArrayList(key, record.getSmartReplies()); - lastAudiblyAlerted.putLong(key, record.getLastAudiblyAlertedMs()); - noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null); - canBubble.add(record.canBubble()); - } - final int M = keys.size(); - String[] keysAr = keys.toArray(new String[M]); - String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]); - int[] importanceAr = new int[M]; - boolean[] canBubbleAr = new boolean[M]; - for (int i = 0; i < M; i++) { - importanceAr[i] = importance.get(i); - canBubbleAr[i] = canBubble.get(i); - } - return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, - suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys, - channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden, - systemGeneratedSmartActions, smartReplies, lastAudiblyAlerted, noisy, - canBubbleAr); + final NotificationListenerService.Ranking ranking = + new NotificationListenerService.Ranking(); + ranking.populate( + key, + rankings.size(), + !record.isIntercepted(), + record.getPackageVisibilityOverride(), + record.getSuppressedVisualEffects(), + record.getImportance(), + record.getImportanceExplanation(), + record.sbn.getOverrideGroupKey(), + record.getChannel(), + record.getPeopleOverride(), + record.getSnoozeCriteria(), + record.canShowBadge(), + record.getUserSentiment(), + record.isHidden(), + record.getLastAudiblyAlertedMs(), + record.getSound() != null || record.getVibration() != null, + record.getSystemGeneratedSmartActions(), + record.getSmartReplies(), + record.canBubble() + ); + rankings.add(ranking); + } + + return new NotificationRankingUpdate( + rankings.toArray(new NotificationListenerService.Ranking[0])); } boolean hasCompanionDevice(ManagedServiceInfo info) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index 52c199a34f67..bee3b2baf3dd 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -20,7 +20,9 @@ import static android.service.notification.NotificationListenerService.Ranking.U import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -33,10 +35,11 @@ import android.app.NotificationChannel; import android.app.PendingIntent; import android.content.Intent; import android.os.Binder; -import android.os.Bundle; import android.os.IBinder; +import android.os.Parcel; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.NotificationRankingUpdate; import android.service.notification.SnoozeCriterion; import android.test.suitebuilder.annotation.SmallTest; @@ -55,8 +58,6 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class NotificationListenerServiceTest extends UiServiceTestCase { - private String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"}; - @Test public void testGetActiveNotifications_notNull() throws Exception { TestListenerService service = new TestListenerService(); @@ -97,52 +98,144 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { } } - private NotificationRankingUpdate generateUpdate() { - List<String> interceptedKeys = new ArrayList<>(); - Bundle visibilityOverrides = new Bundle(); - Bundle overrideGroupKeys = new Bundle(); - Bundle suppressedVisualEffects = new Bundle(); - Bundle explanation = new Bundle(); - Bundle channels = new Bundle(); - Bundle overridePeople = new Bundle(); - Bundle snoozeCriteria = new Bundle(); - Bundle showBadge = new Bundle(); - int[] importance = new int[mKeys.length]; - Bundle userSentiment = new Bundle(); - Bundle mHidden = new Bundle(); - Bundle smartActions = new Bundle(); - Bundle smartReplies = new Bundle(); - Bundle lastAudiblyAlerted = new Bundle(); - Bundle noisy = new Bundle(); - boolean[] canBubble = new boolean[mKeys.length]; + // Tests parceling of NotificationRankingUpdate, and by extension, RankingMap and Ranking. + @Test + public void testRankingUpdate_parcel() { + NotificationRankingUpdate nru = generateUpdate(); + Parcel parcel = Parcel.obtain(); + nru.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel); + assertEquals(nru, nru1); + } + + private void detailedAssertEquals(RankingMap a, RankingMap b) { + Ranking arank = new Ranking(); + Ranking brank = new Ranking(); + assertArrayEquals(a.getOrderedKeys(), b.getOrderedKeys()); + for (String key : a.getOrderedKeys()) { + a.getRanking(key, arank); + b.getRanking(key, brank); + detailedAssertEquals("ranking for key <" + key + ">", arank, brank); + } + } + + // Tests parceling of RankingMap and RankingMap.equals + @Test + public void testRankingMap_parcel() { + RankingMap rmap = generateUpdate().getRankingMap(); + Parcel parcel = Parcel.obtain(); + rmap.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + RankingMap rmap1 = RankingMap.CREATOR.createFromParcel(parcel); + + detailedAssertEquals(rmap, rmap1); + assertEquals(rmap, rmap1); + } + + private void detailedAssertEquals(String comment, Ranking a, Ranking b) { + assertEquals(comment, a.getKey(), b.getKey()); + assertEquals(comment, a.getRank(), b.getRank()); + assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter()); + assertEquals(comment, a.getVisibilityOverride(), b.getVisibilityOverride()); + assertEquals(comment, a.getSuppressedVisualEffects(), b.getSuppressedVisualEffects()); + assertEquals(comment, a.getImportance(), b.getImportance()); + assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation()); + assertEquals(comment, a.getOverrideGroupKey(), b.getOverrideGroupKey()); + assertEquals(comment, a.getChannel(), b.getChannel()); + assertEquals(comment, a.getAdditionalPeople(), b.getAdditionalPeople()); + assertEquals(comment, a.getSnoozeCriteria(), b.getSnoozeCriteria()); + assertEquals(comment, a.canShowBadge(), b.canShowBadge()); + assertEquals(comment, a.getUserSentiment(), b.getUserSentiment()); + assertEquals(comment, a.isSuspended(), b.isSuspended()); + assertEquals(comment, a.getLastAudiblyAlertedMillis(), b.getLastAudiblyAlertedMillis()); + assertEquals(comment, a.isNoisy(), b.isNoisy()); + assertEquals(comment, a.getSmartReplies(), b.getSmartReplies()); + assertEquals(comment, a.canBubble(), b.canBubble()); + assertActionsEqual(a.getSmartActions(), b.getSmartActions()); + } + + // Tests parceling of Ranking and Ranking.equals + @Test + public void testRanking_parcel() { + Ranking ranking = generateUpdate().getRankingMap().getRawRankingObject(mKeys[0]); + Parcel parcel = Parcel.obtain(); + ranking.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + Ranking ranking1 = new Ranking(parcel); + detailedAssertEquals("rankings differ: ", ranking, ranking1); + assertEquals(ranking, ranking1); + } + + private void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) { + assertEquals(a.getRankingMap(), b.getRankingMap()); + } + + // Tests NotificationRankingUpdate.equals(), and by extension, RankingMap and Ranking. + @Test + public void testRankingUpdate_equals() { + NotificationRankingUpdate nru = generateUpdate(); + NotificationRankingUpdate nru2 = generateUpdate(); + detailedAssertEquals(nru, nru2); + assertEquals(nru, nru2); + Ranking tweak = nru2.getRankingMap().getRawRankingObject(mKeys[0]); + tweak.populate( + tweak.getKey(), + tweak.getRank(), + !tweak.matchesInterruptionFilter(), // note the inversion here! + tweak.getVisibilityOverride(), + tweak.getSuppressedVisualEffects(), + tweak.getImportance(), + tweak.getImportanceExplanation(), + tweak.getOverrideGroupKey(), + tweak.getChannel(), + (ArrayList) tweak.getAdditionalPeople(), + (ArrayList) tweak.getSnoozeCriteria(), + tweak.canShowBadge(), + tweak.getUserSentiment(), + tweak.isSuspended(), + tweak.getLastAudiblyAlertedMillis(), + tweak.isNoisy(), + (ArrayList) tweak.getSmartActions(), + (ArrayList) tweak.getSmartReplies(), + tweak.canBubble() + ); + assertNotEquals(nru, nru2); + } + // Test data + + private String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"}; + + private NotificationRankingUpdate generateUpdate() { + Ranking[] rankings = new Ranking[mKeys.length]; for (int i = 0; i < mKeys.length; i++) { - String key = mKeys[i]; - visibilityOverrides.putInt(key, getVisibilityOverride(i)); - overrideGroupKeys.putString(key, getOverrideGroupKey(key)); - if (isIntercepted(i)) { - interceptedKeys.add(key); - } - suppressedVisualEffects.putInt(key, getSuppressedVisualEffects(i)); - importance[i] = getImportance(i); - explanation.putString(key, getExplanation(key)); - channels.putParcelable(key, getChannel(key, i)); - overridePeople.putStringArrayList(key, getPeople(key, i)); - snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i)); - showBadge.putBoolean(key, getShowBadge(i)); - userSentiment.putInt(key, getUserSentiment(i)); - mHidden.putBoolean(key, getHidden(i)); - smartActions.putParcelableArrayList(key, getSmartActions(key, i)); - smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i)); - lastAudiblyAlerted.putLong(key, lastAudiblyAlerted(i)); - noisy.putBoolean(key, getNoisy(i)); - canBubble[i] = canBubble(i); + final String key = mKeys[i]; + Ranking ranking = new Ranking(); + ranking.populate( + key, + i, + !isIntercepted(i), + getVisibilityOverride(i), + getSuppressedVisualEffects(i), + getImportance(i), + getExplanation(key), + getOverrideGroupKey(key), + getChannel(key, i), + getPeople(key, i), + getSnoozeCriteria(key, i), + getShowBadge(i), + getUserSentiment(i), + getHidden(i), + lastAudiblyAlerted(i), + getNoisy(i), + getSmartActions(key, i), + getSmartReplies(key, i), + canBubble(i) + ); + rankings[i] = ranking; } - NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys, - interceptedKeys.toArray(new String[0]), visibilityOverrides, - suppressedVisualEffects, importance, explanation, overrideGroupKeys, - channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden, - smartActions, smartReplies, lastAudiblyAlerted, noisy, canBubble); + NotificationRankingUpdate update = new NotificationRankingUpdate(rankings); return update; } diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java index c16a0f446651..b5b0384ca599 100644 --- a/tests/net/java/android/net/NetworkStatsTest.java +++ b/tests/net/java/android/net/NetworkStatsTest.java @@ -569,7 +569,7 @@ public class NetworkStatsTest { .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L); - delta.migrateTun(tunUid, tunIface, new String[] {underlyingIface}); + assertTrue(delta.toString(), delta.migrateTun(tunUid, tunIface, underlyingIface)); assertEquals(20, delta.size()); // tunIface and TEST_IFACE entries are not changed. @@ -650,7 +650,7 @@ public class NetworkStatsTest { .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L); - delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface}); + assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface)); assertEquals(9, delta.size()); // tunIface entries should not be changed. @@ -813,37 +813,6 @@ public class NetworkStatsTest { } @Test - public void testFilterDebugEntries() { - NetworkStats.Entry entry1 = new NetworkStats.Entry( - "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry2 = new NetworkStats.Entry( - "test2", 10101, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry3 = new NetworkStats.Entry( - "test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats.Entry entry4 = new NetworkStats.Entry( - "test2", 10101, SET_DBG_VPN_OUT, TAG_NONE, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L); - - NetworkStats stats = new NetworkStats(TEST_START, 4) - .addValues(entry1) - .addValues(entry2) - .addValues(entry3) - .addValues(entry4); - - stats.filterDebugEntries(); - - assertEquals(2, stats.size()); - assertEquals(entry1, stats.getValues(0, null)); - assertEquals(entry3, stats.getValues(1, null)); - } - - @Test public void testApply464xlatAdjustments() { final String v4Iface = "v4-wlan0"; final String baseIface = "wlan0"; diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index d9f2c201fe37..bce526d3ae29 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -57,11 +57,11 @@ import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_PO import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -216,16 +216,11 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectSystemReady(); - assertNull(mService.getTunAdjustedStats()); mService.systemReady(); - // Verify that system ready fetches realtime stats and initializes tun adjusted stats. - verify(mNetManager).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL); - assertNotNull("failed to initialize TUN adjusted stats", mService.getTunAdjustedStats()); - assertEquals(0, mService.getTunAdjustedStats().size()); - mSession = mService.openSession(); assertNotNull("openSession() failed", mSession); + // catch INetworkManagementEventObserver during systemReady() ArgumentCaptor<INetworkManagementEventObserver> networkObserver = ArgumentCaptor.forClass(INetworkManagementEventObserver.class); @@ -738,13 +733,11 @@ public class NetworkStatsServiceTest { NetworkStats stats = mService.getDetailedUidStats(ifaceFilter); - // mNetManager#getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL) has following invocations: - // 1) NetworkStatsService#systemReady from #setUp. - // 2) mService#forceUpdateIfaces in the test above. - // 3) Finally, mService#getDetailedUidStats. - verify(mNetManager, times(3)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL); - assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE)); - assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface)); + verify(mNetManager, times(1)).getNetworkStatsUidDetail(eq(UID_ALL), argThat(ifaces -> + ifaces != null && ifaces.length == 2 + && ArrayUtils.contains(ifaces, TEST_IFACE) + && ArrayUtils.contains(ifaces, stackedIface))); + assertEquals(2, stats.size()); assertEquals(uidStats, stats.getValues(0, null)); assertEquals(tetheredStats1, stats.getValues(1, null)); @@ -930,111 +923,11 @@ public class NetworkStatsServiceTest { } @Test - public void vpnRewriteTrafficThroughItself() throws Exception { - // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - expectDefaultSettings(); - NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()}; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // - // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED - // over VPN. - // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE - // over VPN. - // - // VPN UID rewrites packets read from TUN back to TUN, plus some of its own traffic - // (100 bytes). - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 5) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L) - .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L) - // VPN rewrites all the packets read from TUN + 100 additional bytes of VPN's - // own traffic. - .addValues(TUN_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 0L, 0L, 1600L, 160L, 2L) - // VPN sent 1760 bytes over WiFi in foreground (SET_FOREGROUND) i.e. 1600 - // bytes (160 packets) + 1 byte/packet overhead (=160 bytes). - .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1760L, 176L, 1L) - // VPN received 3300 bytes over WiFi in background (SET_DEFAULT) i.e. 3000 bytes - // (300 packets) + 1 byte/packet encryption overhead (=300 bytes). - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 3300L, 300L, 0L, 0L, 1L)); - - forcePollAndWaitForIdle(); - - // Verify increased TUN usage by UID_VPN does not get attributed to other apps. - NetworkStats tunStats = - mService.getDetailedUidStats(new String[] {TUN_IFACE}); - assertValues( - tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 1); - assertValues( - tunStats, TUN_IFACE, UID_BLUE, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 500L, 50L, 1); - assertValues( - tunStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 0L, 0L, 1600L, 160L, 2); - - // Verify correct attribution over WiFi. - assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1); - assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1); - assertUidTotal(sTemplateWifi, UID_VPN, 300L, 0L, 260L, 26L, 2); - } - - @Test public void vpnWithOneUnderlyingIface() throws Exception { // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). expectDefaultSettings(); NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()}; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED - // over VPN. - // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE - // over VPN. - // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over WiFi. - // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes - // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN. - // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes - // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L) - .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L) - // VPN received 3300 bytes over WiFi in background (SET_DEFAULT). - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 3300L, 300L, 0L, 0L, 1L) - // VPN sent 1650 bytes over WiFi in foreground (SET_FOREGROUND). - .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1650L, 150L, 1L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1); - assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1); - assertUidTotal(sTemplateWifi, UID_VPN, 300L, 0L, 150L, 0L, 2); - } - - @Test - public void vpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception { - // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - expectDefaultSettings(); - NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()}; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)}; expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); @@ -1045,33 +938,23 @@ public class NetworkStatsServiceTest { getActiveIface(networkStates)); // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption // overhead per packet): - // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED - // over VPN. - // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE - // over VPN. - // Additionally, the VPN sends 6000 bytes (600 packets) of its own traffic into the tun - // interface (passing that traffic to the VPN endpoint), and receives 5000 bytes (500 - // packets) from it. Including overhead that is 6600/5500 bytes. - // VPN sent 8250 bytes (750 packets), and received 8800 (800 packets) over WiFi. - // Of 8250 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes - // attributed to UID_BLUE, and 6750 bytes attributed to UID_VPN. - // Of 8800 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes - // attributed to UID_BLUE, and 5800 bytes attributed to UID_VPN. + // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. + // 500 bytes (50 packets) were sent/received by UID_BLUE over VPN. + // VPN sent/received 1650 bytes (150 packets) over WiFi. + // Of 1650 bytes over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes attributed to + // UID_BLUE, and 150 bytes attributed to UID_VPN for both rx/tx traffic. incrementCurrentTime(HOUR_IN_MILLIS); expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 1000L, 100L, 1L) - .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 500L, 50L, 1L) - .addValues(TUN_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 5000L, 500L, 6000L, 600L, 1L) - // VPN received 8800 bytes over WiFi in background (SET_DEFAULT). - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 8800L, 800L, 0L, 0L, 1L) - // VPN sent 8250 bytes over WiFi in foreground (SET_FOREGROUND). - .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 8250L, 750L, 1L)); + .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L) + .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 500L, 50L, 500L, 50L, 1L) + .addValues( + TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1650L, 150L, 1650L, 150L, 2L)); forcePollAndWaitForIdle(); - assertUidTotal(sTemplateWifi, UID_RED, 2000L, 200L, 1000L, 100L, 1); - assertUidTotal(sTemplateWifi, UID_BLUE, 1000L, 100L, 500L, 50L, 1); - assertUidTotal(sTemplateWifi, UID_VPN, 5800L, 500L, 6750L, 600L, 2); + assertUidTotal(sTemplateWifi, UID_RED, 1000L, 100L, 1000L, 100L, 1); + assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1); + assertUidTotal(sTemplateWifi, UID_VPN, 150L, 0L, 150L, 0L, 2); } @Test @@ -1079,7 +962,7 @@ public class NetworkStatsServiceTest { // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). expectDefaultSettings(); NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()}; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)}; expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); @@ -1110,136 +993,6 @@ public class NetworkStatsServiceTest { } @Test - public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception { - // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and - // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. - // Additionally, VPN is duplicating traffic across both WiFi and Cell. - expectDefaultSettings(); - NetworkState[] networkStates = - new NetworkState[] { - buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() - }; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN. - // VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total). - // Of 8800 bytes over WiFi/Cell, expect: - // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE. - // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L) - .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L) - .addValues( - TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 500L, 50L, 500L, 50L, 1); - assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1); - assertUidTotal(sTemplateWifi, UID_VPN, 1200L, 100L, 1200L, 100L, 2); - - assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 500L, 50L, 500L, 50L, 1); - assertUidTotal(buildTemplateMobileWildcard(), UID_BLUE, 500L, 50L, 500L, 50L, 1); - assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 1200L, 100L, 1200L, 100L, 2); - } - - @Test - public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception { - // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and - // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. - // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell. - expectDefaultSettings(); - NetworkState[] networkStates = - new NetworkState[] { - buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() - }; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent, and 500 bytes (50 packets) received by UID_RED over - // VPN. - // VPN sent 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell. - // And, it received 330 bytes (30 packets) over WiFi and 220 bytes (20 packets) over Cell. - // For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for sent (tx) - // traffic. For received (rx) traffic, expect 300 bytes over WiFi and 200 bytes over Cell. - // - // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for tx traffic. - // And, 30 bytes over WiFi and 20 bytes over Cell for rx traffic. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 500L, 50L, 1000L, 100L, 2L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 330L, 30L, 660L, 60L, 1L) - .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 220L, 20L, 440L, 40L, 1L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 300L, 30L, 600L, 60L, 1); - assertUidTotal(sTemplateWifi, UID_VPN, 30L, 0L, 60L, 0L, 1); - - assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 400L, 40L, 1); - assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 20L, 0L, 40L, 0L, 1); - } - - @Test - public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception { - // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and - // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. - // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell. - expectDefaultSettings(); - NetworkState[] networkStates = - new NetworkState[] { - buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() - }; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface: - // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. - // VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell. - // For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both - // rx/tx. - // UID_VPN gets nothing attributed to it (avoiding negative stats). - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 600L, 60L, 600L, 60L, 0L) - .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 200L, 20L, 200L, 20L, 0L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 600L, 60L, 600L, 60L, 0); - assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0); - - assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 200L, 20L, 0); - assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 0L, 0L, 0L, 0L, 0); - } - - @Test public void vpnWithIncorrectUnderlyingIface() throws Exception { // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2), // but has declared only WiFi (TEST_IFACE) in its underlying network set. @@ -1248,7 +1001,7 @@ public class NetworkStatsServiceTest { new NetworkState[] { buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() }; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)}; expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); @@ -1277,134 +1030,6 @@ public class NetworkStatsServiceTest { } @Test - public void recordSnapshot_migratesTunTrafficAndUpdatesTunAdjustedStats() throws Exception { - assertEquals(0, mService.getTunAdjustedStats().size()); - // VPN using WiFi (TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectBandwidthControlCheck(); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were downloaded by UID_RED over VPN. - // VPN received 1100 bytes (100 packets) over WiFi. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L)); - - // this should lead to NSS#recordSnapshotLocked - mService.forceUpdateIfaces( - new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */); - - // Verify TUN adjusted stats have traffic migrated correctly. - // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100 - // bytes attributed to UID_VPN. - NetworkStats tunAdjStats = mService.getTunAdjustedStats(); - assertValues( - tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); - assertValues( - tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0); - } - - @Test - public void getDetailedUidStats_migratesTunTrafficAndUpdatesTunAdjustedStats() - throws Exception { - assertEquals(0, mService.getTunAdjustedStats().size()); - // VPN using WiFi (TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectBandwidthControlCheck(); - mService.forceUpdateIfaces( - new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were downloaded by UID_RED over VPN. - // VPN received 1100 bytes (100 packets) over WiFi. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L)); - - mService.getDetailedUidStats(INTERFACES_ALL); - - // Verify internally maintained TUN adjusted stats - NetworkStats tunAdjStats = mService.getTunAdjustedStats(); - // Verify stats for TEST_IFACE (WiFi): - // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100 - // bytes attributed to UID_VPN. - assertValues( - tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); - assertValues( - tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0); - // Verify stats for TUN_IFACE; only UID_RED should have usage on it. - assertValues( - tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); - assertValues( - tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0); - - // lets assume that since last time, VPN received another 1100 bytes (same assumptions as - // before i.e. UID_RED downloaded another 1000 bytes). - incrementCurrentTime(HOUR_IN_MILLIS); - // Note - NetworkStatsFactory returns counters that are monotonically increasing. - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 0L, 0L, 0L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 0L, 0L, 0L)); - - mService.getDetailedUidStats(INTERFACES_ALL); - - tunAdjStats = mService.getTunAdjustedStats(); - // verify TEST_IFACE stats: - assertValues( - tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0); - assertValues( - tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 200L, 0L, 0L, 0L, 0); - // verify TUN_IFACE stats: - assertValues( - tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0); - assertValues( - tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0); - } - - @Test - public void getDetailedUidStats_returnsCorrectStatsWithVpnRunning() throws Exception { - // VPN using WiFi (TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectBandwidthControlCheck(); - mService.forceUpdateIfaces( - new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were downloaded by UID_RED over VPN. - // VPN received 1100 bytes (100 packets) over WiFi. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L)); - - // Query realtime stats for TEST_IFACE. - NetworkStats queriedStats = - mService.getDetailedUidStats(new String[] {TEST_IFACE}); - - assertEquals(HOUR_IN_MILLIS, queriedStats.getElapsedRealtime()); - // verify that returned stats are only for TEST_IFACE and VPN traffic is migrated correctly. - assertEquals(new String[] {TEST_IFACE}, queriedStats.getUniqueIfaces()); - assertValues( - queriedStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); - assertValues( - queriedStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0); - } - - @Test public void testRegisterUsageCallback() throws Exception { // pretend that wifi network comes online; service should ask about full // network state, and poll any existing interfaces before updating. @@ -1757,11 +1382,11 @@ public class NetworkStatsServiceTest { return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null); } - private static VpnInfo createVpnInfo(String[] underlyingIfaces) { + private static VpnInfo createVpnInfo(String underlyingIface) { VpnInfo info = new VpnInfo(); info.ownerUid = UID_VPN; info.vpnIface = TUN_IFACE; - info.underlyingIfaces = underlyingIfaces; + info.primaryUnderlyingIface = underlyingIface; return info; } diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 1aad4be5d66f..f62fef076f48 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -131,14 +131,10 @@ static void write_atoms_info_cpp(FILE *out, const Atoms &atoms) { "mobile_bytes_transfer"}; fprintf(out, "const std::set<int> " - "AtomsInfo::kNotTruncatingTimestampAtomWhiteList = {\n"); - for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); - atom != atoms.decls.end(); atom++) { - if (kTruncatingAtomNames.find(atom->name) == - kTruncatingAtomNames.end()) { - string constant = make_constant_name(atom->name); - fprintf(out, " %s,\n", constant.c_str()); - } + "AtomsInfo::kTruncatingTimestampAtomBlackList = {\n"); + for (set<string>::const_iterator blacklistedAtom = kTruncatingAtomNames.begin(); + blacklistedAtom != kTruncatingAtomNames.end(); blacklistedAtom++) { + fprintf(out, " %s,\n", make_constant_name(*blacklistedAtom).c_str()); } fprintf(out, "};\n"); fprintf(out, "\n"); @@ -840,7 +836,7 @@ write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributio fprintf(out, "struct AtomsInfo {\n"); fprintf(out, " const static std::set<int> " - "kNotTruncatingTimestampAtomWhiteList;\n"); + "kTruncatingTimestampAtomBlackList;\n"); fprintf(out, " const static std::map<int, int> kAtomsWithUidField;\n"); fprintf(out, " const static std::set<int> kAtomsWithAttributionChain;\n"); |