From 30ad254a1e10e1d410719496c8cfda794015795f Mon Sep 17 00:00:00 2001 From: Vinit Deshapnde Date: Wed, 21 Aug 2013 13:09:01 -0700 Subject: [PATCH] Introduce network link quality statistics This change starts tracking traffic quality data for WiFi and mobile networks. The quality is tracked based on incidental traffic, and not on specific measurements. Theoretical bandwidths are hard-coded, as well as sampling interval; although sampling interval can be changed by setting a system policy. Bugs filed to remove shortcomings of this change - 10342372 Change LinkInfo name to something better 10342318 Move hardcoded values of MobileLinkInfo to resources so they can be updated without changing code Bug: 10006249 Change-Id: I83d8c7594da20fe53abbd5e1f909b1f606b035bb --- .../java/android/net/ConnectivityManager.java | 39 +++++ .../android/net/IConnectivityManager.aidl | 8 + .../android/server/ConnectivityService.java | 150 +++++++++++++++++- 3 files changed, 194 insertions(+), 3 deletions(-) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 1b418fa5f8..f6a3a4a772 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1442,4 +1442,43 @@ public class ConnectivityManager { } return null; } + + /** + * get the information about a specific network link + * @hide + */ + public LinkInfo getLinkInfo(int networkType) { + try { + LinkInfo li = mService.getLinkInfo(networkType); + return li; + } catch (RemoteException e) { + return null; + } + } + + /** + * get the information of currently active network link + * @hide + */ + public LinkInfo getActiveLinkInfo() { + try { + LinkInfo li = mService.getActiveLinkInfo(); + return li; + } catch (RemoteException e) { + return null; + } + } + + /** + * get the information of all network links + * @hide + */ + public LinkInfo[] getAllLinkInfo() { + try { + LinkInfo[] li = mService.getAllLinkInfo(); + return li; + } catch (RemoteException e) { + return null; + } + } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 992ec37cda..bf2dade3bc 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -16,6 +16,7 @@ package android.net; +import android.net.LinkInfo; import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkQuotaInfo; @@ -145,4 +146,11 @@ interface IConnectivityManager String getMobileProvisioningUrl(); String getMobileRedirectedProvisioningUrl(); + + LinkInfo getLinkInfo(int networkType); + + LinkInfo getActiveLinkInfo(); + + LinkInfo[] getAllLinkInfo(); + } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index a0e6dd1a87..27614d0f7d 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -31,6 +31,7 @@ import static android.net.ConnectivityManager.isNetworkTypeValid; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -56,6 +57,7 @@ import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.LinkInfo; import android.net.LinkProperties.CompareResult; import android.net.MobileDataStateTracker; import android.net.NetworkConfig; @@ -68,6 +70,7 @@ import android.net.NetworkUtils; import android.net.Proxy; import android.net.ProxyProperties; import android.net.RouteInfo; +import android.net.SamplingDataTracker; import android.net.Uri; import android.net.wifi.WifiStateTracker; import android.net.wimax.WimaxManagerConstants; @@ -144,8 +147,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.GregorianCalendar; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; @@ -174,6 +179,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final String FAIL_FAST_TIME_MS = "persist.radio.fail_fast_time_ms"; + private static final String ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED = + "android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED"; + + private static final int SAMPLE_INTERVAL_ELAPSED_REQURST_CODE = 0; + + private PendingIntent mSampleIntervalElapsedIntent; + + // Set network sampling interval at 12 minutes, this way, even if the timers get + // aggregated, it will fire at around 15 minutes, which should allow us to + // aggregate this timer with other timers (specially the socket keep alive timers) + private static final int DEFAULT_SAMPLING_INTERVAL_IN_SECONDS = (VDBG ? 30 : 12 * 60); + + // start network sampling a minute after booting ... + private static final int DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS = (VDBG ? 30 : 60); + + AlarmManager mAlarmManager; + // used in recursive route setting to add gateways for the host for which // a host route was requested. private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10; @@ -326,6 +348,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14; + /** + * user internally to indicate that data sampling interval is up + */ + private static final int EVENT_SAMPLE_INTERVAL_ELAPSED = 15; + /** Handler used for internal events. */ private InternalHandler mHandler; /** Handler used for incoming {@link NetworkStateTracker} events. */ @@ -392,6 +419,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { List mProtectedNetworks; private DataConnectionStats mDataConnectionStats; + private AtomicInteger mEnableFailFastMobileDataTag = new AtomicInteger(0); TelephonyManager mTelephonyManager; @@ -634,6 +662,29 @@ public class ConnectivityService extends IConnectivityManager.Stub { mDataConnectionStats = new DataConnectionStats(mContext); mDataConnectionStats.startMonitoring(); + // start network sampling .. + Intent intent = new Intent(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED, null); + mSampleIntervalElapsedIntent = PendingIntent.getBroadcast(mContext, + SAMPLE_INTERVAL_ELAPSED_REQURST_CODE, intent, 0); + + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + setAlarm(DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS * 1000, mSampleIntervalElapsedIntent); + + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED); + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED)) { + mHandler.sendMessage(mHandler.obtainMessage + (EVENT_SAMPLE_INTERVAL_ELAPSED)); + } + } + }, + new IntentFilter(filter)); + mPacManager = new PacManager(mContext); } @@ -1911,6 +1962,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { // if (mActiveDefaultNetwork != -1) { // currentPriority = mNetConfigs[mActiveDefaultNetwork].mPriority; // } + for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) { if (checkType == prevNetType) continue; if (mNetConfigs[checkType] == null) continue; @@ -1925,6 +1977,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { // optimization should work and we need to investigate why it doesn't work. // This could be related to how DEACTIVATE_DATA_CALL is reporting its // complete before it is really complete. + // if (!mNetTrackers[checkType].isAvailable()) continue; // if (currentPriority >= mNetConfigs[checkType].mPriority) continue; @@ -2520,12 +2573,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { } - /** + /** * Reads the network specific TCP buffer sizes from SystemProperties * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system * wide use */ - private void updateNetworkSettings(NetworkStateTracker nt) { + private void updateNetworkSettings(NetworkStateTracker nt) { String key = nt.getTcpBufferSizesPropName(); String bufferSizes = key == null ? null : SystemProperties.get(key); @@ -2547,7 +2600,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - /** + /** * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max] * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem * @@ -2956,6 +3009,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { + " != tag:" + tag); } } + case EVENT_SAMPLE_INTERVAL_ELAPSED: + handleNetworkSamplingTimeout(); + break; } } } @@ -4371,4 +4427,92 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } }; + + @Override + public LinkInfo getLinkInfo(int networkType) { + enforceAccessPermission(); + if (isNetworkTypeValid(networkType)) { + return mNetTrackers[networkType].getLinkInfo(); + } else { + return null; + } + } + + @Override + public LinkInfo getActiveLinkInfo() { + enforceAccessPermission(); + if (isNetworkTypeValid(mActiveDefaultNetwork)) { + return mNetTrackers[mActiveDefaultNetwork].getLinkInfo(); + } else { + return null; + } + } + + @Override + public LinkInfo[] getAllLinkInfo() { + enforceAccessPermission(); + final ArrayList result = Lists.newArrayList(); + for (NetworkStateTracker tracker : mNetTrackers) { + if (tracker != null) { + LinkInfo li = tracker.getLinkInfo(); + if (li != null) { + result.add(li); + } + } + } + + return result.toArray(new LinkInfo[result.size()]); + } + + /* Infrastructure for network sampling */ + + private void handleNetworkSamplingTimeout() { + + log("Sampling interval elapsed, updating statistics .."); + + // initialize list of interfaces .. + Map mapIfaceToSample = + new HashMap(); + for (NetworkStateTracker tracker : mNetTrackers) { + if (tracker != null) { + String ifaceName = tracker.getNetworkInterfaceName(); + if (ifaceName != null) { + mapIfaceToSample.put(ifaceName, null); + } + } + } + + // Read samples for all interfaces + SamplingDataTracker.getSamplingSnapshots(mapIfaceToSample); + + // process samples for all networks + for (NetworkStateTracker tracker : mNetTrackers) { + if (tracker != null) { + String ifaceName = tracker.getNetworkInterfaceName(); + SamplingDataTracker.SamplingSnapshot ss = mapIfaceToSample.get(ifaceName); + if (ss != null) { + // end the previous sampling cycle + tracker.stopSampling(ss); + // start a new sampling cycle .. + tracker.startSampling(ss); + } + } + } + + log("Done."); + + int samplingIntervalInSeconds = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS, + DEFAULT_SAMPLING_INTERVAL_IN_SECONDS); + + if (DBG) log("Setting timer for " + String.valueOf(samplingIntervalInSeconds) + "seconds"); + + setAlarm(samplingIntervalInSeconds * 1000, mSampleIntervalElapsedIntent); + } + + void setAlarm(int timeoutInMilliseconds, PendingIntent intent) { + long wakeupTime = SystemClock.elapsedRealtime() + timeoutInMilliseconds; + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, wakeupTime, intent); + } } +