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 2241d45c68
commit 1f12cb52a4
18 changed files with 1136 additions and 43 deletions

View File

@@ -16,6 +16,7 @@
package android.bluetooth;
import android.net.BaseNetworkStateTracker;
import android.os.IBinder;
import android.os.ServiceManager;
import android.os.INetworkManagementService;
@@ -54,7 +55,7 @@ import java.util.concurrent.atomic.AtomicReference;
*
* @hide
*/
public class BluetoothTetheringDataTracker implements NetworkStateTracker {
public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker {
private static final String NETWORKTYPE = "BLUETOOTH_TETHER";
private static final String TAG = "BluetoothTethering";
private static final boolean DBG = true;
@@ -66,18 +67,12 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker {
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
private final Object mLinkPropertiesLock = new Object();
private LinkProperties mLinkProperties;
private LinkCapabilities mLinkCapabilities;
private final Object mNetworkInfoLock = new Object();
private NetworkInfo mNetworkInfo;
private BluetoothPan mBluetoothPan;
private static String mRevTetheredIface;
/* For sending events to connectivity service handler */
private Handler mCsHandler;
protected Context mContext;
private static BluetoothTetheringDataTracker sInstance;
private BtdtHandler mBtdtHandler;
private AtomicReference<AsyncChannel> mAsyncChannel = new AtomicReference<AsyncChannel>(null);

View File

@@ -57,6 +57,10 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker {
mLinkCapabilities = new LinkCapabilities();
}
protected BaseNetworkStateTracker() {
// By default, let the sub classes construct everything
}
@Deprecated
protected Handler getTargetHandler() {
return mTarget;
@@ -73,29 +77,36 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker {
}
@Override
public final void startMonitoring(Context context, Handler target) {
public void startMonitoring(Context context, Handler target) {
mContext = Preconditions.checkNotNull(context);
mTarget = Preconditions.checkNotNull(target);
startMonitoringInternal();
}
protected abstract void startMonitoringInternal();
protected void startMonitoringInternal() {
}
@Override
public final NetworkInfo getNetworkInfo() {
public NetworkInfo getNetworkInfo() {
return new NetworkInfo(mNetworkInfo);
}
@Override
public final LinkProperties getLinkProperties() {
public LinkProperties getLinkProperties() {
return new LinkProperties(mLinkProperties);
}
@Override
public final LinkCapabilities getLinkCapabilities() {
public LinkCapabilities getLinkCapabilities() {
return new LinkCapabilities(mLinkCapabilities);
}
@Override
public LinkInfo getLinkInfo() {
return null;
}
@Override
public void captivePortalCheckComplete() {
// not implemented
@@ -176,4 +187,23 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker {
public void supplyMessenger(Messenger messenger) {
// not supported on this network
}
@Override
public String getNetworkInterfaceName() {
if (mLinkProperties != null) {
return mLinkProperties.getInterfaceName();
} else {
return null;
}
}
@Override
public void startSampling(SamplingDataTracker.SamplingSnapshot s) {
// nothing to do
}
@Override
public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
// nothing to do
}
}

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

@@ -29,18 +29,14 @@ import android.util.Slog;
*
* {@hide}
*/
public class DummyDataStateTracker implements NetworkStateTracker {
public class DummyDataStateTracker extends BaseNetworkStateTracker {
private static final String TAG = "DummyDataStateTracker";
private static final boolean DBG = true;
private static final boolean VDBG = false;
private NetworkInfo mNetworkInfo;
private boolean mTeardownRequested = false;
private Handler mTarget;
private Context mContext;
private LinkProperties mLinkProperties;
private LinkCapabilities mLinkCapabilities;
private boolean mPrivateDnsRouteSet = false;
private boolean mDefaultRouteSet = false;

View File

@@ -36,7 +36,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* ConnectivityService.
* @hide
*/
public class EthernetDataTracker implements NetworkStateTracker {
public class EthernetDataTracker extends BaseNetworkStateTracker {
private static final String NETWORKTYPE = "ETHERNET";
private static final String TAG = "Ethernet";
@@ -46,15 +46,11 @@ public class EthernetDataTracker implements NetworkStateTracker {
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
private static boolean mLinkUp;
private LinkProperties mLinkProperties;
private LinkCapabilities mLinkCapabilities;
private NetworkInfo mNetworkInfo;
private InterfaceObserver mInterfaceObserver;
private String mHwAddr;
/* For sending events to connectivity service handler */
private Handler mCsHandler;
private Context mContext;
private static EthernetDataTracker sInstance;
private static String sIfaceMatch = "";

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

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
parcelable LinkInfo;

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Class that represents useful attributes of generic network links
* such as the upload/download throughput or packet error rate.
* Generally speaking, you should be dealing with instances of
* LinkInfo subclasses, such as {@link android.net.#WifiLinkInfo}
* or {@link android.net.#MobileLinkInfo} which provide additional
* information.
* @hide
*/
public class LinkInfo implements Parcelable
{
public static final int UNKNOWN = Integer.MAX_VALUE;
public static final int NORMALIZED_MIN_SIGNAL_STRENGTH = 0;
public static final int NORMALIZED_MAX_SIGNAL_STRENGTH = 99;
public static final int NORMALIZED_SIGNAL_STRENGTH_RANGE = NORMALIZED_MAX_SIGNAL_STRENGTH + 1;
/* Network type as defined by ConnectivityManager */
public int mNetworkType = ConnectivityManager.TYPE_NONE;
public int mNormalizedSignalStrength = UNKNOWN;
public int mPacketCount = UNKNOWN;
public int mPacketErrorCount = UNKNOWN;
public int mTheoreticalTxBandwidth = UNKNOWN;
public int mTheoreticalRxBandwidth = UNKNOWN;
public int mTheoreticalLatency = UNKNOWN;
/* Timestamp when last sample was made available */
public long mLastDataSampleTime = UNKNOWN;
/* Sample duration in millisecond */
public int mDataSampleDuration = UNKNOWN;
public LinkInfo() {
}
/**
* Implement the Parcelable interface
* @hide
*/
public int describeContents() {
return 0;
}
/**
* Implement the Parcelable interface.
*/
protected static final int OBJECT_TYPE_LINKINFO = 1;
protected static final int OBJECT_TYPE_WIFI_LINKINFO = 2;
protected static final int OBJECT_TYPE_MOBILE_LINKINFO = 3;
public void writeToParcel(Parcel dest, int flags) {
writeToParcel(dest, flags, OBJECT_TYPE_LINKINFO);
}
public void writeToParcel(Parcel dest, int flags, int objectType) {
dest.writeInt(objectType);
dest.writeInt(mNetworkType);
dest.writeInt(mNormalizedSignalStrength);
dest.writeInt(mPacketCount);
dest.writeInt(mPacketErrorCount);
dest.writeInt(mTheoreticalTxBandwidth);
dest.writeInt(mTheoreticalRxBandwidth);
dest.writeInt(mTheoreticalLatency);
dest.writeLong(mLastDataSampleTime);
dest.writeInt(mDataSampleDuration);
}
public static final Creator<LinkInfo> CREATOR =
new Creator<LinkInfo>() {
public LinkInfo createFromParcel(Parcel in) {
int objectType = in.readInt();
if (objectType == OBJECT_TYPE_LINKINFO) {
LinkInfo li = new LinkInfo();
li.initializeFromParcel(in);
return li;
} else if (objectType == OBJECT_TYPE_WIFI_LINKINFO) {
return WifiLinkInfo.createFromParcelBody(in);
} else if (objectType == OBJECT_TYPE_MOBILE_LINKINFO) {
return MobileLinkInfo.createFromParcelBody(in);
} else {
return null;
}
}
public LinkInfo[] newArray(int size) {
return new LinkInfo[size];
}
};
protected void initializeFromParcel(Parcel in) {
mNetworkType = in.readInt();
mNormalizedSignalStrength = in.readInt();
mPacketCount = in.readInt();
mPacketErrorCount = in.readInt();
mTheoreticalTxBandwidth = in.readInt();
mTheoreticalRxBandwidth = in.readInt();
mTheoreticalLatency = in.readInt();
mLastDataSampleTime = in.readLong();
mDataSampleDuration = in.readInt();
}
}

View File

@@ -18,4 +18,3 @@
package android.net;
parcelable LinkProperties;

View File

@@ -28,6 +28,8 @@ import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
@@ -49,7 +51,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
*
* {@hide}
*/
public class MobileDataStateTracker implements NetworkStateTracker {
public class MobileDataStateTracker extends BaseNetworkStateTracker {
private static final String TAG = "MobileDataStateTracker";
private static final boolean DBG = true;
@@ -59,12 +61,8 @@ public class MobileDataStateTracker implements NetworkStateTracker {
private ITelephony mPhoneService;
private String mApnType;
private NetworkInfo mNetworkInfo;
private boolean mTeardownRequested = false;
private Handler mTarget;
private Context mContext;
private LinkProperties mLinkProperties;
private LinkCapabilities mLinkCapabilities;
private boolean mPrivateDnsRouteSet = false;
private boolean mDefaultRouteSet = false;
@@ -78,6 +76,10 @@ public class MobileDataStateTracker implements NetworkStateTracker {
private AtomicBoolean mIsCaptivePortal = new AtomicBoolean(false);
private SignalStrength mSignalStrength;
private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker();
/**
* Create a new MobileDataStateTracker
* @param netType the ConnectivityManager network type
@@ -108,8 +110,19 @@ public class MobileDataStateTracker implements NetworkStateTracker {
mContext.registerReceiver(new MobileDataStateReceiver(), filter);
mMobileDataState = PhoneConstants.DataState.DISCONNECTED;
TelephonyManager tm = (TelephonyManager)mContext.getSystemService(
Context.TELEPHONY_SERVICE);
tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
mSignalStrength = signalStrength;
}
};
static class MdstHandler extends Handler {
private MobileDataStateTracker mMdst;
@@ -251,6 +264,30 @@ public class MobileDataStateTracker implements NetworkStateTracker {
setDetailedState(DetailedState.CONNECTED, reason, apnName);
break;
}
if (VDBG) {
Slog.d(TAG, "TelephonyMgr.DataConnectionStateChanged");
if (mNetworkInfo != null) {
Slog.d(TAG, "NetworkInfo = " + mNetworkInfo.toString());
Slog.d(TAG, "subType = " + String.valueOf(mNetworkInfo.getSubtype()));
Slog.d(TAG, "subType = " + mNetworkInfo.getSubtypeName());
}
if (mLinkProperties != null) {
Slog.d(TAG, "LinkProperties = " + mLinkProperties.toString());
} else {
Slog.d(TAG, "LinkProperties = " );
}
if (mLinkCapabilities != null) {
Slog.d(TAG, "LinkCapabilities = " + mLinkCapabilities.toString());
} else {
Slog.d(TAG, "LinkCapabilities = " );
}
}
/* lets not sample traffic data across state changes */
mSamplingDataTracker.resetSamplingData();
} else {
// There was no state change. Check if LinkProperties has been updated.
if (TextUtils.equals(reason, PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) {
@@ -283,7 +320,7 @@ public class MobileDataStateTracker implements NetworkStateTracker {
String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
if (DBG) {
log("Received " + intent.getAction() +
" broadcast" + reason == null ? "" : "(" + reason + ")");
" broadcast" + (reason == null ? "" : "(" + reason + ")"));
}
setDetailedState(DetailedState.FAILED, reason, apnName);
} else {
@@ -557,7 +594,7 @@ public class MobileDataStateTracker implements NetworkStateTracker {
return writer.toString();
}
/**
/**
* Internal method supporting the ENABLE_MMS feature.
* @param apnType the type of APN to be enabled or disabled (e.g., mms)
* @param enable {@code true} to enable the specified APN type,
@@ -617,9 +654,11 @@ public class MobileDataStateTracker implements NetworkStateTracker {
}
}
/**
* @see android.net.NetworkStateTracker#getLinkProperties()
*/
@Override
public LinkProperties getLinkProperties() {
return new LinkProperties(mLinkProperties);
}
@@ -627,6 +666,7 @@ public class MobileDataStateTracker implements NetworkStateTracker {
/**
* @see android.net.NetworkStateTracker#getLinkCapabilities()
*/
@Override
public LinkCapabilities getLinkCapabilities() {
return new LinkCapabilities(mLinkCapabilities);
}
@@ -648,4 +688,152 @@ public class MobileDataStateTracker implements NetworkStateTracker {
static private void sloge(String s) {
Slog.e(TAG, s);
}
@Override
public LinkInfo getLinkInfo() {
if (mNetworkInfo == null || mNetworkInfo.getType() == ConnectivityManager.TYPE_NONE) {
// no data available yet; just return
return null;
}
MobileLinkInfo li = new MobileLinkInfo();
li.mNetworkType = mNetworkInfo.getType();
mSamplingDataTracker.setCommonLinkInfoFields(li);
if (mNetworkInfo.getSubtype() != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
li.mMobileNetworkType = mNetworkInfo.getSubtype();
NetworkDataEntry entry = getNetworkDataEntry(mNetworkInfo.getSubtype());
if (entry != null) {
li.mTheoreticalRxBandwidth = entry.downloadBandwidth;
li.mTheoreticalRxBandwidth = entry.uploadBandwidth;
li.mTheoreticalLatency = entry.latency;
}
if (mSignalStrength != null) {
li.mNormalizedSignalStrength = getNormalizedSignalStrength(
li.mMobileNetworkType, mSignalStrength);
}
}
SignalStrength ss = mSignalStrength;
if (ss != null) {
li.mRssi = ss.getGsmSignalStrength();
li.mGsmErrorRate = ss.getGsmBitErrorRate();
li.mCdmaDbm = ss.getCdmaDbm();
li.mCdmaEcio = ss.getCdmaEcio();
li.mEvdoDbm = ss.getEvdoDbm();
li.mEvdoEcio = ss.getEvdoEcio();
li.mEvdoSnr = ss.getEvdoSnr();
li.mLteSignalStrength = ss.getLteSignalStrength();
li.mLteRsrp = ss.getLteRsrp();
li.mLteRsrq = ss.getLteRsrq();
li.mLteRssnr = ss.getLteRssnr();
li.mLteCqi = ss.getLteCqi();
}
if (VDBG) {
Slog.d(TAG, "Returning LinkInfo with"
+ " MobileNetworkType = " + String.valueOf(li.mMobileNetworkType)
+ " Theoretical Rx BW = " + String.valueOf(li.mTheoreticalRxBandwidth)
+ " gsm Signal Strength = " + String.valueOf(li.mRssi)
+ " cdma Signal Strength = " + String.valueOf(li.mCdmaDbm)
+ " evdo Signal Strength = " + String.valueOf(li.mEvdoDbm)
+ " Lte Signal Strength = " + String.valueOf(li.mLteSignalStrength));
}
return li;
}
static class NetworkDataEntry {
public int networkType;
public int downloadBandwidth; // in kbps
public int uploadBandwidth; // in kbps
public int latency; // in millisecond
NetworkDataEntry(int i1, int i2, int i3, int i4) {
networkType = i1;
downloadBandwidth = i2;
uploadBandwidth = i3;
latency = i4;
}
}
private static NetworkDataEntry [] mTheoreticalBWTable = new NetworkDataEntry[] {
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EDGE, 237, 118, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_GPRS, 48, 40, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_UMTS, 384, 64, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSDPA, 14400, -1, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSUPA, 14400, 5760, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPA, 14400, 5760, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPAP, 21000, 5760, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_CDMA, -1, -1, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_1xRTT, -1, -1, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_0, 2468, 153, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_A, 3072, 1800, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_B, 14700, 1800, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_IDEN, -1, -1, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_LTE, 100000, 50000, -1),
new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EHRPD, -1, -1, -1),
};
private static NetworkDataEntry getNetworkDataEntry(int networkType) {
for (NetworkDataEntry entry : mTheoreticalBWTable) {
if (entry.networkType == networkType) {
return entry;
}
}
Slog.e(TAG, "Could not find Theoretical BW entry for " + String.valueOf(networkType));
return null;
}
private static int getNormalizedSignalStrength(int networkType, SignalStrength ss) {
int level;
switch(networkType) {
case TelephonyManager.NETWORK_TYPE_EDGE:
case TelephonyManager.NETWORK_TYPE_GPRS:
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:
level = ss.getGsmLevel();
break;
case TelephonyManager.NETWORK_TYPE_CDMA:
case TelephonyManager.NETWORK_TYPE_1xRTT:
level = ss.getCdmaLevel();
break;
case TelephonyManager.NETWORK_TYPE_EVDO_0:
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
level = ss.getEvdoLevel();
break;
case TelephonyManager.NETWORK_TYPE_LTE:
level = ss.getLteLevel();
break;
case TelephonyManager.NETWORK_TYPE_IDEN:
case TelephonyManager.NETWORK_TYPE_EHRPD:
default:
return LinkInfo.UNKNOWN;
}
return (level * LinkInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE) /
SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
}
@Override
public void startSampling(SamplingDataTracker.SamplingSnapshot s) {
mSamplingDataTracker.startSampling(s);
}
@Override
public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
mSamplingDataTracker.stopSampling(s);
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.os.Parcel;
import android.net.LinkInfo;
/**
* Class that represents useful attributes of mobile network links
* such as the upload/download throughput or error rate etc.
* @hide
*/
public final class MobileLinkInfo extends LinkInfo
{
// Represents TelephonyManager.NetworkType
public int mMobileNetworkType = UNKNOWN;
public int mRssi = UNKNOWN;
public int mGsmErrorRate = UNKNOWN;
public int mCdmaDbm = UNKNOWN;
public int mCdmaEcio = UNKNOWN;
public int mEvdoDbm = UNKNOWN;
public int mEvdoEcio = UNKNOWN;
public int mEvdoSnr = UNKNOWN;
public int mLteSignalStrength = UNKNOWN;
public int mLteRsrp = UNKNOWN;
public int mLteRsrq = UNKNOWN;
public int mLteRssnr = UNKNOWN;
public int mLteCqi = UNKNOWN;
/**
* Implement the Parcelable interface.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags, OBJECT_TYPE_MOBILE_LINKINFO);
dest.writeInt(mMobileNetworkType);
dest.writeInt(mRssi);
dest.writeInt(mGsmErrorRate);
dest.writeInt(mCdmaDbm);
dest.writeInt(mCdmaEcio);
dest.writeInt(mEvdoDbm);
dest.writeInt(mEvdoEcio);
dest.writeInt(mEvdoSnr);
dest.writeInt(mLteSignalStrength);
dest.writeInt(mLteRsrp);
dest.writeInt(mLteRsrq);
dest.writeInt(mLteRssnr);
dest.writeInt(mLteCqi);
}
/* Un-parceling helper */
public static MobileLinkInfo createFromParcelBody(Parcel in) {
MobileLinkInfo li = new MobileLinkInfo();
li.initializeFromParcel(in);
li.mMobileNetworkType = in.readInt();
li.mRssi = in.readInt();
li.mGsmErrorRate = in.readInt();
li.mCdmaDbm = in.readInt();
li.mCdmaEcio = in.readInt();
li.mEvdoDbm = in.readInt();
li.mEvdoEcio = in.readInt();
li.mEvdoSnr = in.readInt();
li.mLteSignalStrength = in.readInt();
li.mLteRsrp = in.readInt();
li.mLteRsrq = in.readInt();
li.mLteRssnr = in.readInt();
li.mLteCqi = in.readInt();
return li;
}
}

View File

@@ -83,7 +83,6 @@ public interface NetworkStateTracker {
*/
public static final int EVENT_NETWORK_DISCONNECTED = BASE_NETWORK_STATE_TRACKER + 5;
/**
* -------------------------------------------------------------
* Control Interface
@@ -119,6 +118,12 @@ public interface NetworkStateTracker {
*/
public LinkCapabilities getLinkCapabilities();
/**
* Get interesting information about this network link
* @return a copy of link information, null if not available
*/
public LinkInfo getLinkInfo();
/**
* Return the system properties name associated with the tcp buffer sizes
* for this network.
@@ -234,4 +239,20 @@ public interface NetworkStateTracker {
* the underlying network specific code.
*/
public void supplyMessenger(Messenger messenger);
/*
* Network interface name that we'll lookup for sampling data
*/
public String getNetworkInterfaceName();
/*
* Save the starting sample
*/
public void startSampling(SamplingDataTracker.SamplingSnapshot s);
/*
* Save the ending sample
*/
public void stopSampling(SamplingDataTracker.SamplingSnapshot s);
}

View File

@@ -0,0 +1,295 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.os.SystemClock;
import android.util.Slog;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
/**
* @hide
*/
public class SamplingDataTracker
{
private static final boolean DBG = false;
private static final String TAG = "SamplingDataTracker";
public static class SamplingSnapshot
{
public int mTxByteCount;
public int mRxByteCount;
public int mTxPacketCount;
public int mRxPacketCount;
public int mTxPacketErrorCount;
public int mRxPacketErrorCount;
public long mTimestamp;
}
public static void getSamplingSnapshots(Map<String, SamplingSnapshot> mapIfaceToSample) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("/proc/net/dev"));
// Skip over the line bearing column titles (there are 2 lines)
String line;
reader.readLine();
reader.readLine();
while ((line = reader.readLine()) != null) {
// remove leading whitespace
line = line.trim();
String[] tokens = line.split("[ ]+");
if (tokens.length < 17) {
continue;
}
/* column format is
* Interface (Recv)bytes packets errs drop fifo frame compressed multicast \
* (Transmit)bytes packets errs drop fifo colls carrier compress
*/
String currentIface = tokens[0].split(":")[0];
if (DBG) Slog.d(TAG, "Found data for interface " + currentIface);
if (mapIfaceToSample.containsKey(currentIface)) {
SamplingSnapshot ss = new SamplingSnapshot();
ss.mTxByteCount = Integer.parseInt(tokens[1]);
ss.mTxPacketCount = Integer.parseInt(tokens[2]);
ss.mTxPacketErrorCount = Integer.parseInt(tokens[3]);
ss.mRxByteCount = Integer.parseInt(tokens[9]);
ss.mRxPacketCount = Integer.parseInt(tokens[10]);
ss.mRxPacketErrorCount = Integer.parseInt(tokens[11]);
ss.mTimestamp = SystemClock.elapsedRealtime();
if (DBG) {
Slog.d(TAG, "Interface = " + currentIface);
Slog.d(TAG, "ByteCount = " + String.valueOf(ss.mTxByteCount));
Slog.d(TAG, "TxPacketCount = " + String.valueOf(ss.mTxPacketCount));
Slog.d(TAG, "TxPacketErrorCount = "
+ String.valueOf(ss.mTxPacketErrorCount));
Slog.d(TAG, "RxByteCount = " + String.valueOf(ss.mRxByteCount));
Slog.d(TAG, "RxPacketCount = " + String.valueOf(ss.mRxPacketCount));
Slog.d(TAG, "RxPacketErrorCount = "
+ String.valueOf(ss.mRxPacketErrorCount));
Slog.d(TAG, "Timestamp = " + String.valueOf(ss.mTimestamp));
Slog.d(TAG, "---------------------------");
}
mapIfaceToSample.put(currentIface, ss);
}
}
if (DBG) {
Iterator it = mapIfaceToSample.entrySet().iterator();
while (it.hasNext()) {
Map.Entry kvpair = (Map.Entry)it.next();
if (kvpair.getValue() == null) {
Slog.d(TAG, "could not find snapshot for interface " + kvpair.getKey());
}
}
}
} catch(FileNotFoundException e) {
Slog.e(TAG, "could not find /proc/net/dev");
} catch (IOException e) {
Slog.e(TAG, "could not read /proc/net/dev");
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
Slog.e(TAG, "could not close /proc/net/dev");
}
}
}
// Snapshots from previous sampling interval
private SamplingSnapshot mBeginningSample;
private SamplingSnapshot mEndingSample;
// Starting snapshot of current interval
private SamplingSnapshot mLastSample;
// Protects sampling data from concurrent access
public final Object mSamplingDataLock = new Object();
// We need long enough time for a good sample
private final int MINIMUM_SAMPLING_INTERVAL = 15 * 1000;
// statistics is useless unless we have enough data
private final int MINIMUM_SAMPLED_PACKETS = 30;
public void startSampling(SamplingSnapshot s) {
synchronized(mSamplingDataLock) {
mLastSample = s;
}
}
public void stopSampling(SamplingSnapshot s) {
synchronized(mSamplingDataLock) {
if (mLastSample != null) {
if (s.mTimestamp - mLastSample.mTimestamp > MINIMUM_SAMPLING_INTERVAL
&& getSampledPacketCount(mLastSample, s) > MINIMUM_SAMPLED_PACKETS) {
mBeginningSample = mLastSample;
mEndingSample = s;
mLastSample = null;
} else {
if (DBG) Slog.d(TAG, "Throwing current sample away because it is too small");
}
}
}
}
public void resetSamplingData() {
if (DBG) Slog.d(TAG, "Resetting sampled network data");
synchronized(mSamplingDataLock) {
// We could just take another sample here and treat it as an
// 'ending sample' effectively shortening sampling interval, but that
// requires extra work (specifically, reading the sample needs to be
// done asynchronously)
mLastSample = null;
}
}
public int getSampledTxByteCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mTxByteCount - mBeginningSample.mTxByteCount;
} else {
return LinkInfo.UNKNOWN;
}
}
}
public int getSampledTxPacketCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mTxPacketCount - mBeginningSample.mTxPacketCount;
} else {
return LinkInfo.UNKNOWN;
}
}
}
public int getSampledTxPacketErrorCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mTxPacketErrorCount - mBeginningSample.mTxPacketErrorCount;
} else {
return LinkInfo.UNKNOWN;
}
}
}
public int getSampledRxByteCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mRxByteCount - mBeginningSample.mRxByteCount;
} else {
return LinkInfo.UNKNOWN;
}
}
}
public int getSampledRxPacketCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mRxPacketCount - mBeginningSample.mRxPacketCount;
} else {
return LinkInfo.UNKNOWN;
}
}
}
public int getSampledPacketCount() {
return getSampledPacketCount(mBeginningSample, mEndingSample);
}
public int getSampledPacketCount(SamplingSnapshot begin, SamplingSnapshot end) {
if (begin != null && end != null) {
int rxPacketCount = end.mRxPacketCount - begin.mRxPacketCount;
int txPacketCount = end.mTxPacketCount - begin.mTxPacketCount;
return rxPacketCount + txPacketCount;
} else {
return LinkInfo.UNKNOWN;
}
}
public int getSampledPacketErrorCount() {
if (mBeginningSample != null && mEndingSample != null) {
int rxPacketErrorCount = getSampledRxPacketErrorCount();
int txPacketErrorCount = getSampledTxPacketErrorCount();
return rxPacketErrorCount + txPacketErrorCount;
} else {
return LinkInfo.UNKNOWN;
}
}
public int getSampledRxPacketErrorCount() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return mEndingSample.mRxPacketErrorCount - mBeginningSample.mRxPacketErrorCount;
} else {
return LinkInfo.UNKNOWN;
}
}
}
public long getSampleTimestamp() {
synchronized(mSamplingDataLock) {
if (mEndingSample != null) {
return mEndingSample.mTimestamp;
} else {
return LinkInfo.UNKNOWN;
}
}
}
public int getSampleDuration() {
synchronized(mSamplingDataLock) {
if (mBeginningSample != null && mEndingSample != null) {
return (int) (mEndingSample.mTimestamp - mBeginningSample.mTimestamp);
} else {
return LinkInfo.UNKNOWN;
}
}
}
public void setCommonLinkInfoFields(LinkInfo li) {
synchronized(mSamplingDataLock) {
li.mLastDataSampleTime = getSampleTimestamp();
li.mDataSampleDuration = getSampleDuration();
li.mPacketCount = getSampledPacketCount();
li.mPacketErrorCount = getSampledPacketErrorCount();
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.os.Parcel;
import android.net.LinkInfo;
/**
* Class that represents useful attributes of wifi network links
* such as the upload/download throughput or error rate etc.
* @hide
*/
public final class WifiLinkInfo extends LinkInfo
{
/**
* Type enumerations for Wifi Network
*/
/* Indicates Wifi network type such as b/g etc*/
public int mType = UNKNOWN;
public String mBssid;
/* Rssi found by scans */
public int mRssi = UNKNOWN;
/* packet statistics */
public int mTxGood = UNKNOWN;
public int mTxBad = UNKNOWN;
/**
* Implement the Parcelable interface.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags, OBJECT_TYPE_WIFI_LINKINFO);
dest.writeInt(mType);
dest.writeInt(mRssi);
dest.writeInt(mTxGood);
dest.writeInt(mTxBad);
dest.writeString(mBssid);
}
/* Un-parceling helper */
public static WifiLinkInfo createFromParcelBody(Parcel in) {
WifiLinkInfo li = new WifiLinkInfo();
li.initializeFromParcel(in);
li.mType = in.readInt();
li.mRssi = in.readInt();
li.mTxGood = in.readInt();
li.mTxBad = in.readInt();
li.mBssid = in.readString();
return li;
}
}

View File

@@ -5346,6 +5346,17 @@ public final class Settings {
*/
public static final String CONNECTIVITY_CHANGE_DELAY = "connectivity_change_delay";
/**
* Network sampling interval, in seconds. We'll generate link information
* about bytes/packets sent and error rates based on data sampled in this interval
*
* @hide
*/
public static final String CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS =
"connectivity_sampling_interval_in_seconds";
/**
* The series of successively longer delays used in retrying to download PAC file.
* Last delay is used between successful PAC downloads.

View File

@@ -202,6 +202,8 @@
<protected-broadcast android:name="android.net.conn.TETHER_STATE_CHANGED" />
<protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" />
<protected-broadcast android:name="android.net.conn.NETWORK_CONDITIONS_MEASURED" />
<protected-brodcast
android:name="android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED" />
<protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE" />
<protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE" />
<protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />

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

View File

@@ -20,11 +20,14 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.BaseNetworkStateTracker;
import android.net.LinkCapabilities;
import android.net.LinkInfo;
import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkStateTracker;
import android.net.SamplingDataTracker;
import android.net.WifiLinkInfo;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
@@ -37,7 +40,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
*
* @hide
*/
public class WifiStateTracker implements NetworkStateTracker {
public class WifiStateTracker extends BaseNetworkStateTracker {
private static final String NETWORKTYPE = "WIFI";
private static final String TAG = "WifiStateTracker";
@@ -48,17 +51,17 @@ public class WifiStateTracker implements NetworkStateTracker {
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
private LinkProperties mLinkProperties;
private LinkCapabilities mLinkCapabilities;
private NetworkInfo mNetworkInfo;
private NetworkInfo.State mLastState = NetworkInfo.State.UNKNOWN;
private WifiInfo mWifiInfo;
/* For sending events to connectivity service handler */
private Handler mCsHandler;
private Context mContext;
private BroadcastReceiver mWifiStateReceiver;
private WifiManager mWifiManager;
private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker();
public WifiStateTracker(int netType, String networkName) {
mNetworkInfo = new NetworkInfo(netType, 0, networkName, "");
mLinkProperties = new LinkProperties();
@@ -174,6 +177,7 @@ public class WifiStateTracker implements NetworkStateTracker {
/**
* Fetch NetworkInfo for the network
*/
@Override
public NetworkInfo getNetworkInfo() {
return new NetworkInfo(mNetworkInfo);
}
@@ -181,6 +185,7 @@ public class WifiStateTracker implements NetworkStateTracker {
/**
* Fetch LinkProperties for the network
*/
@Override
public LinkProperties getLinkProperties() {
return new LinkProperties(mLinkProperties);
}
@@ -191,10 +196,47 @@ public class WifiStateTracker implements NetworkStateTracker {
*
* @return a copy of this connections capabilities, may be empty but never null.
*/
@Override
public LinkCapabilities getLinkCapabilities() {
return new LinkCapabilities(mLinkCapabilities);
}
/**
* Return link info
* @return an object of type WifiLinkInfo
*/
@Override
public LinkInfo getLinkInfo() {
if (mNetworkInfo == null) {
// no data available yet; just return
return null;
}
WifiLinkInfo li = new WifiLinkInfo();
li.mNetworkType = mNetworkInfo.getType();
synchronized(mSamplingDataTracker.mSamplingDataLock) {
mSamplingDataTracker.setCommonLinkInfoFields(li);
li.mTxGood = mSamplingDataTracker.getSampledTxPacketCount();
li.mTxBad = mSamplingDataTracker.getSampledTxPacketErrorCount();
}
// li.setTheoreticalRxBandwidth(??);
// li.setTheoreticalTxBandwidth(??);
if (mWifiInfo != null) {
li.mBssid = mWifiInfo.getBSSID();
int rssi = mWifiInfo.getRssi();
li.mRssi = rssi;
li.mNormalizedSignalStrength = mWifiManager.calculateSignalLevel(rssi,
LinkInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE);
}
return li;
}
/**
* Check if default route is set
*/
@@ -224,6 +266,7 @@ public class WifiStateTracker implements NetworkStateTracker {
if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
WifiManager.EXTRA_NETWORK_INFO);
mLinkProperties = intent.getParcelableExtra(
WifiManager.EXTRA_LINK_PROPERTIES);
if (mLinkProperties == null) {
@@ -234,7 +277,9 @@ public class WifiStateTracker implements NetworkStateTracker {
if (mLinkCapabilities == null) {
mLinkCapabilities = new LinkCapabilities();
}
// don't want to send redundent state messages
mWifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
// don't want to send redundant state messages
// but send portal check detailed state notice
NetworkInfo.State state = mNetworkInfo.getState();
if (mLastState == state &&
@@ -242,13 +287,15 @@ public class WifiStateTracker implements NetworkStateTracker {
return;
} else {
mLastState = state;
/* lets not sample traffic data across state changes */
mSamplingDataTracker.resetSamplingData();
}
Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED,
new NetworkInfo(mNetworkInfo));
msg.sendToTarget();
} else if (intent.getAction().equals(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)) {
mLinkProperties = (LinkProperties) intent.getParcelableExtra(
WifiManager.EXTRA_LINK_PROPERTIES);
mLinkProperties = intent.getParcelableExtra(WifiManager.EXTRA_LINK_PROPERTIES);
Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
msg.sendToTarget();
}
@@ -273,4 +320,15 @@ public class WifiStateTracker implements NetworkStateTracker {
public void supplyMessenger(Messenger messenger) {
// not supported on this network
}
@Override
public void startSampling(SamplingDataTracker.SamplingSnapshot s) {
mSamplingDataTracker.startSampling(s);
}
@Override
public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
mSamplingDataTracker.stopSampling(s);
}
}