Bug 713687 - Part 2 - Network API Android backend: listen to network changes and notify the DOM. r=dougt
authorMounir Lamouri <mounir.lamouri@gmail.com>
Tue, 17 Jan 2012 19:40:39 +0100
changeset 84744 b92399819ec4dc53371f336283ed58c21884b9bd
parent 84743 e652d673382fae155a88ef779b2fa3b4e16421f6
child 84745 9ee757bbbcd0031fe56d1cf394e0e7d9bff9bcee
push id21873
push usermlamouri@mozilla.com
push dateWed, 18 Jan 2012 10:29:07 +0000
treeherdermozilla-central@7538f4d4697c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdougt
bugs713687
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 713687 - Part 2 - Network API Android backend: listen to network changes and notify the DOM. r=dougt
embedding/android/GeckoApp.java
embedding/android/GeckoEvent.java
embedding/android/GeckoNetworkManager.java
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoEvent.java
mobile/android/base/GeckoNetworkManager.java
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
widget/android/nsAppShell.cpp
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -83,16 +83,18 @@ abstract public class GeckoApp
     public static GeckoApp mAppContext;
     public static boolean mFullscreen = false;
     public static File sGREDir = null;
     static Thread mLibLoadThread = null;
     public Handler mMainHandler;
     private IntentFilter mConnectivityFilter;
     private BroadcastReceiver mConnectivityReceiver;
     private BroadcastReceiver mBatteryReceiver;
