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
This commit is contained in:
Vinit Deshapnde
2013-08-21 13:09:01 -07:00
parent db5cb48920
commit 30ad254a1e
3 changed files with 194 additions and 3 deletions

View File

@@ -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;
}
}
}

View File

@@ -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();
}

View File

@@ -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;
@@ -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<LinkInfo> 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<String, SamplingDataTracker.SamplingSnapshot> mapIfaceToSample =
new HashMap<String, SamplingDataTracker.SamplingSnapshot>();
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);
}
}