From 825f0828ac3671ab5d18c0a854468bed3f8c3a34 Mon Sep 17 00:00:00 2001 From: junyulai Date: Thu, 12 Dec 2019 19:42:59 +0800 Subject: [PATCH] [SM02] Support record mobile network stats by collapsed rat type Previously network stats could be recorded by different rat type. However, the feature was disabled by ag/173504 since rat type frequently flapping between HSPA+ and UMTS. Given that this feature might be useful for collecting metrics, re-implement it based on current architecture and reduce the overhead introduced by frequently flapping by: 1. only react when rat type changes between 2G/3G/4G/5G. 2. reduce the number of records by only recording a subset of rat type that represented for a given network class. 3. enforce 1 second rate limit if flapping too much. Note that the feature is still disabled but will be enabled in follow-up patches. Test: manual test Bug: 129082217 Change-Id: Ic6b2f10f2c8b082820e0662eb9cee70d70d28cd6 Merged-In: Ic6b2f10f2c8b082820e0662eb9cee70d70d28cd6 (cherry picked from commit 15ab452e7e3f00289fbedbdb86c512ad560dda7e) --- core/java/android/net/NetworkIdentity.java | 15 ++-- core/java/android/net/NetworkTemplate.java | 40 +++++++++ .../server/net/NetworkStatsService.java | 89 ++++++++++++++++++- 3 files changed, 134 insertions(+), 10 deletions(-) diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index c1198aac27..b67ad519b4 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; @@ -42,11 +43,8 @@ public class NetworkIdentity implements Comparable { /** * 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 + // TODO: make this flag configurable through settings. See http://b/146415925 public static final boolean COMBINE_SUBTYPE_ENABLED = true; public static final int SUBTYPE_COMBINED = -1; @@ -187,13 +185,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..6080d16b9d 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -37,6 +37,7 @@ import static android.net.wifi.WifiInfo.sanitizeSsid; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.TelephonyManager; import android.util.BackupUtils; import android.util.Log; @@ -394,6 +395,45 @@ public class NetworkTemplate implements Parcelable { } } + /** + * 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; + } + } + /** * Check if matches Wi-Fi network template. */ diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 10136b3b8b..7c4624d25c 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -27,6 +27,8 @@ 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.COMBINE_SUBTYPE_ENABLED; +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,6 +47,7 @@ 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; @@ -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; @@ -280,6 +291,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 +371,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 +429,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 +509,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime, mSettings.getPollInterval(), pollIntent); + // TODO: listen to changes from all subscriptions. + // watch for networkType changes + if (!COMBINE_SUBTYPE_ENABLED) { + mTeleManager.listen(mPhoneListener, LISTEN_SERVICE_STATE); + } + registerGlobalAlert(); } @@ -506,6 +535,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext.unregisterReceiver(mUserReceiver); mContext.unregisterReceiver(mShutdownReceiver); + if (!COMBINE_SUBTYPE_ENABLED) { + mTeleManager.listen(mPhoneListener, LISTEN_NONE); + } + final long currentTime = mClock.millis(); // persist any pending stats @@ -1156,6 +1189,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 (LOGV) { + 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), SECOND_IN_MILLIS); + } + } + private void updateIfaces( Network[] defaultNetworks, NetworkState[] networkStates, @@ -1177,7 +1242,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 +1263,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mDefaultNetworks = defaultNetworks; } + mLastNetworkStates = states; + 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 = 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 +1333,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]); } + /** + * If combine subtype is not enabled. For networks with {@code TRANSPORT_CELLULAR}, get + * subType that 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 (COMBINE_SUBTYPE_ENABLED) return SUBTYPE_COMBINED; + + 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);