+    private IntentFilter mNetworkFilter;
+    private GeckoNetworkManager mNetworkReceiver;
 
     enum LaunchState {PreLaunch, Launching, WaitForDebugger,
                       Launched, GeckoRunning, GeckoExiting};
     private static LaunchState sLaunchState = LaunchState.PreLaunch;
     private static boolean sTryCatchAttached = false;
 
 
     static boolean checkLaunchState(LaunchState checkState) {
@@ -412,16 +414,21 @@ abstract public class GeckoApp
         batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
         mBatteryReceiver = new GeckoBatteryManager();
         registerReceiver(mBatteryReceiver, batteryFilter);
 
         if (SmsManager.getInstance() != null) {
             SmsManager.getInstance().init();
         }
 
+        mNetworkFilter = new IntentFilter();
+        mNetworkFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mNetworkReceiver = new GeckoNetworkManager();
+        mNetworkReceiver.initialize();
+
         if (!checkAndSetLaunchState(LaunchState.PreLaunch,
                                     LaunchState.Launching))
             return;
 
         checkAndLaunchUpdate();
         mLibLoadThread = new Thread(new Runnable() {
             public void run() {
                 // At some point while loading the gecko libs our default locale gets set
@@ -504,16 +511,17 @@ abstract public class GeckoApp
 
         // Whatever we do here should be fast, because we're blocking
         // the next activity from showing up until we finish.
 
         // onPause will be followed by either onResume or onStop.
         super.onPause();
 
         unregisterReceiver(mConnectivityReceiver);
+        unregisterReceiver(mNetworkReceiver);
     }
 
     @Override
     public void onResume()
     {
         Log.i(LOG_FILE_NAME, "resume");
         if (checkLaunchState(LaunchState.GeckoRunning))
             GeckoAppShell.onResume();
@@ -522,16 +530,18 @@ abstract public class GeckoApp
         super.onResume();
 
         // Just in case. Normally we start in onNewIntent
         if (checkLaunchState(LaunchState.PreLaunch) ||
             checkLaunchState(LaunchState.Launching))
             onNewIntent(getIntent());
 
         registerReceiver(mConnectivityReceiver, mConnectivityFilter);
+        mNetworkReceiver.resume();
+        registerReceiver(mNetworkReceiver, mNetworkFilter);
     }
 
     @Override
     public void onStop()
     {
         Log.i(LOG_FILE_NAME, "stop");
         // We're about to be stopped, potentially in preparation for
         // being destroyed.  We're killable after this point -- as I
@@ -577,16 +587,17 @@ abstract public class GeckoApp
 
         if (SmsManager.getInstance() != null) {
             SmsManager.getInstance().shutdown();
         }
 
         super.onDestroy();
 
         unregisterReceiver(mBatteryReceiver);
+        unregisterReceiver(mNetworkReceiver);
     }
 
     @Override
     public void onConfigurationChanged(android.content.res.Configuration newConfig)
     {
         Log.i(LOG_FILE_NAME, "configuration changed");
         // nothing, just ignore
         super.onConfigurationChanged(newConfig);
--- a/embedding/android/GeckoEvent.java
+++ b/embedding/android/GeckoEvent.java
@@ -70,16 +70,20 @@ public class GeckoEvent {
     public static final int ACTIVITY_SHUTDOWN = 11;
     public static final int LOAD_URI = 12;
     public static final int SURFACE_CREATED = 13;
     public static final int SURFACE_DESTROYED = 14;
     public static final int GECKO_EVENT_SYNC = 15;
     public static final int ACTIVITY_START = 17;
     public static final int SAVE_STATE = 18;
     public static final int BROADCAST = 19;
+    public static final int VIEWPORT = 20;
+    public static final int TILE_SIZE = 21;
+    public static final int VISTITED = 22;
+    public static final int NETWORK_CHANGED = 23;
 
     public static final int IME_COMPOSITION_END = 0;
     public static final int IME_COMPOSITION_BEGIN = 1;
     public static final int IME_SET_TEXT = 2;
     public static final int IME_GET_TEXT = 3;
     public static final int IME_DELETE_TEXT = 4;
     public static final int IME_SET_SELECTION = 5;
     public static final int IME_GET_SELECTION = 6;
@@ -107,16 +111,19 @@ public class GeckoEvent {
     public int mKeyCode, mUnicodeChar;
     public int mOffset, mCount;
     public String mCharacters, mCharactersExtra;
     public int mRangeType, mRangeStyles;
     public int mRangeForeColor, mRangeBackColor;
     public Location mLocation;
     public Address  mAddress;
 
+    public double mBandwidth;
+    public boolean mCanBeMetered;
+
     public int mNativeWindow;
 
     public GeckoEvent() {
         mType = NATIVE_POKE;
     }
 
     public GeckoEvent(int evType) {
         mType = evType;
@@ -230,9 +237,15 @@ public class GeckoEvent {
         mCharacters = subject;
         mCharactersExtra = data;
     }
 
     public GeckoEvent(String uri) {
         mType = LOAD_URI;
         mCharacters = uri;
     }
+
+    public GeckoEvent(double bandwidth, boolean canBeMetered) {
+        mType = NETWORK_CHANGED;
+        mBandwidth = bandwidth;
+        mCanBeMetered = canBeMetered;
+    }
 }
--- a/embedding/android/GeckoNetworkManager.java
+++ b/embedding/android/GeckoNetworkManager.java
@@ -36,17 +36,19 @@
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko;
 
 import java.lang.Math;
 
 import android.util.Log;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 
 import android.telephony.TelephonyManager;
 
 /*
  * A part of the work of GeckoNetworkManager is to give an estimation of the
@@ -87,77 +89,146 @@ import android.telephony.TelephonyManage
  * 3.75G (20 Mb/s)
  * int NETWORK_TYPE_HSPAP    Current network is HSPA+
  *
  * 3.9G (50 Mb/s)
  * int NETWORK_TYPE_LTE      Current network is LTE
  */
 
 public class GeckoNetworkManager
+  extends BroadcastReceiver
 {
   static private final double  kDefaultBandwidth    = -1.0;
   static private final boolean kDefaultCanBeMetered = false;
 
   static private final double  kMaxBandwidth = 20.0;
 
-  static private final double  kNetworkSpeed_2_G    = 15.0 / 1024.0;  // 15 kb/s
-  static private final double  kNetworkSpeed_2_5_G  = 60.0 / 1024.0;  // 60 kb/s
-  static private final double  kNetworkSpeed_2_75_G = 200.0 / 1024.0; // 200 kb/s
-  static private final double  kNetworkSpeed_3_G    = 300.0 / 1024.0; // 300 kb/s
-  static private final double  kNetworkSpeed_3_5_G  = 7.0;            // 7 Mb/s
-  static private final double  kNetworkSpeed_3_75_G = 20.0;           // 20 Mb/s
-  static private final double  kNetworkSpeed_3_9_G  = 50.0;           // 50 Mb/s
+  static private final double  kNetworkSpeedEthernet = 20.0;           // 20 Mb/s
+  static private final double  kNetworkSpeedWifi     = 20.0;           // 20 Mb/s
+  static private final double  kNetworkSpeedWiMax    = 40.0;           // 40 Mb/s
+  static private final double  kNetworkSpeed_2_G     = 15.0 / 1024.0;  // 15 kb/s
+  static private final double  kNetworkSpeed_2_5_G   = 60.0 / 1024.0;  // 60 kb/s
+  static private final double  kNetworkSpeed_2_75_G  = 200.0 / 1024.0; // 200 kb/s
+  static private final double  kNetworkSpeed_3_G     = 300.0 / 1024.0; // 300 kb/s
+  static private final double  kNetworkSpeed_3_5_G   = 7.0;            // 7 Mb/s
+  static private final double  kNetworkSpeed_3_75_G  = 20.0;           // 20 Mb/s
+  static private final double  kNetworkSpeed_3_9_G   = 50.0;           // 50 Mb/s
 
-  private enum MobileNetworkType {
+  private enum NetworkType {
+    NETWORK_NONE,
+    NETWORK_ETHERNET,
+    NETWORK_WIFI,
+    NETWORK_WIMAX,
     NETWORK_2_G,    // 2G
     NETWORK_2_5_G,  // 2.5G
     NETWORK_2_75_G, // 2.75G
     NETWORK_3_G,    // 3G
     NETWORK_3_5_G,  // 3.5G
     NETWORK_3_75_G, // 3.75G
     NETWORK_3_9_G,  // 3.9G
     NETWORK_UNKNOWN
   }
 
-  private static MobileNetworkType getMobileNetworkType() {
+  static private NetworkType sNetworkType = NetworkType.NETWORK_NONE;
+
+  @Override
+  public void onReceive(Context aContext, Intent aIntent) {
+    updateNetworkType();
+  }
+
+  public static void initialize() {
+    sNetworkType = getNetworkType();
+  }
+
+  public static void resume() {
+    updateNetworkType();
+  }
+
+  private static void updateNetworkType() {
+    NetworkType previousNetworkType = sNetworkType;
+    sNetworkType = getNetworkType();
+
+    if (sNetworkType == previousNetworkType) {
+      return;
+    }
+
+    GeckoAppShell.sendEventToGecko(new GeckoEvent(getNetworkSpeed(sNetworkType),
+                                                  isNetworkUsuallyMetered(sNetworkType)));
+  }
+
+  public static double[] getCurrentInformation() {
+    return new double[] { getNetworkSpeed(sNetworkType),
+                          isNetworkUsuallyMetered(sNetworkType) ? 1.0 : 0.0 };
+  }
+
+  private static NetworkType getNetworkType() {
+    ConnectivityManager cm =
+      (ConnectivityManager)GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+    if (cm.getActiveNetworkInfo() == null) {
+      return NetworkType.NETWORK_NONE;
+    }
+
+    switch (cm.getActiveNetworkInfo().getType()) {
+      case ConnectivityManager.TYPE_ETHERNET:
+        return NetworkType.NETWORK_ETHERNET;
+      case ConnectivityManager.TYPE_WIFI:
+        return NetworkType.NETWORK_WIFI;
+      case ConnectivityManager.TYPE_WIMAX:
+        return NetworkType.NETWORK_WIMAX;
+      case ConnectivityManager.TYPE_MOBILE:
+        break; // We will handle sub-types after the switch.
+      default:
+        Log.w("GeckoNetworkManager", "Ignoring the current network type.");
+        return NetworkType.NETWORK_UNKNOWN;
+    }
+
     TelephonyManager tm =
       (TelephonyManager)GeckoApp.mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
 
     switch (tm.getNetworkType()) {
       case TelephonyManager.NETWORK_TYPE_IDEN:
       case TelephonyManager.NETWORK_TYPE_CDMA:
-        return MobileNetworkType.NETWORK_2_G;
+        return NetworkType.NETWORK_2_G;
       case TelephonyManager.NETWORK_TYPE_GPRS:
       case TelephonyManager.NETWORK_TYPE_1xRTT:
-        return MobileNetworkType.NETWORK_2_5_G;
+        return NetworkType.NETWORK_2_5_G;
       case TelephonyManager.NETWORK_TYPE_EDGE:
-        return MobileNetworkType.NETWORK_2_75_G;
+        return NetworkType.NETWORK_2_75_G;
       case TelephonyManager.NETWORK_TYPE_UMTS:
       case TelephonyManager.NETWORK_TYPE_EVDO_0:
-        return MobileNetworkType.NETWORK_3_G;
+        return NetworkType.NETWORK_3_G;
       case TelephonyManager.NETWORK_TYPE_HSPA:
       case TelephonyManager.NETWORK_TYPE_HSDPA:
       case TelephonyManager.NETWORK_TYPE_HSUPA:
       case TelephonyManager.NETWORK_TYPE_EVDO_A:
       case TelephonyManager.NETWORK_TYPE_EVDO_B:
       case TelephonyManager.NETWORK_TYPE_EHRPD:
-        return MobileNetworkType.NETWORK_3_5_G;
+        return NetworkType.NETWORK_3_5_G;
       case TelephonyManager.NETWORK_TYPE_HSPAP:
-        return MobileNetworkType.NETWORK_3_75_G;
+        return NetworkType.NETWORK_3_75_G;
       case TelephonyManager.NETWORK_TYPE_LTE:
-        return MobileNetworkType.NETWORK_3_9_G;
+        return NetworkType.NETWORK_3_9_G;
       case TelephonyManager.NETWORK_TYPE_UNKNOWN:
       default:
         Log.w("GeckoNetworkManager", "Connected to an unknown mobile network!");
-        return MobileNetworkType.NETWORK_UNKNOWN;
+        return NetworkType.NETWORK_UNKNOWN;
     }
   }
 
-  public static double getMobileNetworkSpeed(MobileNetworkType aType) {
+  private static double getNetworkSpeed(NetworkType aType) {
     switch (aType) {
+      case NETWORK_NONE:
+        return 0.0;
+      case NETWORK_ETHERNET:
+        return kNetworkSpeedEthernet;
+      case NETWORK_WIFI:
+        return kNetworkSpeedWifi;
+      case NETWORK_WIMAX:
+        return kNetworkSpeedWiMax;
       case NETWORK_2_G:
         return kNetworkSpeed_2_G;
       case NETWORK_2_5_G:
         return kNetworkSpeed_2_5_G;
       case NETWORK_2_75_G:
         return kNetworkSpeed_2_75_G;
       case NETWORK_3_G:
         return kNetworkSpeed_3_G;
@@ -168,39 +239,30 @@ public class GeckoNetworkManager
       case NETWORK_3_9_G:
         return kNetworkSpeed_3_9_G;
       case NETWORK_UNKNOWN:
       default:
         return kDefaultBandwidth;
     }
   }
 
-  public static double[] getCurrentInformation() {
-    ConnectivityManager cm =
-      (ConnectivityManager)GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-
-    if (cm.getActiveNetworkInfo() == null) {
-      return new double[] { 0.0, 1.0 };
+  private static boolean isNetworkUsuallyMetered(NetworkType aType) {
+    switch (aType) {
+      case NETWORK_NONE:
+      case NETWORK_UNKNOWN:
+      case NETWORK_ETHERNET:
+      case NETWORK_WIFI:
+      case NETWORK_WIMAX:
+        return false;
+      case NETWORK_2_G:
+      case NETWORK_2_5_G:
+      case NETWORK_2_75_G:
+      case NETWORK_3_G:
+      case NETWORK_3_5_G:
+      case NETWORK_3_75_G:
+      case NETWORK_3_9_G:
+        return true;
+      default:
+        Log.e("GeckoNetworkManager", "Got an unexpected network type!");
+        return false;
     }
-
-    int type = cm.getActiveNetworkInfo().getType();
-    double bandwidth = kDefaultBandwidth;
-    boolean canBeMetered = kDefaultCanBeMetered;
-
-    switch (type) {
-      case ConnectivityManager.TYPE_ETHERNET:
-      case ConnectivityManager.TYPE_WIFI:
-      case ConnectivityManager.TYPE_WIMAX:
-        bandwidth = kMaxBandwidth;
-        canBeMetered = false;
-        break;
-      case ConnectivityManager.TYPE_MOBILE:
-        bandwidth = Math.min(getMobileNetworkSpeed(getMobileNetworkType()), kMaxBandwidth);
-        canBeMetered = true;
-        break;
-      default:
-        Log.w("GeckoNetworkManager", "Ignoring the current network type.");
-        break;
-    }
-
-    return new double[] { bandwidth, canBeMetered ? 1.0 : 0.0 };
   }
 }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -114,19 +114,21 @@ abstract public class GeckoApp
     private static GeckoThread sGeckoThread = null;
     public Handler mMainHandler;
     private File mProfileDir;
     private static boolean sIsGeckoReady = false;
     private static int mOrientation;
 
     private IntentFilter mConnectivityFilter;
     private IntentFilter mBatteryFilter;
+    private IntentFilter mNetworkFilter;
 
     private BroadcastReceiver mConnectivityReceiver;
     private BroadcastReceiver mBatteryReceiver;
+    private GeckoNetworkManager mNetworkReceiver;
 
     public static BrowserToolbar mBrowserToolbar;
     public static DoorHangerPopup mDoorHangerPopup;
     public static AutoCompletePopup mAutoCompletePopup;
     public Favicons mFavicons;
 
     private Geocoder mGeocoder;
     private Address  mLastGeoAddress;
@@ -1569,16 +1571,21 @@ abstract public class GeckoApp
         batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
         mBatteryReceiver = new GeckoBatteryManager();
         registerReceiver(mBatteryReceiver, batteryFilter);
 
         if (SmsManager.getInstance() != null) {
           SmsManager.getInstance().init();
         }
 
+        mNetworkFilter = new IntentFilter();
+        mNetworkFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mNetworkReceiver = new GeckoNetworkManager();
+        mNetworkReceiver.initialize();
+
         final GeckoApp self = this;
  
         GeckoAppShell.getHandler().postDelayed(new Runnable() {
             public void run() {
                 
                 Log.w(LOGTAG, "zerdatime " + new Date().getTime() + " - pre checkLaunchState");
 
                 /*
@@ -1699,16 +1706,17 @@ abstract public class GeckoApp
 
         // Whatever we do here should be fast, because we're blocking
         // the next activity from showing up until we finish.
 
         // onPause will be followed by either onResume or onStop.
         super.onPause();
 
         unregisterReceiver(mConnectivityReceiver);
+        unregisterReceiver(mNetworkReceiver);
     }
 
     @Override
     public void onResume()
     {
         Log.i(LOGTAG, "resume");
         if (checkLaunchState(LaunchState.GeckoRunning))
             GeckoAppShell.onResume();
@@ -1716,16 +1724,19 @@ abstract public class GeckoApp
         // Undo whatever we did in onPause.
         super.onResume();
 
         // Just in case. Normally we start in onNewIntent
         if (checkLaunchState(LaunchState.Launching))
             onNewIntent(getIntent());
 
         registerReceiver(mConnectivityReceiver, mConnectivityFilter);
+        mNetworkReceiver.resume();
+        registerReceiver(mNetworkReceiver, mNetworkFilter);
+
         if (mOwnActivityDepth > 0)
             mOwnActivityDepth--;
     }
 
     @Override
     public void onStop()
     {
         Log.i(LOGTAG, "stop");
@@ -1800,16 +1811,17 @@ abstract public class GeckoApp
 
         if (SmsManager.getInstance() != null) {
           SmsManager.getInstance().shutdown();
         }
 
         super.onDestroy();
 
         unregisterReceiver(mBatteryReceiver);
+        unregisterReceiver(mNetworkReceiver);
     }
 
     @Override
     public void onContentChanged() {
         super.onContentChanged();
         if (mAboutHomeContent != null)
             mAboutHomeContent.onActivityContentChanged(this);
     }
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -76,16 +76,17 @@ public class GeckoEvent {
     public static final int SURFACE_CREATED = 13;
     public static final int SURFACE_DESTROYED = 14;
     public static final int GECKO_EVENT_SYNC = 15;
     public static final int ACTIVITY_START = 17;
     public static final int BROADCAST = 19;
     public static final int VIEWPORT = 20;
     public static final int TILE_SIZE = 21;
     public static final int VISTITED = 22;
+    public static final int NETWORK_CHANGED = 23;
 
     public static final int IME_COMPOSITION_END = 0;
     public static final int IME_COMPOSITION_BEGIN = 1;
     public static final int IME_SET_TEXT = 2;
     public static final int IME_GET_TEXT = 3;
     public static final int IME_DELETE_TEXT = 4;
     public static final int IME_SET_SELECTION = 5;
     public static final int IME_GET_SELECTION = 6;
@@ -113,16 +114,19 @@ public class GeckoEvent {
     public int mKeyCode, mUnicodeChar;
     public int mOffset, mCount;
     public String mCharacters, mCharactersExtra;
     public int mRangeType, mRangeStyles;
     public int mRangeForeColor, mRangeBackColor;
     public Location mLocation;
     public Address  mAddress;
 
+    public double mBandwidth;
+    public boolean mCanBeMetered;
+
     public int mNativeWindow;
 
     public GeckoEvent() {
         mType = NATIVE_POKE;
     }
 
     public GeckoEvent(int evType) {
         mType = evType;
@@ -257,9 +261,15 @@ public class GeckoEvent {
         mType = LOAD_URI;
         mCharacters = uri;
     }
 
     public GeckoEvent(int type, String data) {
         mType = type;
         mCharacters = data;
     }
+
+    public GeckoEvent(double bandwidth, boolean canBeMetered) {
+        mType = NETWORK_CHANGED;
+        mBandwidth = bandwidth;
+        mCanBeMetered = canBeMetered;
+    }
 }
--- a/mobile/android/base/GeckoNetworkManager.java
+++ b/mobile/android/base/GeckoNetworkManager.java
@@ -36,17 +36,19 @@
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko;
 
 import java.lang.Math;
 
 import android.util.Log;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 
 import android.telephony.TelephonyManager;
 
 /*
  * A part of the work of GeckoNetworkManager is to give an estimation of the
@@ -87,77 +89,146 @@ import android.telephony.TelephonyManage
  * 3.75G (20 Mb/s)
  * int NETWORK_TYPE_HSPAP    Current network is HSPA+
  *
  * 3.9G (50 Mb/s)
  * int NETWORK_TYPE_LTE      Current network is LTE
  */
 
 public class GeckoNetworkManager
+  extends BroadcastReceiver
 {
   static private final double  kDefaultBandwidth    = -1.0;
   static private final boolean kDefaultCanBeMetered = false;
 
   static private final double  kMaxBandwidth = 20.0;
 
-  static private final double  kNetworkSpeed_2_G    = 15.0 / 1024.0;  // 15 kb/s
-  static private final double  kNetworkSpeed_2_5_G  = 60.0 / 1024.0;  // 60 kb/s
-  static private final double  kNetworkSpeed_2_75_G = 200.0 / 1024.0; // 200 kb/s
-  static private final double  kNetworkSpeed_3_G    = 300.0 / 1024.0; // 300 kb/s
-  static private final double  kNetworkSpeed_3_5_G  = 7.0;            // 7 Mb/s
-  static private final double  kNetworkSpeed_3_75_G = 20.0;           // 20 Mb/s
-  static private final double  kNetworkSpeed_3_9_G  = 50.0;           // 50 Mb/s
+  static private final double  kNetworkSpeedEthernet = 20.0;           // 20 Mb/s
+  static private final double  kNetworkSpeedWifi     = 20.0;           // 20 Mb/s
+  static private final double  kNetworkSpeedWiMax    = 40.0;           // 40 Mb/s
+  static private final double  kNetworkSpeed_2_G     = 15.0 / 1024.0;  // 15 kb/s
+  static private final double  kNetworkSpeed_2_5_G   = 60.0 / 1024.0;  // 60 kb/s
+  static private final double  kNetworkSpeed_2_75_G  = 200.0 / 1024.0; // 200 kb/s
+  static private final double  kNetworkSpeed_3_G     = 300.0 / 1024.0; // 300 kb/s
+  static private final double  kNetworkSpeed_3_5_G   = 7.0;            // 7 Mb/s
+  static private final double  kNetworkSpeed_3_75_G  = 20.0;           // 20 Mb/s
+  static private final double  kNetworkSpeed_3_9_G   = 50.0;           // 50 Mb/s
 
-  private enum MobileNetworkType {
+  private enum NetworkType {
+    NETWORK_NONE,
+    NETWORK_ETHERNET,
+    NETWORK_WIFI,
+    NETWORK_WIMAX,
     NETWORK_2_G,    // 2G
     NETWORK_2_5_G,  // 2.5G
     NETWORK_2_75_G, // 2.75G
     NETWORK_3_G,    // 3G
     NETWORK_3_5_G,  // 3.5G
     NETWORK_3_75_G, // 3.75G
     NETWORK_3_9_G,  // 3.9G
     NETWORK_UNKNOWN
   }
 
-  private static MobileNetworkType getMobileNetworkType() {
+  static private NetworkType sNetworkType = NetworkType.NETWORK_NONE;
+
+  @Override
+  public void onReceive(Context aContext, Intent aIntent) {
+    updateNetworkType();
+  }
+
+  public static void initialize() {
+    sNetworkType = getNetworkType();
+  }
+
+  public static void resume() {
+    updateNetworkType();
+  }
+
+  private static void updateNetworkType() {
+    NetworkType previousNetworkType = sNetworkType;
+    sNetworkType = getNetworkType();
+
+    if (sNetworkType == previousNetworkType) {
+      return;
+    }
+
+    GeckoAppShell.sendEventToGecko(new GeckoEvent(getNetworkSpeed(sNetworkType),
+                                                  isNetworkUsuallyMetered(sNetworkType)));
+  }
+
+  public static double[] getCurrentInformation() {
+    return new double[] { getNetworkSpeed(sNetworkType),
+                          isNetworkUsuallyMetered(sNetworkType) ? 1.0 : 0.0 };
+  }
+
+  private static NetworkType getNetworkType() {
+    ConnectivityManager cm =
+      (ConnectivityManager)GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+    if (cm.getActiveNetworkInfo() == null) {
+      return NetworkType.NETWORK_NONE;
+    }
+
+    switch (cm.getActiveNetworkInfo().getType()) {
+      case ConnectivityManager.TYPE_ETHERNET:
+        return NetworkType.NETWORK_ETHERNET;
+      case ConnectivityManager.TYPE_WIFI:
+        return NetworkType.NETWORK_WIFI;
+      case ConnectivityManager.TYPE_WIMAX:
+        return NetworkType.NETWORK_WIMAX;
+      case ConnectivityManager.TYPE_MOBILE:
+        break; // We will handle sub-types after the switch.
+      default:
+        Log.w("GeckoNetworkManager", "Ignoring the current network type.");
+        return NetworkType.NETWORK_UNKNOWN;
+    }
+
     TelephonyManager tm =
       (TelephonyManager)GeckoApp.mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
 
     switch (tm.getNetworkType()) {
       case TelephonyManager.NETWORK_TYPE_IDEN:
       case TelephonyManager.NETWORK_TYPE_CDMA:
-        return MobileNetworkType.NETWORK_2_G;
+        return NetworkType.NETWORK_2_G;
       case TelephonyManager.NETWORK_TYPE_GPRS:
       case TelephonyManager.NETWORK_TYPE_1xRTT:
-        return MobileNetworkType.NETWORK_2_5_G;
+        return NetworkType.NETWORK_2_5_G;
       case TelephonyManager.NETWORK_TYPE_EDGE:
-        return MobileNetworkType.NETWORK_2_75_G;
+        return NetworkType.NETWORK_2_75_G;
       case TelephonyManager.NETWORK_TYPE_UMTS:
       case TelephonyManager.NETWORK_TYPE_EVDO_0:
-        return MobileNetworkType.NETWORK_3_G;
+        return NetworkType.NETWORK_3_G;
       case TelephonyManager.NETWORK_TYPE_HSPA:
       case TelephonyManager.NETWORK_TYPE_HSDPA:
       case TelephonyManager.NETWORK_TYPE_HSUPA:
       case TelephonyManager.NETWORK_TYPE_EVDO_A:
       case TelephonyManager.NETWORK_TYPE_EVDO_B:
       case TelephonyManager.NETWORK_TYPE_EHRPD:
-        return MobileNetworkType.NETWORK_3_5_G;
+        return NetworkType.NETWORK_3_5_G;
       case TelephonyManager.NETWORK_TYPE_HSPAP:
-        return MobileNetworkType.NETWORK_3_75_G;
+        return NetworkType.NETWORK_3_75_G;
       case TelephonyManager.NETWORK_TYPE_LTE:
-        return MobileNetworkType.NETWORK_3_9_G;
+        return NetworkType.NETWORK_3_9_G;
       case TelephonyManager.NETWORK_TYPE_UNKNOWN:
       default:
         Log.w("GeckoNetworkManager", "Connected to an unknown mobile network!");
-        return MobileNetworkType.NETWORK_UNKNOWN;
+        return NetworkType.NETWORK_UNKNOWN;
     }
   }
 
-  public static double getMobileNetworkSpeed(MobileNetworkType aType) {
+  private static double getNetworkSpeed(NetworkType aType) {
     switch (aType) {
+      case NETWORK_NONE:
+        return 0.0;
+      case NETWORK_ETHERNET:
+        return kNetworkSpeedEthernet;
+      case NETWORK_WIFI:
+        return kNetworkSpeedWifi;
+      case NETWORK_WIMAX:
+        return kNetworkSpeedWiMax;
       case NETWORK_2_G:
         return kNetworkSpeed_2_G;
       case NETWORK_2_5_G:
         return kNetworkSpeed_2_5_G;
       case NETWORK_2_75_G:
         return kNetworkSpeed_2_75_G;
       case NETWORK_3_G:
         return kNetworkSpeed_3_G;
@@ -168,39 +239,30 @@ public class GeckoNetworkManager
       case NETWORK_3_9_G:
         return kNetworkSpeed_3_9_G;
       case NETWORK_UNKNOWN:
       default:
         return kDefaultBandwidth;
     }
   }
 
-  public static double[] getCurrentInformation() {
-    ConnectivityManager cm =
-      (ConnectivityManager)GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-
-    if (cm.getActiveNetworkInfo() == null) {
-      return new double[] { 0.0, 1.0 };
+  private static boolean isNetworkUsuallyMetered(NetworkType aType) {
+    switch (aType) {
+      case NETWORK_NONE:
+      case NETWORK_UNKNOWN:
+      case NETWORK_ETHERNET:
+      case NETWORK_WIFI:
+      case NETWORK_WIMAX:
+        return false;
+      case NETWORK_2_G:
+      case NETWORK_2_5_G:
+      case NETWORK_2_75_G:
+      case NETWORK_3_G:
+      case NETWORK_3_5_G:
+      case NETWORK_3_75_G:
+      case NETWORK_3_9_G:
+        return true;
+      default:
+        Log.e("GeckoNetworkManager", "Got an unexpected network type!");
+        return false;
     }
-
-    int type = cm.getActiveNetworkInfo().getType();
-    double bandwidth = kDefaultBandwidth;
-    boolean canBeMetered = kDefaultCanBeMetered;
-
-    switch (type) {
-      case ConnectivityManager.TYPE_ETHERNET:
-      case ConnectivityManager.TYPE_WIFI:
-      case ConnectivityManager.TYPE_WIMAX:
-        bandwidth = kMaxBandwidth;
-        canBeMetered = false;
-        break;
-      case ConnectivityManager.TYPE_MOBILE:
-        bandwidth = Math.min(getMobileNetworkSpeed(getMobileNetworkType()), kMaxBandwidth);
-        canBeMetered = true;
-        break;
-      default:
-        Log.w("GeckoNetworkManager", "Ignoring the current network type.");
-        break;
-    }
-
-    return new double[] { bandwidth, canBeMetered ? 1.0 : 0.0 };
   }
 }
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -64,16 +64,18 @@ jfieldID AndroidGeckoEvent::jUnicodeChar
 jfieldID AndroidGeckoEvent::jOffsetField = 0;
 jfieldID AndroidGeckoEvent::jCountField = 0;
 jfieldID AndroidGeckoEvent::jRangeTypeField = 0;
 jfieldID AndroidGeckoEvent::jRangeStylesField = 0;
 jfieldID AndroidGeckoEvent::jRangeForeColorField = 0;
 jfieldID AndroidGeckoEvent::jRangeBackColorField = 0;
 jfieldID AndroidGeckoEvent::jLocationField = 0;
 jfieldID AndroidGeckoEvent::jAddressField = 0;
+jfieldID AndroidGeckoEvent::jBandwidthField = 0;
+jfieldID AndroidGeckoEvent::jCanBeMeteredField = 0;
 
 jclass AndroidPoint::jPointClass = 0;
 jfieldID AndroidPoint::jXField = 0;
 jfieldID AndroidPoint::jYField = 0;
 
 jclass AndroidRect::jRectClass = 0;
 jfieldID AndroidRect::jBottomField = 0;
 jfieldID AndroidRect::jLeftField = 0;
@@ -172,16 +174,18 @@ AndroidGeckoEvent::InitGeckoEventClass(J
     jOffsetField = getField("mOffset", "I");
     jCountField = getField("mCount", "I");
     jRangeTypeField = getField("mRangeType", "I");
     jRangeStylesField = getField("mRangeStyles", "I");
     jRangeForeColorField = getField("mRangeForeColor", "I");
     jRangeBackColorField = getField("mRangeBackColor", "I");
     jLocationField = getField("mLocation", "Landroid/location/Location;");
     jAddressField = getField("mAddress", "Landroid/location/Address;");
+    jBandwidthField = getField("mBandwidth", "D");
+    jCanBeMeteredField = getField("mCanBeMetered", "Z");
 }
 
 void
 AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(JNIEnv *jEnv)
 {
 #ifndef MOZ_JAVA_COMPOSITOR
     initInit();
 
@@ -486,16 +490,22 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jo
 
         case VIEWPORT:
         case BROADCAST: {
             ReadCharactersField(jenv);
             ReadCharactersExtraField(jenv);
             break;
         }
 
+        case NETWORK_CHANGED: {
+            mBandwidth = jenv->GetDoubleField(jobj, jBandwidthField);
+            mCanBeMetered = jenv->GetBooleanField(jobj, jCanBeMeteredField);
+            break;
+        }
+
         default:
             break;
     }
 
 #ifndef DEBUG_ANDROID_EVENTS
     ALOG("AndroidGeckoEvent: %p : %d", (void*)jobj, mType);
 #endif
 }
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -450,16 +450,18 @@ public:
     int Offset() { return mOffset; }
     int Count() { return mCount; }
     int RangeType() { return mRangeType; }
     int RangeStyles() { return mRangeStyles; }
     int RangeForeColor() { return mRangeForeColor; }
     int RangeBackColor() { return mRangeBackColor; }
     nsGeoPosition* GeoPosition() { return mGeoPosition; }
     nsGeoPositionAddress* GeoAddress() { return mGeoAddress; }
+    double Bandwidth() { return mBandwidth; }
+    bool CanBeMetered() { return mCanBeMetered; }
 
 protected:
     int mAction;
     int mType;
     int64_t mTime;
     nsIntPoint mP0;
     nsIntPoint mP1;
     nsIntRect mRect;
@@ -468,16 +470,18 @@ protected:
     int mOffset, mCount;
     int mRangeType, mRangeStyles;
     int mRangeForeColor, mRangeBackColor;
     double mAlpha, mBeta, mGamma;
     double mX, mY, mZ;
     nsString mCharacters, mCharactersExtra;
     nsRefPtr<nsGeoPosition> mGeoPosition;
     nsRefPtr<nsGeoPositionAddress> mGeoAddress;
+    double mBandwidth;
+    bool mCanBeMetered;
 
     void ReadP0Field(JNIEnv *jenv);
     void ReadP1Field(JNIEnv *jenv);
     void ReadRectField(JNIEnv *jenv);
     void ReadCharactersField(JNIEnv *jenv);
     void ReadCharactersExtraField(JNIEnv *jenv);
 
     static jclass jGeckoEventClass;
@@ -505,16 +509,19 @@ protected:
     static jfieldID jUnicodeCharField;
     static jfieldID jRangeTypeField;
     static jfieldID jRangeStylesField;
     static jfieldID jRangeForeColorField;
     static jfieldID jRangeBackColorField;
     static jfieldID jLocationField;
     static jfieldID jAddressField;
 
+    static jfieldID jBandwidthField;
+    static jfieldID jCanBeMeteredField;
+
 public:
     enum {
         NATIVE_POKE = 0,
         KEY_EVENT = 1,
         MOTION_EVENT = 2,
         ORIENTATION_EVENT = 3,
         ACCELERATION_EVENT = 4,
         LOCATION_EVENT = 5,
@@ -529,16 +536,17 @@ public:
         SURFACE_DESTROYED = 14,
         GECKO_EVENT_SYNC = 15,
         FORCED_RESIZE = 16,
         ACTIVITY_START = 17,
         BROADCAST = 19,
         VIEWPORT = 20,
         TILE_SIZE = 21,
         VISITED = 22,
+        NETWORK_CHANGED = 23,
         dummy_java_enum_list_end
     };
 
     enum {
         IME_COMPOSITION_END = 0,
         IME_COMPOSITION_BEGIN = 1,
         IME_SET_TEXT = 2,
         IME_GET_TEXT = 3,
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -31,16 +31,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "mozilla/Hal.h"
 #include "nsAppShell.h"
 #include "nsWindow.h"
 #include "nsThreadUtils.h"
 #include "nsICommandLineRunner.h"
 #include "nsIObserverService.h"
 #include "nsIAppStartup.h"
 #include "nsIGeolocationProvider.h"
 
@@ -451,16 +452,22 @@ nsAppShell::ProcessNextNativeEvent(bool 
 
     case AndroidGeckoEvent::VISITED: {
 #ifdef MOZ_ANDROID_HISTORY
         nsAndroidHistory::NotifyURIVisited(nsString(curEvent->Characters()));
 #endif
         break;
     }
 
+    case AndroidGeckoEvent::NETWORK_CHANGED: {
+        hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->Bandwidth(),
+                                                         curEvent->CanBeMetered()));
+        break;
+    }
+
     default:
         nsWindow::OnGlobalAndroidEvent(curEvent);
     }
 
     EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type());
 
     return true;
 }