diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java index 83b5f63576..d975017f9c 100644 --- a/core/java/android/net/EthernetManager.java +++ b/core/java/android/net/EthernetManager.java @@ -199,6 +199,21 @@ public class EthernetManager { } } + /** + * Whether to treat interfaces created by {@link TestNetworkManager#createTapInterface} + * as Ethernet interfaces. The effects of this method apply to any test interfaces that are + * already present on the system. + * @hide + */ + @TestApi + public void setIncludeTestInterfaces(boolean include) { + try { + mService.setIncludeTestInterfaces(include); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * A request for a tethered interface. */ diff --git a/core/java/android/net/IEthernetManager.aidl b/core/java/android/net/IEthernetManager.aidl index ccc6e35209..e058e5a70c 100644 --- a/core/java/android/net/IEthernetManager.aidl +++ b/core/java/android/net/IEthernetManager.aidl @@ -33,6 +33,7 @@ interface IEthernetManager boolean isAvailable(String iface); void addListener(in IEthernetServiceListener listener); void removeListener(in IEthernetServiceListener listener); + void setIncludeTestInterfaces(boolean include); void requestTetheredInterface(in ITetheredInterfaceCallback callback); void releaseTetheredInterface(in ITetheredInterfaceCallback callback); } diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index c1198aac27..0948a4da1a 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -25,6 +25,7 @@ import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; import android.service.NetworkIdentityProto; +import android.telephony.Annotation.NetworkType; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -39,16 +40,6 @@ import java.util.Objects; public class NetworkIdentity implements Comparable { private static final String TAG = "NetworkIdentity"; - /** - * When enabled, combine all {@link #mSubType} together under - * {@link #SUBTYPE_COMBINED}. - * - * @deprecated we no longer offer to collect statistics on a per-subtype - * basis; this is always disabled. - */ - @Deprecated - public static final boolean COMBINE_SUBTYPE_ENABLED = true; - public static final int SUBTYPE_COMBINED = -1; final int mType; @@ -63,7 +54,7 @@ public class NetworkIdentity implements Comparable { int type, int subType, String subscriberId, String networkId, boolean roaming, boolean metered, boolean defaultNetwork) { mType = type; - mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType; + mSubType = subType; mSubscriberId = subscriberId; mNetworkId = networkId; mRoaming = roaming; @@ -95,7 +86,7 @@ public class NetworkIdentity implements Comparable { final StringBuilder builder = new StringBuilder("{"); builder.append("type=").append(getNetworkTypeName(mType)); builder.append(", subType="); - if (COMBINE_SUBTYPE_ENABLED) { + if (mSubType == SUBTYPE_COMBINED) { builder.append("COMBINED"); } else { builder.append(mSubType); @@ -187,13 +178,14 @@ public class NetworkIdentity implements Comparable { } /** - * Build a {@link NetworkIdentity} from the given {@link NetworkState}, - * assuming that any mobile networks are using the current IMSI. + * Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType}, + * assuming that any mobile networks are using the current IMSI. The subType if applicable, + * should be set as one of the TelephonyManager.NETWORK_TYPE_* constants, or + * {@link android.telephony.TelephonyManager#NETWORK_TYPE_UNKNOWN} if not. */ public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state, - boolean defaultNetwork) { + boolean defaultNetwork, @NetworkType int subType) { final int type = state.networkInfo.getType(); - final int subType = state.networkInfo.getSubtype(); String subscriberId = null; String networkId = null; diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 5498f74ba2..cb9463a59d 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -34,9 +34,13 @@ import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.ROAMING_YES; import static android.net.wifi.WifiInfo.sanitizeSsid; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.Annotation.NetworkType; +import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.BackupUtils; import android.util.Log; @@ -73,6 +77,14 @@ public class NetworkTemplate implements Parcelable { public static final int MATCH_BLUETOOTH = 8; public static final int MATCH_PROXY = 9; + /** + * Include all network types when filtering. This is meant to merge in with the + * {@code TelephonyManager.NETWORK_TYPE_*} constants, and thus needs to stay in sync. + * + * @hide + */ + public static final int NETWORK_TYPE_ALL = -1; + private static boolean isKnownMatchRule(final int rule) { switch (rule) { case MATCH_MOBILE: @@ -117,7 +129,22 @@ public class NetworkTemplate implements Parcelable { } /** - * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks, + * Template to match cellular networks with the given IMSI and {@code ratType}. + * Use {@link #NETWORK_TYPE_ALL} to include all network types when filtering. + * See {@code TelephonyManager.NETWORK_TYPE_*}. + */ + public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId, + @NetworkType int ratType) { + if (TextUtils.isEmpty(subscriberId)) { + return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType); + } + return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType); + } + + /** + * Template to match metered {@link ConnectivityManager#TYPE_MOBILE} networks, * regardless of IMSI. */ @UnsupportedAppUsage @@ -126,7 +153,7 @@ public class NetworkTemplate implements Parcelable { } /** - * Template to match all {@link ConnectivityManager#TYPE_WIFI} networks, + * Template to match all metered {@link ConnectivityManager#TYPE_WIFI} networks, * regardless of SSID. */ @UnsupportedAppUsage @@ -192,6 +219,7 @@ public class NetworkTemplate implements Parcelable { private final int mMetered; private final int mRoaming; private final int mDefaultNetwork; + private final int mSubType; @UnsupportedAppUsage public NetworkTemplate(int matchRule, String subscriberId, String networkId) { @@ -201,11 +229,11 @@ public class NetworkTemplate implements Parcelable { public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, String networkId) { this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL); + DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL); } public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, - String networkId, int metered, int roaming, int defaultNetwork) { + String networkId, int metered, int roaming, int defaultNetwork, int subType) { mMatchRule = matchRule; mSubscriberId = subscriberId; mMatchSubscriberIds = matchSubscriberIds; @@ -213,6 +241,7 @@ public class NetworkTemplate implements Parcelable { mMetered = metered; mRoaming = roaming; mDefaultNetwork = defaultNetwork; + mSubType = subType; if (!isKnownMatchRule(matchRule)) { Log.e(TAG, "Unknown network template rule " + matchRule @@ -228,6 +257,7 @@ public class NetworkTemplate implements Parcelable { mMetered = in.readInt(); mRoaming = in.readInt(); mDefaultNetwork = in.readInt(); + mSubType = in.readInt(); } @Override @@ -239,6 +269,7 @@ public class NetworkTemplate implements Parcelable { dest.writeInt(mMetered); dest.writeInt(mRoaming); dest.writeInt(mDefaultNetwork); + dest.writeInt(mSubType); } @Override @@ -271,13 +302,16 @@ public class NetworkTemplate implements Parcelable { builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString( mDefaultNetwork)); } + if (mSubType != NETWORK_TYPE_ALL) { + builder.append(", subType=").append(mSubType); + } return builder.toString(); } @Override public int hashCode() { return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming, - mDefaultNetwork); + mDefaultNetwork, mSubType); } @Override @@ -289,7 +323,8 @@ public class NetworkTemplate implements Parcelable { && Objects.equals(mNetworkId, other.mNetworkId) && mMetered == other.mMetered && mRoaming == other.mRoaming - && mDefaultNetwork == other.mDefaultNetwork; + && mDefaultNetwork == other.mDefaultNetwork + && mSubType == other.mSubType; } return false; } @@ -376,6 +411,11 @@ public class NetworkTemplate implements Parcelable { || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork); } + private boolean matchesCollapsedRatType(NetworkIdentity ident) { + return mSubType == NETWORK_TYPE_ALL + || getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType); + } + public boolean matchesSubscriberId(String subscriberId) { return ArrayUtils.contains(mMatchSubscriberIds, subscriberId); } @@ -388,9 +428,52 @@ public class NetworkTemplate implements Parcelable { // TODO: consider matching against WiMAX subscriber identity return true; } else { + // Only metered mobile network would be matched regardless of metered filter. + // This is used to exclude non-metered APNs, e.g. IMS. See ag/908650. + // TODO: Respect metered filter and remove mMetered condition. return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) && !ArrayUtils.isEmpty(mMatchSubscriberIds) - && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); + && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId) + && matchesCollapsedRatType(ident); + } + } + + /** + * Get a Radio Access Technology(RAT) type that is representative of a group of RAT types. + * The mapping is corresponding to {@code TelephonyManager#NETWORK_CLASS_BIT_MASK_*}. + * + * @param ratType An integer defined in {@code TelephonyManager#NETWORK_TYPE_*}. + */ + // TODO: 1. Consider move this to TelephonyManager if used by other modules. + // 2. Consider make this configurable. + // 3. Use TelephonyManager APIs when available. + public static int getCollapsedRatType(int ratType) { + switch (ratType) { + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_GSM: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_IDEN: + case TelephonyManager.NETWORK_TYPE_CDMA: + case TelephonyManager.NETWORK_TYPE_1xRTT: + return TelephonyManager.NETWORK_TYPE_GSM; + case TelephonyManager.NETWORK_TYPE_EVDO_0: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: + return TelephonyManager.NETWORK_TYPE_UMTS; + case TelephonyManager.NETWORK_TYPE_LTE: + case TelephonyManager.NETWORK_TYPE_IWLAN: + return TelephonyManager.NETWORK_TYPE_LTE; + case TelephonyManager.NETWORK_TYPE_NR: + return TelephonyManager.NETWORK_TYPE_NR; + default: + return TelephonyManager.NETWORK_TYPE_UNKNOWN; } } @@ -421,7 +504,8 @@ public class NetworkTemplate implements Parcelable { if (ident.mType == TYPE_WIMAX) { return true; } else { - return sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered); + return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered)) + && matchesCollapsedRatType(ident); } } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 10136b3b8b..d8264b3625 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -27,6 +27,7 @@ import static android.content.Intent.EXTRA_UID; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.isNetworkTypeMobile; +import static android.net.NetworkIdentity.SUBTYPE_COMBINED; import static android.net.NetworkStack.checkNetworkStackPermission; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.IFACE_ALL; @@ -45,10 +46,12 @@ import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStatsHistory.FIELD_ALL; import static android.net.NetworkTemplate.buildTemplateMobileWildcard; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; +import static android.net.NetworkTemplate.getCollapsedRatType; import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; import static android.os.Trace.TRACE_TAG_NETWORK; import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED; +import static android.provider.Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED; import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION; import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE; import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES; @@ -64,6 +67,9 @@ import static android.provider.Settings.Global.NETSTATS_UID_TAG_BUCKET_DURATION; import static android.provider.Settings.Global.NETSTATS_UID_TAG_DELETE_AGE; import static android.provider.Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES; import static android.provider.Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE; +import static android.telephony.PhoneStateListener.LISTEN_NONE; +import static android.telephony.PhoneStateListener.LISTEN_SERVICE_STATE; +import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -109,6 +115,7 @@ import android.os.Binder; import android.os.DropBoxManager; import android.os.Environment; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.IBinder; import android.os.INetworkManagementService; @@ -125,6 +132,8 @@ import android.provider.Settings; import android.provider.Settings.Global; import android.service.NetworkInterfaceProto; import android.service.NetworkStatsServiceDumpProto; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.text.format.DateUtils; @@ -157,6 +166,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -173,6 +183,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private static final int MSG_PERFORM_POLL = 1; // Perform polling, persist network, and register the global alert again. private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2; + private static final int MSG_UPDATE_IFACES = 3; /** Flags to control detail level of poll event. */ private static final int FLAG_PERSIST_NETWORK = 0x1; @@ -229,12 +240,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * Settings that can be changed externally. */ public interface NetworkStatsSettings { - public long getPollInterval(); - public long getPollDelay(); - public boolean getSampleEnabled(); - public boolean getAugmentEnabled(); + long getPollInterval(); + long getPollDelay(); + boolean getSampleEnabled(); + boolean getAugmentEnabled(); + /** + * When enabled, all mobile data is reported under {@link NetworkIdentity#SUBTYPE_COMBINED}. + * When disabled, mobile data is broken down by a granular subtype representative of the + * actual subtype. {@see NetworkTemplate#getCollapsedRatType}. + * Enabling this decreases the level of detail but saves performance, disk space and + * amount of data logged. + */ + boolean getCombineSubtypeEnabled(); - public static class Config { + class Config { public final long bucketDuration; public final long rotateAgeMillis; public final long deleteAgeMillis; @@ -246,16 +265,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - public Config getDevConfig(); - public Config getXtConfig(); - public Config getUidConfig(); - public Config getUidTagConfig(); + Config getDevConfig(); + Config getXtConfig(); + Config getUidConfig(); + Config getUidTagConfig(); - public long getGlobalAlertBytes(long def); - public long getDevPersistBytes(long def); - public long getXtPersistBytes(long def); - public long getUidPersistBytes(long def); - public long getUidTagPersistBytes(long def); + long getGlobalAlertBytes(long def); + long getDevPersistBytes(long def); + long getXtPersistBytes(long def); + long getUidPersistBytes(long def); + long getUidTagPersistBytes(long def); } private final Object mStatsLock = new Object(); @@ -280,6 +299,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @GuardedBy("mStatsLock") private Network[] mDefaultNetworks = new Network[0]; + /** Last states of all networks sent from ConnectivityService. */ + @GuardedBy("mStatsLock") + @Nullable + private NetworkState[] mLastNetworkStates = null; + private final DropBoxNonMonotonicObserver mNonMonotonicObserver = new DropBoxNonMonotonicObserver(); @@ -355,6 +379,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { performPoll(FLAG_PERSIST_ALL); break; } + case MSG_UPDATE_IFACES: { + // If no cached states, ignore. + if (mLastNetworkStates == null) break; + updateIfaces(mDefaultNetworks, mLastNetworkStates, mActiveIface); + break; + } case MSG_PERFORM_POLL_REGISTER_ALERT: { performPoll(FLAG_PERSIST_NETWORK); registerGlobalAlert(); @@ -407,6 +437,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final HandlerThread handlerThread = mDeps.makeHandlerThread(); handlerThread.start(); mHandler = new NetworkStatsHandler(handlerThread.getLooper()); + mPhoneListener = new NetworkTypeListener(new HandlerExecutor(mHandler)); } /** @@ -486,6 +517,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime, mSettings.getPollInterval(), pollIntent); + // TODO: 1. listen to changes from all subscriptions. + // 2. listen to settings changed to support dynamically enable/disable. + // watch for networkType changes + if (!mSettings.getCombineSubtypeEnabled()) { + mTeleManager.listen(mPhoneListener, LISTEN_SERVICE_STATE); + } + registerGlobalAlert(); } @@ -506,6 +544,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext.unregisterReceiver(mUserReceiver); mContext.unregisterReceiver(mShutdownReceiver); + mTeleManager.listen(mPhoneListener, LISTEN_NONE); + final long currentTime = mClock.millis(); // persist any pending stats @@ -1156,6 +1196,38 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } }; + /** + * Receiver that watches for {@link TelephonyManager} changes, such as + * transitioning between Radio Access Technology(RAT) types. + */ + @NonNull + private final NetworkTypeListener mPhoneListener; + + class NetworkTypeListener extends PhoneStateListener { + private volatile int mLastCollapsedRatType = NETWORK_TYPE_UNKNOWN; + + NetworkTypeListener(@NonNull Executor executor) { + super(executor); + } + + @Override + public void onServiceStateChanged(@NonNull ServiceState ss) { + final int networkType = ss.getDataNetworkType(); + final int collapsedRatType = getCollapsedRatType(networkType); + if (collapsedRatType == mLastCollapsedRatType) return; + + if (LOGD) { + Log.d(TAG, "subtype changed for mobile: " + + mLastCollapsedRatType + " -> " + collapsedRatType); + } + // Protect service from frequently updating. Remove pending messages if any. + mHandler.removeMessages(MSG_UPDATE_IFACES); + mLastCollapsedRatType = collapsedRatType; + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_UPDATE_IFACES), mSettings.getPollDelay()); + } + } + private void updateIfaces( Network[] defaultNetworks, NetworkState[] networkStates, @@ -1177,7 +1249,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * they are combined under a single {@link NetworkIdentitySet}. */ @GuardedBy("mStatsLock") - private void updateIfacesLocked(Network[] defaultNetworks, NetworkState[] states) { + private void updateIfacesLocked(@Nullable Network[] defaultNetworks, + @NonNull NetworkState[] states) { if (!mSystemReady) return; if (LOGV) Slog.v(TAG, "updateIfacesLocked()"); @@ -1197,13 +1270,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mDefaultNetworks = defaultNetworks; } + mLastNetworkStates = states; + + final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled(); final ArraySet mobileIfaces = new ArraySet<>(); for (NetworkState state : states) { if (state.networkInfo.isConnected()) { final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType()); final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network); + final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED + : getSubTypeForState(state); final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state, - isDefault); + isDefault, subType); // Traffic occurring on the base interface is always counted for // both total usage and UID details. @@ -1264,6 +1342,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]); } + /** + * For networks with {@code TRANSPORT_CELLULAR}, get subType that was obtained through + * {@link PhoneStateListener}. Otherwise, return 0 given that other networks with different + * transport types do not actually fill this value. + */ + private int getSubTypeForState(@NonNull NetworkState state) { + if (!state.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + return 0; + } + + // TODO: return different subType for different subscriptions. + return mPhoneListener.mLastCollapsedRatType; + } + private static NetworkIdentitySet findOrCreateNetworkIdentitySet( ArrayMap map, K key) { NetworkIdentitySet ident = map.get(key); @@ -1617,6 +1709,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return; } + pw.println("Configs:"); + pw.increaseIndent(); + pw.printPair(NETSTATS_COMBINE_SUBTYPE_ENABLED, mSettings.getCombineSubtypeEnabled()); + pw.println(); + pw.decreaseIndent(); + pw.println("Active interfaces:"); pw.increaseIndent(); for (int i = 0; i < mActiveIfaces.size(); i++) { @@ -2045,6 +2143,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true); } @Override + public boolean getCombineSubtypeEnabled() { + return getGlobalBoolean(NETSTATS_COMBINE_SUBTYPE_ENABLED, false); + } + @Override public Config getDevConfig() { return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS), getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),