OSDN Git Service

Merge "Introduce network link quality statistics" into klp-dev
authorVinit Deshapnde <vinitd@google.com>
Thu, 22 Aug 2013 16:16:44 +0000 (16:16 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Thu, 22 Aug 2013 16:16:45 +0000 (16:16 +0000)
18 files changed:
core/java/android/bluetooth/BluetoothTetheringDataTracker.java
core/java/android/net/BaseNetworkStateTracker.java
core/java/android/net/ConnectivityManager.java
core/java/android/net/DummyDataStateTracker.java
core/java/android/net/EthernetDataTracker.java
core/java/android/net/IConnectivityManager.aidl
core/java/android/net/LinkInfo.aidl [new file with mode: 0644]
core/java/android/net/LinkInfo.java [new file with mode: 0644]
core/java/android/net/LinkProperties.aidl
core/java/android/net/MobileDataStateTracker.java
core/java/android/net/MobileLinkInfo.java [new file with mode: 0644]
core/java/android/net/NetworkStateTracker.java
core/java/android/net/SamplingDataTracker.java [new file with mode: 0644]
core/java/android/net/WifiLinkInfo.java [new file with mode: 0644]
core/java/android/provider/Settings.java
core/res/AndroidManifest.xml
services/java/com/android/server/ConnectivityService.java
wifi/java/android/net/wifi/WifiStateTracker.java

index 0aedecb..a9b7176 100644 (file)
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.net.BaseNetworkStateTracker;
 import android.os.IBinder;
 import android.os.ServiceManager;
 import android.os.INetworkManagementService;
@@ -54,7 +55,7 @@ import java.util.concurrent.atomic.AtomicReference;
  *
  * @hide
  */
-public class BluetoothTetheringDataTracker implements NetworkStateTracker {
+public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker {
     private static final String NETWORKTYPE = "BLUETOOTH_TETHER";
     private static final String TAG = "BluetoothTethering";
     private static final boolean DBG = true;
@@ -66,18 +67,12 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker {
     private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
 
     private final Object mLinkPropertiesLock = new Object();
-    private LinkProperties mLinkProperties;
-
-    private LinkCapabilities mLinkCapabilities;
-
     private final Object mNetworkInfoLock = new Object();
-    private NetworkInfo mNetworkInfo;
 
     private BluetoothPan mBluetoothPan;
     private static String mRevTetheredIface;
     /* For sending events to connectivity service handler */
     private Handler mCsHandler;
-    protected Context mContext;
     private static BluetoothTetheringDataTracker sInstance;
     private BtdtHandler mBtdtHandler;
     private AtomicReference<AsyncChannel> mAsyncChannel = new AtomicReference<AsyncChannel>(null);
index e87f84c..c39488e 100644 (file)
@@ -57,6 +57,10 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker {
         mLinkCapabilities = new LinkCapabilities();
     }
 
+    protected BaseNetworkStateTracker() {
+        // By default, let the sub classes construct everything
+    }
+
     @Deprecated
     protected Handler getTargetHandler() {
         return mTarget;
@@ -73,30 +77,37 @@ 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
+    }
 }
index 1b418fa..f6a3a4a 100644 (file)
@@ -1442,4 +1442,43 @@ public class ConnectivityManager {
         }
         return null;
     }
+
+    /**
+     * get the information about a specific network link
+     * @hide
+     */
+    public LinkInfo getLinkInfo(int networkType) {
+        try {
+            LinkInfo li = mService.getLinkInfo(networkType);
+            return li;
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * get the information of currently active network link
+     * @hide
+     */
+    public LinkInfo getActiveLinkInfo() {
+        try {
+            LinkInfo li = mService.getActiveLinkInfo();
+            return li;
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * get the information of all network links
+     * @hide
+     */
+    public LinkInfo[] getAllLinkInfo() {
+        try {
+            LinkInfo[] li = mService.getAllLinkInfo();
+            return li;
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
 }
index ee738fd..51a1191 100644 (file)
@@ -29,18 +29,14 @@ import android.util.Slog;
  *
  * {@hide}
  */
-public class DummyDataStateTracker implements NetworkStateTracker {
+public class DummyDataStateTracker extends BaseNetworkStateTracker {
 
     private static final String TAG = "DummyDataStateTracker";
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
-    private NetworkInfo mNetworkInfo;
     private boolean mTeardownRequested = false;
     private Handler mTarget;
-    private Context mContext;
-    private LinkProperties mLinkProperties;
-    private LinkCapabilities mLinkCapabilities;
     private boolean mPrivateDnsRouteSet = false;
     private boolean mDefaultRouteSet = false;
 
index d08ba6e..501484c 100644 (file)
@@ -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 = "";
index 992ec37..bf2dade 100644 (file)
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.net.LinkInfo;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.NetworkQuotaInfo;
@@ -145,4 +146,11 @@ interface IConnectivityManager
     String getMobileProvisioningUrl();
 
     String getMobileRedirectedProvisioningUrl();
+
+    LinkInfo getLinkInfo(int networkType);
+
+    LinkInfo getActiveLinkInfo();
+
+    LinkInfo[] getAllLinkInfo();
+
 }
diff --git a/core/java/android/net/LinkInfo.aidl b/core/java/android/net/LinkInfo.aidl
new file mode 100644 (file)
index 0000000..716674b
--- /dev/null
@@ -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 (file)
index 0000000..98e8f35
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ *  Class that represents useful attributes of generic network links
+ *  such as the upload/download throughput or packet error rate.
+ *  Generally speaking, you should be dealing with instances of
+ *  LinkInfo subclasses, such as {@link android.net.#WifiLinkInfo}
+ *  or {@link android.net.#MobileLinkInfo} which provide additional
+ *  information.
+ *  @hide
+ */
+public class LinkInfo implements Parcelable
+{
+    public static final int UNKNOWN = Integer.MAX_VALUE;
+
+    public static final int NORMALIZED_MIN_SIGNAL_STRENGTH = 0;
+
+    public static final int NORMALIZED_MAX_SIGNAL_STRENGTH = 99;
+
+    public static final int NORMALIZED_SIGNAL_STRENGTH_RANGE = NORMALIZED_MAX_SIGNAL_STRENGTH + 1;
+
+    /* Network type as defined by ConnectivityManager */
+    public int mNetworkType = ConnectivityManager.TYPE_NONE;
+
+    public int mNormalizedSignalStrength = UNKNOWN;
+
+    public int mPacketCount = UNKNOWN;
+    public int mPacketErrorCount = UNKNOWN;
+    public int mTheoreticalTxBandwidth = UNKNOWN;
+    public int mTheoreticalRxBandwidth = UNKNOWN;
+    public int mTheoreticalLatency = UNKNOWN;
+
+    /* Timestamp when last sample was made available */
+    public long mLastDataSampleTime = UNKNOWN;
+
+    /* Sample duration in millisecond */
+    public int mDataSampleDuration = UNKNOWN;
+
+    public LinkInfo() {
+
+    }
+
+    /**
+     * Implement the Parcelable interface
+     * @hide
+     */
+    public int describeContents() {
+        return 0;
+    }
+    /**
+     * Implement the Parcelable interface.
+     */
+
+    protected static final int OBJECT_TYPE_LINKINFO = 1;
+    protected static final int OBJECT_TYPE_WIFI_LINKINFO = 2;
+    protected static final int OBJECT_TYPE_MOBILE_LINKINFO = 3;
+
+    public void writeToParcel(Parcel dest, int flags) {
+        writeToParcel(dest, flags, OBJECT_TYPE_LINKINFO);
+    }
+
+    public void writeToParcel(Parcel dest, int flags, int objectType) {
+        dest.writeInt(objectType);
+        dest.writeInt(mNetworkType);
+        dest.writeInt(mNormalizedSignalStrength);
+        dest.writeInt(mPacketCount);
+        dest.writeInt(mPacketErrorCount);
+        dest.writeInt(mTheoreticalTxBandwidth);
+        dest.writeInt(mTheoreticalRxBandwidth);
+        dest.writeInt(mTheoreticalLatency);
+        dest.writeLong(mLastDataSampleTime);
+        dest.writeInt(mDataSampleDuration);
+    }
+
+    public static final Creator<LinkInfo> CREATOR =
+            new Creator<LinkInfo>() {
+                public LinkInfo createFromParcel(Parcel in) {
+                    int objectType = in.readInt();
+                    if (objectType == OBJECT_TYPE_LINKINFO) {
+                        LinkInfo li = new LinkInfo();
+                        li.initializeFromParcel(in);
+                        return li;
+                    } else if (objectType == OBJECT_TYPE_WIFI_LINKINFO) {
+                        return WifiLinkInfo.createFromParcelBody(in);
+                    } else if (objectType == OBJECT_TYPE_MOBILE_LINKINFO) {
+                        return MobileLinkInfo.createFromParcelBody(in);
+                    } else {
+                        return null;
+                    }
+                }
+
+                public LinkInfo[] newArray(int size) {
+                    return new LinkInfo[size];
+                }
+            };
+
+    protected void initializeFromParcel(Parcel in) {
+        mNetworkType = in.readInt();
+        mNormalizedSignalStrength = in.readInt();
+        mPacketCount = in.readInt();
+        mPacketErrorCount = in.readInt();
+        mTheoreticalTxBandwidth = in.readInt();
+        mTheoreticalRxBandwidth = in.readInt();
+        mTheoreticalLatency = in.readInt();
+        mLastDataSampleTime = in.readLong();
+        mDataSampleDuration = in.readInt();
+    }
+
+}
index e4fd312..faa13b0 100644 (file)
@@ -28,6 +28,8 @@ import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -49,7 +51,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
  *
  * {@hide}
  */
-public class MobileDataStateTracker implements NetworkStateTracker {
+public class MobileDataStateTracker extends BaseNetworkStateTracker {
 
     private static final String TAG = "MobileDataStateTracker";
     private static final boolean DBG = true;
@@ -59,12 +61,8 @@ public class MobileDataStateTracker implements NetworkStateTracker {
     private ITelephony mPhoneService;
 
     private String mApnType;
-    private NetworkInfo mNetworkInfo;
     private boolean mTeardownRequested = false;
     private Handler mTarget;
-    private Context mContext;
-    private LinkProperties mLinkProperties;
-    private LinkCapabilities mLinkCapabilities;
     private boolean mPrivateDnsRouteSet = false;
     private boolean mDefaultRouteSet = false;
 
@@ -78,6 +76,10 @@ public class MobileDataStateTracker implements NetworkStateTracker {
 
     private AtomicBoolean mIsCaptivePortal = new AtomicBoolean(false);
 
+    private SignalStrength mSignalStrength;
+
+    private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker();
+
     /**
      * Create a new MobileDataStateTracker
      * @param netType the ConnectivityManager network type
@@ -108,8 +110,19 @@ public class MobileDataStateTracker implements NetworkStateTracker {
 
         mContext.registerReceiver(new MobileDataStateReceiver(), filter);
         mMobileDataState = PhoneConstants.DataState.DISCONNECTED;
+
+        TelephonyManager tm = (TelephonyManager)mContext.getSystemService(
+                Context.TELEPHONY_SERVICE);
+        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
     }
 
+    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            mSignalStrength = signalStrength;
+        }
+    };
+
     static class MdstHandler extends Handler {
         private MobileDataStateTracker mMdst;
 
@@ -251,6 +264,30 @@ public class MobileDataStateTracker implements NetworkStateTracker {
                             setDetailedState(DetailedState.CONNECTED, reason, apnName);
                             break;
                     }
+
+                    if (VDBG) {
+                        Slog.d(TAG, "TelephonyMgr.DataConnectionStateChanged");
+                        if (mNetworkInfo != null) {
+                            Slog.d(TAG, "NetworkInfo = " + mNetworkInfo.toString());
+                            Slog.d(TAG, "subType = " + String.valueOf(mNetworkInfo.getSubtype()));
+                            Slog.d(TAG, "subType = " + mNetworkInfo.getSubtypeName());
+                        }
+                        if (mLinkProperties != null) {
+                            Slog.d(TAG, "LinkProperties = " + mLinkProperties.toString());
+                        } else {
+                            Slog.d(TAG, "LinkProperties = " );
+                        }
+
+                        if (mLinkCapabilities != null) {
+                            Slog.d(TAG, "LinkCapabilities = " + mLinkCapabilities.toString());
+                        } else {
+                            Slog.d(TAG, "LinkCapabilities = " );
+                        }
+                    }
+
+
+                    /* lets not sample traffic data across state changes */
+                    mSamplingDataTracker.resetSamplingData();
                 } else {
                     // There was no state change. Check if LinkProperties has been updated.
                     if (TextUtils.equals(reason, PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) {
@@ -283,7 +320,7 @@ public class MobileDataStateTracker implements NetworkStateTracker {
                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
                 if (DBG) {
                     log("Received " + intent.getAction() +
-                                " broadcast" + reason == null ? "" : "(" + reason + ")");
+                                " broadcast" + (reason == null ? "" : "(" + reason + ")"));
                 }
                 setDetailedState(DetailedState.FAILED, reason, apnName);
             } else {
@@ -557,7 +594,7 @@ public class MobileDataStateTracker implements NetworkStateTracker {
         return writer.toString();
     }
 
-   /**
+    /**
      * Internal method supporting the ENABLE_MMS feature.
      * @param apnType the type of APN to be enabled or disabled (e.g., mms)
      * @param enable {@code true} to enable the specified APN type,
@@ -617,9 +654,11 @@ public class MobileDataStateTracker implements NetworkStateTracker {
         }
     }
 
+
     /**
      * @see android.net.NetworkStateTracker#getLinkProperties()
      */
+    @Override
     public LinkProperties getLinkProperties() {
         return new LinkProperties(mLinkProperties);
     }
@@ -627,6 +666,7 @@ public class MobileDataStateTracker implements NetworkStateTracker {
     /**
      * @see android.net.NetworkStateTracker#getLinkCapabilities()
      */
+    @Override
     public LinkCapabilities getLinkCapabilities() {
         return new LinkCapabilities(mLinkCapabilities);
     }
@@ -648,4 +688,152 @@ public class MobileDataStateTracker implements NetworkStateTracker {
     static private void sloge(String s) {
         Slog.e(TAG, s);
     }
+
+    @Override
+    public LinkInfo getLinkInfo() {
+        if (mNetworkInfo == null || mNetworkInfo.getType() == ConnectivityManager.TYPE_NONE) {
+            // no data available yet; just return
+            return null;
+        }
+
+        MobileLinkInfo li = new MobileLinkInfo();
+
+        li.mNetworkType = mNetworkInfo.getType();
+
+        mSamplingDataTracker.setCommonLinkInfoFields(li);
+
+        if (mNetworkInfo.getSubtype() != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+            li.mMobileNetworkType = mNetworkInfo.getSubtype();
+
+            NetworkDataEntry entry = getNetworkDataEntry(mNetworkInfo.getSubtype());
+            if (entry != null) {
+                li.mTheoreticalRxBandwidth = entry.downloadBandwidth;
+                li.mTheoreticalRxBandwidth = entry.uploadBandwidth;
+                li.mTheoreticalLatency = entry.latency;
+            }
+
+            if (mSignalStrength != null) {
+                li.mNormalizedSignalStrength = getNormalizedSignalStrength(
+                        li.mMobileNetworkType, mSignalStrength);
+            }
+        }
+
+        SignalStrength ss = mSignalStrength;
+        if (ss != null) {
+
+            li.mRssi = ss.getGsmSignalStrength();
+            li.mGsmErrorRate = ss.getGsmBitErrorRate();
+            li.mCdmaDbm = ss.getCdmaDbm();
+            li.mCdmaEcio = ss.getCdmaEcio();
+            li.mEvdoDbm = ss.getEvdoDbm();
+            li.mEvdoEcio = ss.getEvdoEcio();
+            li.mEvdoSnr = ss.getEvdoSnr();
+            li.mLteSignalStrength = ss.getLteSignalStrength();
+            li.mLteRsrp = ss.getLteRsrp();
+            li.mLteRsrq = ss.getLteRsrq();
+            li.mLteRssnr = ss.getLteRssnr();
+            li.mLteCqi = ss.getLteCqi();
+        }
+
+        if (VDBG) {
+            Slog.d(TAG, "Returning LinkInfo with"
+                    + " MobileNetworkType = " + String.valueOf(li.mMobileNetworkType)
+                    + " Theoretical Rx BW = " + String.valueOf(li.mTheoreticalRxBandwidth)
+                    + " gsm Signal Strength = " + String.valueOf(li.mRssi)
+                    + " cdma Signal Strength = " + String.valueOf(li.mCdmaDbm)
+                    + " evdo Signal Strength = " + String.valueOf(li.mEvdoDbm)
+                    + " Lte Signal Strength = " + String.valueOf(li.mLteSignalStrength));
+        }
+
+        return li;
+    }
+
+    static class NetworkDataEntry {
+        public int networkType;
+        public int downloadBandwidth;               // in kbps
+        public int uploadBandwidth;                 // in kbps
+        public int latency;                         // in millisecond
+
+        NetworkDataEntry(int i1, int i2, int i3, int i4) {
+            networkType = i1;
+            downloadBandwidth = i2;
+            uploadBandwidth = i3;
+            latency = i4;
+        }
+    }
+
+    private static NetworkDataEntry [] mTheoreticalBWTable = new NetworkDataEntry[] {
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EDGE,     237,   118, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_GPRS,      48,    40, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_UMTS,     384,    64, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSDPA,  14400,    -1, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSUPA,  14400,  5760, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPA,   14400,  5760, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPAP,  21000,  5760, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_CDMA,      -1,    -1, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_1xRTT,     -1,    -1, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_0,  2468,   153, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_A,  3072,  1800, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_B, 14700,  1800, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_IDEN,      -1,    -1, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_LTE,   100000, 50000, -1),
+            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EHRPD,     -1,    -1, -1),
+    };
+
+    private static NetworkDataEntry getNetworkDataEntry(int networkType) {
+        for (NetworkDataEntry entry : mTheoreticalBWTable) {
+            if (entry.networkType == networkType) {
+                return entry;
+            }
+        }
+
+        Slog.e(TAG, "Could not find Theoretical BW entry for " + String.valueOf(networkType));
+        return null;
+    }
+
+    private static int getNormalizedSignalStrength(int networkType, SignalStrength ss) {
+
+        int level;
+
+        switch(networkType) {
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+                level = ss.getGsmLevel();
+                break;
+            case TelephonyManager.NETWORK_TYPE_CDMA:
+            case TelephonyManager.NETWORK_TYPE_1xRTT:
+                level = ss.getCdmaLevel();
+                break;
+            case TelephonyManager.NETWORK_TYPE_EVDO_0:
+            case TelephonyManager.NETWORK_TYPE_EVDO_A:
+            case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                level = ss.getEvdoLevel();
+                break;
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                level = ss.getLteLevel();
+                break;
+            case TelephonyManager.NETWORK_TYPE_IDEN:
+            case TelephonyManager.NETWORK_TYPE_EHRPD:
+            default:
+                return LinkInfo.UNKNOWN;
+        }
+
+        return (level * LinkInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE) /
+                SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+    }
+
+    @Override
+    public void startSampling(SamplingDataTracker.SamplingSnapshot s) {
+        mSamplingDataTracker.startSampling(s);
+    }
+
+    @Override
+    public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
+        mSamplingDataTracker.stopSampling(s);
+    }
 }
diff --git a/core/java/android/net/MobileLinkInfo.java b/core/java/android/net/MobileLinkInfo.java
new file mode 100644 (file)
index 0000000..2d18275
--- /dev/null
@@ -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;
+    }
+}
index 9ed7533..a3d7b14 100644 (file)
@@ -83,7 +83,6 @@ public interface NetworkStateTracker {
      */
     public static final int EVENT_NETWORK_DISCONNECTED = BASE_NETWORK_STATE_TRACKER + 5;
 
-
     /**
      * -------------------------------------------------------------
      * Control Interface
@@ -120,6 +119,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 (file)
index 0000000..b5dc140
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+
+import android.os.SystemClock;
+import android.util.Slog;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * @hide
+ */
+public class SamplingDataTracker
+{
+    private static final boolean DBG = false;
+    private static final String  TAG = "SamplingDataTracker";
+
+    public static class SamplingSnapshot
+    {
+        public int  mTxByteCount;
+        public int  mRxByteCount;
+        public int  mTxPacketCount;
+        public int  mRxPacketCount;
+        public int  mTxPacketErrorCount;
+        public int  mRxPacketErrorCount;
+        public long mTimestamp;
+    }
+
+    public static void getSamplingSnapshots(Map<String, SamplingSnapshot> mapIfaceToSample) {
+
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader("/proc/net/dev"));
+
+            // Skip over the line bearing column titles (there are 2 lines)
+            String line;
+            reader.readLine();
+            reader.readLine();
+
+            while ((line = reader.readLine()) != null) {
+
+                // remove leading whitespace
+                line = line.trim();
+
+                String[] tokens = line.split("[ ]+");
+                if (tokens.length < 17) {
+                    continue;
+                }
+
+                /* column format is
+                 * Interface  (Recv)bytes packets errs drop fifo frame compressed multicast \
+                 *            (Transmit)bytes packets errs drop fifo colls carrier compress
+                */
+
+                String currentIface = tokens[0].split(":")[0];
+                if (DBG) Slog.d(TAG, "Found data for interface " + currentIface);
+                if (mapIfaceToSample.containsKey(currentIface)) {
+
+                    SamplingSnapshot ss = new SamplingSnapshot();
+
+                    ss.mTxByteCount        = Integer.parseInt(tokens[1]);
+                    ss.mTxPacketCount      = Integer.parseInt(tokens[2]);
+                    ss.mTxPacketErrorCount = Integer.parseInt(tokens[3]);
+                    ss.mRxByteCount        = Integer.parseInt(tokens[9]);
+                    ss.mRxPacketCount      = Integer.parseInt(tokens[10]);
+                    ss.mRxPacketErrorCount = Integer.parseInt(tokens[11]);
+
+                    ss.mTimestamp          = SystemClock.elapsedRealtime();
+
+                    if (DBG) {
+                        Slog.d(TAG, "Interface = " + currentIface);
+                        Slog.d(TAG, "ByteCount = " + String.valueOf(ss.mTxByteCount));
+                        Slog.d(TAG, "TxPacketCount = " + String.valueOf(ss.mTxPacketCount));
+                        Slog.d(TAG, "TxPacketErrorCount = "
+                                + String.valueOf(ss.mTxPacketErrorCount));
+                        Slog.d(TAG, "RxByteCount = " + String.valueOf(ss.mRxByteCount));
+                        Slog.d(TAG, "RxPacketCount = " + String.valueOf(ss.mRxPacketCount));
+                        Slog.d(TAG, "RxPacketErrorCount = "
+                                + String.valueOf(ss.mRxPacketErrorCount));
+                        Slog.d(TAG, "Timestamp = " + String.valueOf(ss.mTimestamp));
+                        Slog.d(TAG, "---------------------------");
+                    }
+
+                    mapIfaceToSample.put(currentIface, ss);
+                }
+            }
+
+            if (DBG) {
+                Iterator it = mapIfaceToSample.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry kvpair = (Map.Entry)it.next();
+                    if (kvpair.getValue() == null) {
+                        Slog.d(TAG, "could not find snapshot for interface " + kvpair.getKey());
+                    }
+                }
+            }
+        } catch(FileNotFoundException e) {
+            Slog.e(TAG, "could not find /proc/net/dev");
+        } catch (IOException e) {
+            Slog.e(TAG, "could not read /proc/net/dev");
+        } finally {
+            try {
+                if (reader != null) {
+                    reader.close();
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "could not close /proc/net/dev");
+            }
+        }
+    }
+
+    // Snapshots from previous sampling interval
+    private SamplingSnapshot mBeginningSample;
+    private SamplingSnapshot mEndingSample;
+
+    // Starting snapshot of current interval
+    private SamplingSnapshot mLastSample;
+
+    // Protects sampling data from concurrent access
+    public final Object mSamplingDataLock = new Object();
+
+    // We need long enough time for a good sample
+    private final int MINIMUM_SAMPLING_INTERVAL = 15 * 1000;
+
+    // statistics is useless unless we have enough data
+    private final int MINIMUM_SAMPLED_PACKETS   = 30;
+
+    public void startSampling(SamplingSnapshot s) {
+        synchronized(mSamplingDataLock) {
+            mLastSample = s;
+        }
+    }
+
+    public void stopSampling(SamplingSnapshot s) {
+        synchronized(mSamplingDataLock) {
+            if (mLastSample != null) {
+                if (s.mTimestamp - mLastSample.mTimestamp > MINIMUM_SAMPLING_INTERVAL
+                        && getSampledPacketCount(mLastSample, s) > MINIMUM_SAMPLED_PACKETS) {
+                    mBeginningSample = mLastSample;
+                    mEndingSample = s;
+                    mLastSample = null;
+                } else {
+                    if (DBG) Slog.d(TAG, "Throwing current sample away because it is too small");
+                }
+            }
+        }
+    }
+
+    public void resetSamplingData() {
+        if (DBG) Slog.d(TAG, "Resetting sampled network data");
+        synchronized(mSamplingDataLock) {
+
+            // We could just take another sample here and treat it as an
+            // 'ending sample' effectively shortening sampling interval, but that
+            // requires extra work (specifically, reading the sample needs to be
+            // done asynchronously)
+
+            mLastSample = null;
+        }
+    }
+
+    public int getSampledTxByteCount() {
+        synchronized(mSamplingDataLock) {
+            if (mBeginningSample != null && mEndingSample != null) {
+                return mEndingSample.mTxByteCount - mBeginningSample.mTxByteCount;
+            } else {
+                return LinkInfo.UNKNOWN;
+            }
+        }
+    }
+
+    public int getSampledTxPacketCount() {
+        synchronized(mSamplingDataLock) {
+            if (mBeginningSample != null && mEndingSample != null) {
+                return mEndingSample.mTxPacketCount - mBeginningSample.mTxPacketCount;
+            } else {
+                return LinkInfo.UNKNOWN;
+            }
+        }
+    }
+
+    public int getSampledTxPacketErrorCount() {
+        synchronized(mSamplingDataLock) {
+            if (mBeginningSample != null && mEndingSample != null) {
+                return mEndingSample.mTxPacketErrorCount - mBeginningSample.mTxPacketErrorCount;
+            } else {
+                return LinkInfo.UNKNOWN;
+            }
+        }
+    }
+
+    public int getSampledRxByteCount() {
+        synchronized(mSamplingDataLock) {
+            if (mBeginningSample != null && mEndingSample != null) {
+                return mEndingSample.mRxByteCount - mBeginningSample.mRxByteCount;
+            } else {
+                return LinkInfo.UNKNOWN;
+            }
+        }
+    }
+
+    public int getSampledRxPacketCount() {
+        synchronized(mSamplingDataLock) {
+            if (mBeginningSample != null && mEndingSample != null) {
+                return mEndingSample.mRxPacketCount - mBeginningSample.mRxPacketCount;
+            } else {
+                return LinkInfo.UNKNOWN;
+            }
+        }
+    }
+
+    public int getSampledPacketCount() {
+        return getSampledPacketCount(mBeginningSample, mEndingSample);
+    }
+
+    public int getSampledPacketCount(SamplingSnapshot begin, SamplingSnapshot end) {
+        if (begin != null && end != null) {
+            int rxPacketCount = end.mRxPacketCount - begin.mRxPacketCount;
+            int txPacketCount = end.mTxPacketCount - begin.mTxPacketCount;
+            return rxPacketCount + txPacketCount;
+        } else {
+            return LinkInfo.UNKNOWN;
+        }
+    }
+
+    public int getSampledPacketErrorCount() {
+        if (mBeginningSample != null && mEndingSample != null) {
+            int rxPacketErrorCount = getSampledRxPacketErrorCount();
+            int txPacketErrorCount = getSampledTxPacketErrorCount();
+            return rxPacketErrorCount + txPacketErrorCount;
+        } else {
+            return LinkInfo.UNKNOWN;
+        }
+    }
+
+    public int getSampledRxPacketErrorCount() {
+        synchronized(mSamplingDataLock) {
+            if (mBeginningSample != null && mEndingSample != null) {
+                return mEndingSample.mRxPacketErrorCount - mBeginningSample.mRxPacketErrorCount;
+            } else {
+                return LinkInfo.UNKNOWN;
+            }
+        }
+    }
+
+    public long getSampleTimestamp() {
+        synchronized(mSamplingDataLock) {
+            if (mEndingSample != null) {
+                return mEndingSample.mTimestamp;
+            } else {
+                return LinkInfo.UNKNOWN;
+            }
+        }
+    }
+
+    public int getSampleDuration() {
+        synchronized(mSamplingDataLock) {
+            if (mBeginningSample != null && mEndingSample != null) {
+                return (int) (mEndingSample.mTimestamp - mBeginningSample.mTimestamp);
+            } else {
+                return LinkInfo.UNKNOWN;
+            }
+        }
+    }
+
+    public void setCommonLinkInfoFields(LinkInfo li) {
+        synchronized(mSamplingDataLock) {
+            li.mLastDataSampleTime = getSampleTimestamp();
+            li.mDataSampleDuration = getSampleDuration();
+            li.mPacketCount = getSampledPacketCount();
+            li.mPacketErrorCount = getSampledPacketErrorCount();
+        }
+    }
+}
+
diff --git a/core/java/android/net/WifiLinkInfo.java b/core/java/android/net/WifiLinkInfo.java
new file mode 100644 (file)
index 0000000..f3b0032
--- /dev/null
@@ -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;
+    }
+}
index 7539a4b..99176b2 100644 (file)
@@ -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.
index 2c8bb5d..5eb90f1 100644 (file)
     <protected-broadcast android:name="android.net.conn.TETHER_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" />
     <protected-broadcast android:name="android.net.conn.NETWORK_CONDITIONS_MEASURED" />
+    <protected-brodcast
+            android:name="android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED" />
     <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE" />
     <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE" />
     <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
index 07a7fba..ec6b063 100644 (file)
@@ -31,6 +31,7 @@ import static android.net.ConnectivityManager.isNetworkTypeValid;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
+import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -56,6 +57,7 @@ import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.LinkInfo;
 import android.net.LinkProperties.CompareResult;
 import android.net.MobileDataStateTracker;
 import android.net.NetworkConfig;
@@ -68,6 +70,7 @@ import android.net.NetworkUtils;
 import android.net.Proxy;
 import android.net.ProxyProperties;
 import android.net.RouteInfo;
+import android.net.SamplingDataTracker;
 import android.net.Uri;
 import android.net.wifi.WifiStateTracker;
 import android.net.wimax.WimaxManagerConstants;
@@ -144,8 +147,10 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.GregorianCalendar;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -174,6 +179,23 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     private static final String FAIL_FAST_TIME_MS =
             "persist.radio.fail_fast_time_ms";
 
+    private static final String ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED =
+            "android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED";
+
+    private static final int SAMPLE_INTERVAL_ELAPSED_REQURST_CODE = 0;
+
+    private PendingIntent mSampleIntervalElapsedIntent;
+
+    // Set network sampling interval at 12 minutes, this way, even if the timers get
+    // aggregated, it will fire at around 15 minutes, which should allow us to
+    // aggregate this timer with other timers (specially the socket keep alive timers)
+    private static final int DEFAULT_SAMPLING_INTERVAL_IN_SECONDS = (VDBG ? 30 : 12 * 60);
+
+    // start network sampling a minute after booting ...
+    private static final int DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS = (VDBG ? 30 : 60);
+
+    AlarmManager mAlarmManager;
+
     // used in recursive route setting to add gateways for the host for which
     // a host route was requested.
     private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
@@ -326,6 +348,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
      */
     private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14;
 
+    /**
+     * user internally to indicate that data sampling interval is up
+     */
+    private static final int EVENT_SAMPLE_INTERVAL_ELAPSED = 15;
+
     /** Handler used for internal events. */
     private InternalHandler mHandler;
     /** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -392,6 +419,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     List mProtectedNetworks;
 
     private DataConnectionStats mDataConnectionStats;
+
     private AtomicInteger mEnableFailFastMobileDataTag = new AtomicInteger(0);
 
     TelephonyManager mTelephonyManager;
@@ -634,6 +662,29 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         mDataConnectionStats = new DataConnectionStats(mContext);
         mDataConnectionStats.startMonitoring();
 
+        // start network sampling ..
+        Intent intent = new Intent(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED, null);
+        mSampleIntervalElapsedIntent = PendingIntent.getBroadcast(mContext,
+                SAMPLE_INTERVAL_ELAPSED_REQURST_CODE, intent, 0);
+
+        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+        setAlarm(DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS * 1000, mSampleIntervalElapsedIntent);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED);
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        String action = intent.getAction();
+                        if (action.equals(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED)) {
+                            mHandler.sendMessage(mHandler.obtainMessage
+                                    (EVENT_SAMPLE_INTERVAL_ELAPSED));
+                        }
+                    }
+                },
+                new IntentFilter(filter));
+
         mPacManager = new PacManager(mContext);
     }
 
@@ -1911,6 +1962,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
 //            if (mActiveDefaultNetwork != -1) {
 //                currentPriority = mNetConfigs[mActiveDefaultNetwork].mPriority;
 //            }
+
             for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
                 if (checkType == prevNetType) continue;
                 if (mNetConfigs[checkType] == null) continue;
@@ -1925,6 +1977,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
 // optimization should work and we need to investigate why it doesn't work.
 // This could be related to how DEACTIVATE_DATA_CALL is reporting its
 // complete before it is really complete.
+
 //                if (!mNetTrackers[checkType].isAvailable()) continue;
 
 //                if (currentPriority >= mNetConfigs[checkType].mPriority) continue;
@@ -2520,12 +2573,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     }
 
 
-   /**
+    /**
      * Reads the network specific TCP buffer sizes from SystemProperties
      * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
      * wide use
      */
-   private void updateNetworkSettings(NetworkStateTracker nt) {
+    private void updateNetworkSettings(NetworkStateTracker nt) {
         String key = nt.getTcpBufferSizesPropName();
         String bufferSizes = key == null ? null : SystemProperties.get(key);
 
@@ -2547,7 +2600,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         }
     }
 
-   /**
+    /**
      * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max]
      * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem
      *
@@ -2956,6 +3009,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                                 + " != tag:" + tag);
                     }
                 }
+                case EVENT_SAMPLE_INTERVAL_ELAPSED:
+                    handleNetworkSamplingTimeout();
+                    break;
             }
         }
     }
@@ -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<LinkInfo> result = Lists.newArrayList();
+        for (NetworkStateTracker tracker : mNetTrackers) {
+            if (tracker != null) {
+                LinkInfo li = tracker.getLinkInfo();
+                if (li != null) {
+                    result.add(li);
+                }
+            }
+        }
+
+        return result.toArray(new LinkInfo[result.size()]);
+    }
+
+    /* Infrastructure for network sampling */
+
+    private void handleNetworkSamplingTimeout() {
+
+        log("Sampling interval elapsed, updating statistics ..");
+
+        // initialize list of interfaces ..
+        Map<String, SamplingDataTracker.SamplingSnapshot> mapIfaceToSample =
+                new HashMap<String, SamplingDataTracker.SamplingSnapshot>();
+        for (NetworkStateTracker tracker : mNetTrackers) {
+            if (tracker != null) {
+                String ifaceName = tracker.getNetworkInterfaceName();
+                if (ifaceName != null) {
+                    mapIfaceToSample.put(ifaceName, null);
+                }
+            }
+        }
+
+        // Read samples for all interfaces
+        SamplingDataTracker.getSamplingSnapshots(mapIfaceToSample);
+
+        // process samples for all networks
+        for (NetworkStateTracker tracker : mNetTrackers) {
+            if (tracker != null) {
+                String ifaceName = tracker.getNetworkInterfaceName();
+                SamplingDataTracker.SamplingSnapshot ss = mapIfaceToSample.get(ifaceName);
+                if (ss != null) {
+                    // end the previous sampling cycle
+                    tracker.stopSampling(ss);
+                    // start a new sampling cycle ..
+                    tracker.startSampling(ss);
+                }
+            }
+        }
+
+        log("Done.");
+
+        int samplingIntervalInSeconds = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
+                DEFAULT_SAMPLING_INTERVAL_IN_SECONDS);
+
+        if (DBG) log("Setting timer for " + String.valueOf(samplingIntervalInSeconds) + "seconds");
+
+        setAlarm(samplingIntervalInSeconds * 1000, mSampleIntervalElapsedIntent);
+    }
+
+    void setAlarm(int timeoutInMilliseconds, PendingIntent intent) {
+        long wakeupTime = SystemClock.elapsedRealtime() + timeoutInMilliseconds;
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, wakeupTime, intent);
+    }
 }
+
index 461dedb..62630d6 100644 (file)
@@ -20,11 +20,14 @@ import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.BaseNetworkStateTracker;
 import android.net.LinkCapabilities;
+import android.net.LinkInfo;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkStateTracker;
+import android.net.SamplingDataTracker;
+import android.net.WifiLinkInfo;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Messenger;
@@ -37,7 +40,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
  *
  * @hide
  */
-public class WifiStateTracker implements NetworkStateTracker {
+public class WifiStateTracker extends BaseNetworkStateTracker {
 
     private static final String NETWORKTYPE = "WIFI";
     private static final String TAG = "WifiStateTracker";
@@ -48,17 +51,17 @@ public class WifiStateTracker implements NetworkStateTracker {
     private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
     private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
 
-    private LinkProperties mLinkProperties;
-    private LinkCapabilities mLinkCapabilities;
-    private NetworkInfo mNetworkInfo;
     private NetworkInfo.State mLastState = NetworkInfo.State.UNKNOWN;
 
+    private WifiInfo mWifiInfo;
+
     /* For sending events to connectivity service handler */
     private Handler mCsHandler;
-    private Context mContext;
     private BroadcastReceiver mWifiStateReceiver;
     private WifiManager mWifiManager;
 
+    private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker();
+
     public WifiStateTracker(int netType, String networkName) {
         mNetworkInfo = new NetworkInfo(netType, 0, networkName, "");
         mLinkProperties = new LinkProperties();
@@ -174,6 +177,7 @@ public class WifiStateTracker implements NetworkStateTracker {
     /**
      * Fetch NetworkInfo for the network
      */
+    @Override
     public NetworkInfo getNetworkInfo() {
         return new NetworkInfo(mNetworkInfo);
     }
@@ -181,6 +185,7 @@ public class WifiStateTracker implements NetworkStateTracker {
     /**
      * Fetch LinkProperties for the network
      */
+    @Override
     public LinkProperties getLinkProperties() {
         return new LinkProperties(mLinkProperties);
     }
@@ -191,11 +196,48 @@ 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
      */
     public boolean isDefaultRouteSet() {
@@ -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);
+    }
 }
+