From 1f12cb52a494a3eaefc62d03a8d2fdf47a5535e9 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 --- .../BluetoothTetheringDataTracker.java | 9 +- .../android/net/BaseNetworkStateTracker.java | 40 ++- .../java/android/net/ConnectivityManager.java | 39 +++ .../android/net/DummyDataStateTracker.java | 6 +- .../java/android/net/EthernetDataTracker.java | 6 +- .../android/net/IConnectivityManager.aidl | 8 + core/java/android/net/LinkInfo.aidl | 19 ++ core/java/android/net/LinkInfo.java | 128 ++++++++ core/java/android/net/LinkProperties.aidl | 1 - .../android/net/MobileDataStateTracker.java | 202 +++++++++++- core/java/android/net/MobileLinkInfo.java | 89 ++++++ .../java/android/net/NetworkStateTracker.java | 23 +- .../java/android/net/SamplingDataTracker.java | 295 ++++++++++++++++++ core/java/android/net/WifiLinkInfo.java | 75 +++++ core/java/android/provider/Settings.java | 11 + core/res/AndroidManifest.xml | 2 + .../android/server/ConnectivityService.java | 150 ++++++++- .../android/net/wifi/WifiStateTracker.java | 76 ++++- 18 files changed, 1136 insertions(+), 43 deletions(-) create mode 100644 core/java/android/net/LinkInfo.aidl create mode 100644 core/java/android/net/LinkInfo.java create mode 100644 core/java/android/net/MobileLinkInfo.java create mode 100644 core/java/android/net/SamplingDataTracker.java create mode 100644 core/java/android/net/WifiLinkInfo.java diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index 0aedecb82dd2b..a9b717693356a 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -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 mAsyncChannel = new AtomicReference(null); diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java index e87f84c56a21c..c39488eafc15d 100644 --- a/core/java/android/net/BaseNetworkStateTracker.java +++ b/core/java/android/net/BaseNetworkStateTracker.java @@ -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 + } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 1b418fa5f8a54..f6a3a4a772a85 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/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java index ee738fd9c715e..51a1191fd49e6 100644 --- a/core/java/android/net/DummyDataStateTracker.java +++ b/core/java/android/net/DummyDataStateTracker.java @@ -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; diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index 7999c66e2a8b0..3a21a56afaf74 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -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 = ""; diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 992ec37cda4b2..bf2dade3bc366 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/core/java/android/net/LinkInfo.aidl b/core/java/android/net/LinkInfo.aidl new file mode 100644 index 0000000000000..716674b084a4e --- /dev/null +++ b/core/java/android/net/LinkInfo.aidl @@ -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; diff --git a/core/java/android/net/LinkInfo.java b/core/java/android/net/LinkInfo.java new file mode 100644 index 0000000000000..98e8f35bc6e0c --- /dev/null +++ b/core/java/android/net/LinkInfo.java @@ -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 CREATOR = + new Creator() { + 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(); + } + +} diff --git a/core/java/android/net/LinkProperties.aidl b/core/java/android/net/LinkProperties.aidl index 9cd06d57844ef..3cb9525761ed3 100644 --- a/core/java/android/net/LinkProperties.aidl +++ b/core/java/android/net/LinkProperties.aidl @@ -18,4 +18,3 @@ package android.net; parcelable LinkProperties; - diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index e4fd312c06719..faa13b082c34f 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -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); + } } diff --git a/core/java/android/net/MobileLinkInfo.java b/core/java/android/net/MobileLinkInfo.java new file mode 100644 index 0000000000000..2d1827503990b --- /dev/null +++ b/core/java/android/net/MobileLinkInfo.java @@ -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; + } +} diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index 9ed7533719073..a3d7b1448102c 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -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); + } diff --git a/core/java/android/net/SamplingDataTracker.java b/core/java/android/net/SamplingDataTracker.java new file mode 100644 index 0000000000000..b5dc14081c855 --- /dev/null +++ b/core/java/android/net/SamplingDataTracker.java @@ -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 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(); + } + } +} + diff --git a/core/java/android/net/WifiLinkInfo.java b/core/java/android/net/WifiLinkInfo.java new file mode 100644 index 0000000000000..f3b00326b46fc --- /dev/null +++ b/core/java/android/net/WifiLinkInfo.java @@ -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; + } +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 585115a2c9363..c5fd39195bc7b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -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. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1363e3ca2a5a7..a0f53b604e8e9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -202,6 +202,8 @@ + diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index a0e6dd1a87f50..27614d0f7d68e 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); + } } + diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 461dedb8a04bd..62630d61ca6b4 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -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); + } } +