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 d08ba6ef5d25b..501484c95d49c 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -38,7 +38,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"; @@ -48,15 +48,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 7539a4be9081e..99176b2a36a49 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5431,6 +5431,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 2c8bb5da76928..5eb90f1c3da1f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -229,6 +229,8 @@ + diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 07a7fbab74175..ec6b063e82451 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; } } } @@ -4357,4 +4413,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); + } } +