From 367d15ab1a33b6159447fa8542d4fa8ff148371c Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 22 Sep 2011 14:59:51 -0700 Subject: [PATCH] Watch network subtype, tethering teardown, empty. Watch for changes to telephony network subtype, and update iface mapping to persist stats under correct type. Update network stats before removing tethering NAT rules. Skip recording that would create empty historical buckets. Query UID stats before iface stats to always skew positive when counters are actively rolling forward. Bug: 5360042, 5359860, 5335674, 5334448 Change-Id: I8aa37b568e8ffb70647218aa1aff5195d3e44d5a --- .../java/android/net/NetworkStatsHistory.java | 5 + .../android/server/ConnectivityService.java | 7 +- .../java/com/android/server/SystemServer.java | 3 +- .../server/connectivity/Tethering.java | 17 +++- .../server/net/NetworkStatsService.java | 99 ++++++++++++++----- .../server/NetworkStatsServiceTest.java | 1 + 6 files changed, 102 insertions(+), 30 deletions(-) diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index a5cdf70325ee8..a6635bee205ad 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -270,6 +270,11 @@ public class NetworkStatsHistory implements Parcelable { || entry.operations < 0) { throw new IllegalArgumentException("tried recording negative data"); } + if (entry.rxBytes == 0 && entry.rxPackets == 0 && entry.txBytes == 0 && entry.txPackets == 0 + && entry.operations == 0) { + // nothing to record; skip + return; + } // create any buckets needed by this range ensureBuckets(start, end); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 86ac296c2c955..8112b1d0d1491 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -35,6 +35,7 @@ import android.net.EthernetDataTracker; import android.net.IConnectivityManager; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; +import android.net.INetworkStatsService; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LinkProperties.CompareResult; @@ -306,8 +307,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { // the set of network types that can only be enabled by system/sig apps List mProtectedNetworks; - public ConnectivityService( - Context context, INetworkManagementService netd, INetworkPolicyManager policyManager) { + public ConnectivityService(Context context, INetworkManagementService netd, + INetworkStatsService statsService, INetworkPolicyManager policyManager) { if (DBG) log("ConnectivityService starting up"); HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread"); @@ -496,7 +497,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b); - mTethering = new Tethering(mContext, nmService, mHandler.getLooper()); + mTethering = new Tethering(mContext, nmService, statsService, mHandler.getLooper()); mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 || mTethering.getTetherableWifiRegexs().length != 0 || mTethering.getTetherableBluetoothRegexs().length != 0) && diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 977dd6f5d54f8..72d19171048b2 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -364,7 +364,8 @@ class ServerThread extends Thread { try { Slog.i(TAG, "Connectivity Service"); - connectivity = new ConnectivityService(context, networkManagement, networkPolicy); + connectivity = new ConnectivityService( + context, networkManagement, networkStats, networkPolicy); ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity); networkStats.bindConnectivityManager(connectivity); networkPolicy.bindConnectivityManager(connectivity); diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 10f6d2ccdc5fa..6b9c088521246 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -29,6 +29,7 @@ import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; +import android.net.INetworkStatsService; import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; @@ -88,7 +89,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub { // upstream type list and the DUN_REQUIRED secure-setting private int mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_NONE; - private INetworkManagementService mNMService; + private final INetworkManagementService mNMService; + private final INetworkStatsService mStatsService; private Looper mLooper; private HandlerThread mThread; @@ -124,9 +126,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private boolean mUsbTetherRequested; // true if USB tethering should be started // when RNDIS is enabled - public Tethering(Context context, INetworkManagementService nmService, Looper looper) { + public Tethering(Context context, INetworkManagementService nmService, + INetworkStatsService statsService, Looper looper) { mContext = context; mNMService = nmService; + mStatsService = statsService; mLooper = looper; mIfaces = new HashMap(); @@ -913,6 +917,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub { case CMD_INTERFACE_DOWN: if (mMyUpstreamIfaceName != null) { try { + // about to tear down NAT; gather remaining statistics + mStatsService.forceUpdate(); + mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName); mMyUpstreamIfaceName = null; } catch (Exception e) { @@ -957,6 +964,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } if (mMyUpstreamIfaceName != null) { try { + // about to tear down NAT; gather remaining statistics + mStatsService.forceUpdate(); + mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName); mMyUpstreamIfaceName = null; } catch (Exception e) { @@ -995,6 +1005,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub { case CMD_TETHER_MODE_DEAD: if (mMyUpstreamIfaceName != null) { try { + // about to tear down NAT; gather remaining statistics + mStatsService.forceUpdate(); + mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName); mMyUpstreamIfaceName = null; } catch (Exception e) { diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index bc65205cbe10b..aa46795b4fb9c 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -24,8 +24,8 @@ import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.content.Intent.ACTION_SHUTDOWN; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; -import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_DEFAULT; @@ -43,9 +43,12 @@ import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL; import static android.provider.Settings.Secure.NETSTATS_TAG_MAX_HISTORY; import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION; import static android.provider.Settings.Secure.NETSTATS_UID_MAX_HISTORY; +import static android.telephony.PhoneStateListener.LISTEN_DATA_CONNECTION_STATE; +import static android.telephony.PhoneStateListener.LISTEN_NONE; 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; +import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats; @@ -80,6 +83,7 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; +import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Log; @@ -121,7 +125,7 @@ import libcore.io.IoUtils; */ public class NetworkStatsService extends INetworkStatsService.Stub { private static final String TAG = "NetworkStats"; - private static final boolean LOGD = true; + private static final boolean LOGD = false; private static final boolean LOGV = false; /** File header magic number: "ANET" */ @@ -132,7 +136,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private static final int VERSION_UID_WITH_TAG = 3; private static final int VERSION_UID_WITH_SET = 4; - private static final int MSG_PERFORM_POLL = 0x1; + private static final int MSG_PERFORM_POLL = 1; + private static final int MSG_UPDATE_IFACES = 2; /** Flags to control detail level of poll event. */ private static final int FLAG_PERSIST_NETWORK = 0x10; @@ -144,6 +149,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final INetworkManagementService mNetworkManager; private final IAlarmManager mAlarmManager; private final TrustedTime mTime; + private final TelephonyManager mTeleManager; private final NetworkStatsSettings mSettings; private final PowerManager.WakeLock mWakeLock; @@ -227,6 +233,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService"); mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager"); mTime = checkNotNull(time, "missing TrustedTime"); + mTeleManager = checkNotNull(TelephonyManager.getDefault(), "missing TelephonyManager"); mSettings = checkNotNull(settings, "missing NetworkStatsSettings"); final PowerManager powerManager = (PowerManager) context.getSystemService( @@ -279,6 +286,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // ignored; service lives in system_server } + // watch for networkType changes that aren't broadcast through + // CONNECTIVITY_ACTION_IMMEDIATE above. + mTeleManager.listen(mPhoneListener, LISTEN_DATA_CONNECTION_STATE); + registerPollAlarmLocked(); registerGlobalAlert(); @@ -288,10 +299,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void shutdownLocked() { mContext.unregisterReceiver(mConnReceiver); + mContext.unregisterReceiver(mTetherReceiver); mContext.unregisterReceiver(mPollReceiver); mContext.unregisterReceiver(mRemovedReceiver); mContext.unregisterReceiver(mShutdownReceiver); + mTeleManager.listen(mPhoneListener, LISTEN_NONE); + writeNetworkStatsLocked(); if (mUidStatsLoaded) { writeUidStatsLocked(); @@ -535,14 +549,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public void onReceive(Context context, Intent intent) { // on background handler thread, and verified CONNECTIVITY_INTERNAL // permission above. - synchronized (mStatsLock) { - mWakeLock.acquire(); - try { - updateIfacesLocked(); - } finally { - mWakeLock.release(); - } - } + updateIfaces(); } }; @@ -619,6 +626,46 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } }; + private int mLastPhoneState = TelephonyManager.DATA_UNKNOWN; + private int mLastPhoneNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + + /** + * Receiver that watches for {@link TelephonyManager} changes, such as + * transitioning between network types. + */ + private PhoneStateListener mPhoneListener = new PhoneStateListener() { + @Override + public void onDataConnectionStateChanged(int state, int networkType) { + final boolean stateChanged = state != mLastPhoneState; + final boolean networkTypeChanged = networkType != mLastPhoneNetworkType; + + if (networkTypeChanged && !stateChanged) { + // networkType changed without a state change, which means we + // need to roll our own update. delay long enough for + // ConnectivityManager to process. + // TODO: add direct event to ConnectivityService instead of + // relying on this delay. + if (LOGV) Slog.v(TAG, "triggering delayed updateIfaces()"); + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_UPDATE_IFACES), SECOND_IN_MILLIS); + } + + mLastPhoneState = state; + mLastPhoneNetworkType = networkType; + } + }; + + private void updateIfaces() { + synchronized (mStatsLock) { + mWakeLock.acquire(); + try { + updateIfacesLocked(); + } finally { + mWakeLock.release(); + } + } + } + /** * Inspect all current {@link NetworkState} to derive mapping from {@code * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo} @@ -713,19 +760,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final long threshold = mSettings.getPersistThreshold(); try { - // record network stats - final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary(); - performNetworkPollLocked(networkSnapshot, currentTime); - - // persist when enough network data has occurred - final NetworkStats persistNetworkDelta = computeStatsDelta( - mLastPersistNetworkSnapshot, networkSnapshot, true); - final boolean networkPastThreshold = persistNetworkDelta.getTotalBytes() > threshold; - if (persistForce || (persistNetwork && networkPastThreshold)) { - writeNetworkStatsLocked(); - mLastPersistNetworkSnapshot = networkSnapshot; - } - // record tethering stats; persisted during normal UID cycle below final String[] ifacePairs = mConnManager.getTetheredIfacePairs(); final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering( @@ -744,6 +778,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub { writeUidStatsLocked(); mLastPersistUidSnapshot = uidSnapshot; } + + // record network stats + final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary(); + performNetworkPollLocked(networkSnapshot, currentTime); + + // persist when enough network data has occurred + final NetworkStats persistNetworkDelta = computeStatsDelta( + mLastPersistNetworkSnapshot, networkSnapshot, true); + final boolean networkPastThreshold = persistNetworkDelta.getTotalBytes() > threshold; + if (persistForce || (persistNetwork && networkPastThreshold)) { + writeNetworkStatsLocked(); + mLastPersistNetworkSnapshot = networkSnapshot; + } } catch (IllegalStateException e) { Log.wtf(TAG, "problem reading network stats", e); } catch (RemoteException e) { @@ -1356,6 +1403,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { performPoll(flags); return true; } + case MSG_UPDATE_IFACES: { + updateIfaces(); + return true; + } default: { return false; } diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index 99ae027f4b163..2ead254af18c3 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -776,6 +776,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private void expectNetworkStatsPoll() throws Exception { mNetManager.setGlobalAlert(anyLong()); expectLastCall().anyTimes(); + expect(mConnManager.getTetheredIfacePairs()).andReturn(null).anyTimes(); } private void assertStatsFilesExist(boolean exist) {