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)
1  2 
core/java/android/net/EthernetDataTracker.java
core/java/android/provider/Settings.java
core/res/AndroidManifest.xml
services/java/com/android/server/ConnectivityService.java

@@@ -27,8 -27,6 +27,8 @@@ import android.os.RemoteException
  import android.os.ServiceManager;
  import android.util.Log;
  
 +import com.android.server.net.BaseNetworkObserver;
 +
  import java.util.concurrent.atomic.AtomicBoolean;
  import java.util.concurrent.atomic.AtomicInteger;
  
@@@ -38,7 -36,7 +38,7 @@@
   * ConnectivityService.
   * @hide
   */
- public class EthernetDataTracker implements NetworkStateTracker {
+ public class EthernetDataTracker extends BaseNetworkStateTracker {
      private static final String NETWORKTYPE = "ETHERNET";
      private static final String TAG = "Ethernet";
  
      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 = "";
@@@ -64,7 -58,7 +60,7 @@@
  
      private INetworkManagementService mNMService;
  
 -    private static class InterfaceObserver extends INetworkManagementEventObserver.Stub {
 +    private static class InterfaceObserver extends BaseNetworkObserver {
          private EthernetDataTracker mTracker;
  
          InterfaceObserver(EthernetDataTracker tracker) {
              mTracker = tracker;
          }
  
 +        @Override
          public void interfaceStatusChanged(String iface, boolean up) {
              Log.d(TAG, "Interface status changed: " + iface + (up ? "up" : "down"));
          }
  
 +        @Override
          public void interfaceLinkStateChanged(String iface, boolean up) {
              if (mIface.equals(iface)) {
                  Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down"));
              }
          }
  
 +        @Override
          public void interfaceAdded(String iface) {
              mTracker.interfaceAdded(iface);
          }
  
 +        @Override
          public void interfaceRemoved(String iface) {
              mTracker.interfaceRemoved(iface);
          }
 -
 -        public void limitReached(String limitName, String iface) {
 -            // Ignored.
 -        }
 -
 -        public void interfaceClassDataActivityChanged(String label, boolean active) {
 -            // Ignored.
 -        }
      }
  
      private EthernetDataTracker() {
@@@ -3269,23 -3269,6 +3269,23 @@@ public final class Settings 
          public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
  
          /**
 +         * Location access disabled
 +         */
 +        public static final int LOCATION_MODE_OFF = 0;
 +        /**
 +         * Network Location Provider disabled, but GPS and other sensors enabled.
 +         */
 +        public static final int LOCATION_MODE_SENSORS_ONLY = 1;
 +        /**
 +         * Reduced power usage, such as limiting the number of GPS updates per hour.
 +         */
 +        public static final int LOCATION_MODE_BATTERY_SAVING = 2;
 +        /**
 +         * Best-effort location computation allowed.
 +         */
 +        public static final int LOCATION_MODE_HIGH_ACCURACY = 3;
 +
 +        /**
           * A flag containing settings used for biometric weak
           * @hide
           */
           * @param cr the content resolver to use
           * @param provider the location provider to query
           * @return true if the provider is enabled
 +         * @deprecated use {@link #getLocationMode(ContentResolver)}
           */
 +        @Deprecated
          public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
              return isLocationProviderEnabledForUser(cr, provider, UserHandle.myUserId());
          }
  
          /**
 -         * Helper method for determining if the location master switch is enabled.
 -         * @param cr the content resolver to use
 -         * @return true if the master switch is enabled
 -         * @hide
 -         */
 -        public static final boolean isLocationMasterSwitchEnabled(ContentResolver cr) {
 -            int uid = UserHandle.myUserId();
 -            synchronized (mLocationSettingsLock) {
 -                return isLocationProviderEnabledForUser(cr, LocationManager.NETWORK_PROVIDER, uid)
 -                        || isLocationProviderEnabledForUser(cr, LocationManager.GPS_PROVIDER, uid);
 -            }
 -        }
 -
 -        /**
           * Helper method for determining if a location provider is enabled.
           * @param cr the content resolver to use
           * @param provider the location provider to query
           * @param userId the userId to query
           * @return true if the provider is enabled
 +         * @deprecated use {@link #getLocationModeForUser(ContentResolver, int)}
           * @hide
           */
 +        @Deprecated
          public static final boolean isLocationProviderEnabledForUser(ContentResolver cr, String provider, int userId) {
              String allowedProviders = Settings.Secure.getStringForUser(cr,
                      LOCATION_PROVIDERS_ALLOWED, userId);
           * @param cr the content resolver to use
           * @param provider the location provider to enable or disable
           * @param enabled true if the provider should be enabled
 +         * @deprecated use {@link #setLocationMode(ContentResolver, int)}
           */
 +        @Deprecated
          public static final void setLocationProviderEnabled(ContentResolver cr,
                  String provider, boolean enabled) {
              setLocationProviderEnabledForUser(cr, provider, enabled, UserHandle.myUserId());
          }
  
          /**
 -         * Thread-safe method for enabling or disabling the location master switch.
 -         *
 -         * @param cr the content resolver to use
 -         * @param enabled true if master switch should be enabled
 -         * @hide
 -         */
 -        public static final void setLocationMasterSwitchEnabled(ContentResolver cr,
 -                boolean enabled) {
 -            int uid = UserHandle.myUserId();
 -            synchronized (mLocationSettingsLock) {
 -                setLocationProviderEnabledForUser(cr, LocationManager.GPS_PROVIDER, enabled, uid);
 -                setLocationProviderEnabledForUser(cr, LocationManager.NETWORK_PROVIDER, enabled,
 -                        uid);
 -            }
 -        }
 -
 -        /**
           * Thread-safe method for enabling or disabling a single location provider.
           * @param cr the content resolver to use
           * @param provider the location provider to enable or disable
           * @param enabled true if the provider should be enabled
           * @param userId the userId for which to enable/disable providers
 +         * @deprecated use {@link #setLocationModeForUser(ContentResolver, int, int)}
           * @hide
           */
 +        @Deprecated
          public static final void setLocationProviderEnabledForUser(ContentResolver cr,
                  String provider, boolean enabled, int userId) {
              synchronized (mLocationSettingsLock) {
                          userId);
              }
          }
 +
 +        /**
 +         * Thread-safe method for setting the location mode to one of
 +         * {@link #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY},
 +         * {@link #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}.
 +         *
 +         * @param cr the content resolver to use
 +         * @param mode such as {@link #LOCATION_MODE_HIGH_ACCURACY}
 +         * @param userId the userId for which to change mode
 +         *
 +         * @throws IllegalArgumentException if mode is not one of the supported values
 +         */
 +        public static final void setLocationModeForUser(ContentResolver cr, int mode, int userId) {
 +            synchronized (mLocationSettingsLock) {
 +                boolean gps = false;
 +                boolean network = false;
 +                switch (mode) {
 +                    case LOCATION_MODE_OFF:
 +                        break;
 +                    case LOCATION_MODE_SENSORS_ONLY:
 +                        gps = true;
 +                        break;
 +                    case LOCATION_MODE_BATTERY_SAVING:
 +                        network = true;
 +                        break;
 +                    case LOCATION_MODE_HIGH_ACCURACY:
 +                        gps = true;
 +                        network = true;
 +                        break;
 +                    default:
 +                        throw new IllegalArgumentException("Invalid location mode: " + mode);
 +                }
 +                Settings.Secure.setLocationProviderEnabledForUser(
 +                        cr, LocationManager.GPS_PROVIDER, gps, userId);
 +                Settings.Secure.setLocationProviderEnabledForUser(
 +                        cr, LocationManager.NETWORK_PROVIDER, network, userId);
 +            }
 +        }
 +
 +        /**
 +         * Thread-safe method for setting the location mode to one of
 +         * {@link #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY},
 +         * {@link #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}.
 +         *
 +         * @param cr the content resolver to use
 +         * @param mode such as {@link #LOCATION_MODE_HIGH_ACCURACY}
 +         *
 +         * @throws IllegalArgumentException if mode is not one of the supported values
 +         */
 +        public static final void setLocationMode(ContentResolver cr, int mode) {
 +            setLocationModeForUser(cr, mode, UserHandle.myUserId());
 +        }
 +
 +        /**
 +         * Thread-safe method for reading the location mode, returns one of
 +         * {@link #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY},
 +         * {@link #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}.
 +         *
 +         * @param cr the content resolver to use
 +         * @param userId the userId for which to read the mode
 +         * @return the location mode
 +         */
 +        public static final int getLocationModeForUser(ContentResolver cr, int userId) {
 +            synchronized (mLocationSettingsLock) {
 +                boolean gpsEnabled = Settings.Secure.isLocationProviderEnabledForUser(
 +                        cr, LocationManager.GPS_PROVIDER, userId);
 +                boolean networkEnabled = Settings.Secure.isLocationProviderEnabledForUser(
 +                        cr, LocationManager.NETWORK_PROVIDER, userId);
 +                if (gpsEnabled && networkEnabled) {
 +                    return LOCATION_MODE_HIGH_ACCURACY;
 +                } else if (gpsEnabled) {
 +                    return LOCATION_MODE_SENSORS_ONLY;
 +                } else if (networkEnabled) {
 +                    return LOCATION_MODE_BATTERY_SAVING;
 +                } else {
 +                    return LOCATION_MODE_OFF;
 +                }
 +            }
 +        }
 +
 +        /**
 +         * Thread-safe method for reading the location mode, returns one of
 +         * {@link #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY},
 +         * {@link #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}.
 +         *
 +         * @param cr the content resolver to use
 +         * @return the location mode
 +         */
 +        public static final int getLocationMode(ContentResolver cr) {
 +            return getLocationModeForUser(cr, UserHandle.myUserId());
 +        }
      }
  
      /**
           */
          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.
      <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
      <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_CANCEL" />
      <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REPLY" />
 +    <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL" />
 +    <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST" />
 +    <protected-broadcast android:name="android.bluetooth.devicepicker.action.LAUNCH" />
 +    <protected-broadcast android:name="android.bluetooth.devicepicker.action.DEVICE_SELECTED" />
      <protected-broadcast
          android:name="android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED" />
      <protected-broadcast
      <protected-broadcast
          android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
      <protected-broadcast
 +        android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
 +    <protected-broadcast
 +        android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
 +    <protected-broadcast
          android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
 +    <protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" />
 +    <protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" />
 +    <protected-broadcast android:name="android.btopp.intent.action.BT_OPP_HANDOVER_STARTED" />
 +    <protected-broadcast android:name="android.btopp.intent.action.TRANSFER_COMPLETE" />
 +    <protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" />
 +    <protected-broadcast android:name="android.btopp.intent.action.BT_OPP_TRANSFER_PROGRESS" />
 +    <protected-broadcast android:name="android.btopp.intent.action.LIST" />
 +    <protected-broadcast android:name="android.btopp.intent.action.OPEN_OUTBOUND" />
 +    <protected-broadcast android:name="android.btopp.intent.action.HIDE_COMPLETE" />
 +    <protected-broadcast android:name="android.btopp.intent.action.CONFIRM" />
 +    <protected-broadcast android:name="android.btopp.intent.action.HIDE" />
 +    <protected-broadcast android:name="android.btopp.intent.action.BT_OPP_TRANSFER_DONE" />
 +    <protected-broadcast android:name="android.btopp.intent.action.RETRY" />
 +    <protected-broadcast android:name="android.btopp.intent.action.OPEN" />
 +    <protected-broadcast android:name="android.btopp.intent.action.OPEN_INBOUND" />
 +    <protected-broadcast android:name="com.android.bluetooth.pbap.authchall" />
 +    <protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" />
 +    <protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" />
 +    <protected-broadcast android:name="com.android.bluetooth.pbap.authcancelled" />
  
      <protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
  
      <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" />
      <protected-broadcast android:name="android.location.GPS_ENABLED_CHANGE" />
      <protected-broadcast android:name="android.location.PROVIDERS_CHANGED" />
      <protected-broadcast android:name="android.location.GPS_FIX_CHANGE" />
 +    <protected-broadcast android:name="android.net.proxy.PAC_REFRESH" />
  
      <!-- ====================================== -->
      <!-- Permissions for things that cost money -->
          android:label="@string/permlab_anyCodecForPlayback"
          android:description="@string/permdesc_anyCodecForPlayback" />
  
 +    <!-- Allows an application to install and/or uninstall CA certificates on
 +         behalf of the user.
 +         @hide -->
 +    <permission android:name="android.permission.MANAGE_CA_CERTIFICATES"
 +        android:protectionLevel="signature|system"
 +        android:label="@string/permlab_manageCaCertificates"
 +        android:description="@string/permdesc_manageCaCertificates" />
 +
      <!-- ========================================= -->
      <!-- Permissions for special development tools -->
      <!-- ========================================= -->
@@@ -31,6 -31,7 +31,7 @@@ import static android.net.ConnectivityM
  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 +57,7 @@@ import android.net.INetworkPolicyManage
  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 +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 +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 +179,23 @@@ public class ConnectivityService extend
      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;
       */
      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. */
      List mProtectedNetworks;
  
      private DataConnectionStats mDataConnectionStats;
      private AtomicInteger mEnableFailFastMobileDataTag = new AtomicInteger(0);
  
      TelephonyManager mTelephonyManager;
          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);
      }
  
  //            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;
  // 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;
      }
  
  
-    /**
+     /**
       * 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);
  
          }
      }
  
-    /**
+     /**
       * 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
       *
                                  + " != tag:" + tag);
                      }
                  }
+                 case EVENT_SAMPLE_INTERVAL_ELAPSED:
+                     handleNetworkSamplingTimeout();
+                     break;
              }
          }
      }
                          // Get the type of addresses supported by this link
                          LinkProperties lp = mCs.getLinkProperties(
                                  ConnectivityManager.TYPE_MOBILE_HIPRI);
 -                        boolean linkHasIpv4 = hasIPv4Address(lp);
 -                        boolean linkHasIpv6 = hasIPv6Address(lp);
 +                        boolean linkHasIpv4 = lp.hasIPv4Address();
 +                        boolean linkHasIpv6 = lp.hasIPv6Address();
                          log("isMobileOk: linkHasIpv4=" + linkHasIpv4
                                  + " linkHasIpv6=" + linkHasIpv6);
  
              }
          }
  
 -        public boolean hasIPv4Address(LinkProperties lp) {
 -            return lp.hasIPv4Address();
 -        }
 -
 -        // Not implemented in LinkProperties, do it here.
 -        public boolean hasIPv6Address(LinkProperties lp) {
 -            for (LinkAddress address : lp.getLinkAddresses()) {
 -              if (address.getAddress() instanceof Inet6Address) {
 -                return true;
 -              }
 -            }
 -            return false;
 -        }
 -
          private void log(String s) {
              Slog.d(ConnectivityService.TAG, "[" + CHECKMP_TAG + "] " + s);
          }
              }
          }
      };
+     @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);
+     }
  }