Bug 667980: Expose network connection type to chrome. r=dougt
authorDoug Turner <dougt@dougt.org>
Sun, 10 Jul 2011 15:24:05 -0700
changeset 73395 49d539befa07ea32ff1194227ecf314fdf401d0a
parent 73394 f85107f85c644a57d77136c4d77c9a23a3c6c51a
child 73396 ed03d172ff0617554e89d00508f0bad778d3df9b
push id235
push userbzbarsky@mozilla.com
push dateTue, 27 Sep 2011 17:13:04 +0000
treeherdermozilla-beta@2d1e082d176a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdougt
bugs667980
milestone8.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 667980: Expose network connection type to chrome. r=dougt Add an attribute to nsINetworkLinkService that represents the connection type, and add code to the android back-end to expose the connection type and notify when it changes.
embedding/android/AndroidManifest.xml.in
embedding/android/GeckoApp.java
embedding/android/GeckoAppShell.java
embedding/android/GeckoConnectivityReceiver.java
embedding/android/GeckoPhoneStateListener.java
embedding/android/Makefile.in
netwerk/base/public/nsINetworkLinkService.idl
netwerk/system/android/nsAndroidNetworkLinkService.cpp
netwerk/system/mac/nsNetworkLinkService.mm
netwerk/system/maemo/nsMaemoNetworkLinkService.cpp
netwerk/system/qt/nsQtNetworkLinkService.cpp
netwerk/system/win32/nsNotifyAddrListener.cpp
other-licenses/android/APKOpen.cpp
toolkit/system/dbus/nsNetworkManagerListener.cpp
widget/src/android/AndroidBridge.cpp
widget/src/android/AndroidBridge.h
widget/src/android/AndroidJNI.cpp
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -8,16 +8,17 @@
       android:sharedUserId="@MOZ_ANDROID_SHARED_ID@">
     <uses-sdk android:minSdkVersion="5"
               android:targetSdkVersion="5"/>
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
 
     <uses-feature android:name="android.hardware.location" android:required="false"/>
     <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
     <uses-feature android:name="android.hardware.touchscreen"/>
 
     <application android:label="@MOZ_APP_DISPLAYNAME@"
 		 android:icon="@drawable/icon"
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -56,32 +56,34 @@ import android.content.res.*;
 import android.graphics.*;
 import android.widget.*;
 import android.hardware.*;
 
 import android.util.*;
 import android.net.*;
 import android.database.*;
 import android.provider.*;
+import android.telephony.*;
 
 abstract public class GeckoApp
     extends Activity
 {
     public static final String ACTION_ALERT_CLICK = "org.mozilla.gecko.ACTION_ALERT_CLICK";
     public static final String ACTION_ALERT_CLEAR = "org.mozilla.gecko.ACTION_ALERT_CLEAR";
 
     public static FrameLayout mainLayout;
     public static GeckoSurfaceView surfaceView;
     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 PhoneStateListener mPhoneStateListener;
 
     enum LaunchState {PreLaunch, Launching, WaitButton,
                       Launched, GeckoRunning, GeckoExiting};
     private static LaunchState sLaunchState = LaunchState.PreLaunch;
 
 
     static boolean checkLaunchState(LaunchState checkState) {
         synchronized(sLaunchState) {
@@ -227,16 +229,18 @@ abstract public class GeckoApp
         setContentView(mainLayout,
                        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                                                   ViewGroup.LayoutParams.FILL_PARENT));
 
         mConnectivityFilter = new IntentFilter();
         mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         mConnectivityReceiver = new GeckoConnectivityReceiver();
 
+        mPhoneStateListener = new GeckoPhoneStateListener();
+
         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
@@ -321,16 +325,20 @@ 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);
+
+        TelephonyManager tm = (TelephonyManager)
+            GeckoApp.mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
+        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
     }
 
     @Override
     public void onResume()
     {
         Log.i("GeckoApp", "resume");
         if (checkLaunchState(LaunchState.GeckoRunning))
             GeckoAppShell.onResume();
@@ -339,16 +347,23 @@ 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);
+
+        TelephonyManager tm = (TelephonyManager)
+            GeckoApp.mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
+        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
+
+        // Notify if network state changed since we paused
+        GeckoAppShell.onNetworkStateChange(true);
     }
 
     @Override
     public void onStop()
     {
         Log.i("GeckoApp", "stop");
         // We're about to be stopped, potentially in preparation for
         // being destroyed.  We're killable after this point -- as I
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -53,16 +53,17 @@ import android.view.*;
 import android.view.inputmethod.*;
 import android.content.*;
 import android.content.res.*;
 import android.content.pm.*;
 import android.graphics.*;
 import android.widget.*;
 import android.hardware.*;
 import android.location.*;
+import android.telephony.*;
 import android.webkit.MimeTypeMap;
 import android.media.MediaScannerConnection;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
 
 import android.util.*;
 import android.net.Uri;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
@@ -89,31 +90,35 @@ public class GeckoAppShell
     static private final int NOTIFY_IME_CANCELCOMPOSITION = 2;
     static private final int NOTIFY_IME_FOCUSCHANGE = 3;
 
     static public final long kFreeSpaceThreshold = 157286400L; // 150MB
     static private final long kLibFreeSpaceBuffer = 20971520L; // 29MB
     static private File sCacheFile = null;
     static private int sFreeSpace = -1;
 
+    static private String sNetworkState = "unknown";
+    static private String sNetworkType = "unknown";
+    static private int sNetworkTypeCode = 0;
+
     /* The Android-side API: API methods that Android calls */
 
     // Initialization methods
     public static native void nativeInit();
     public static native void nativeRun(String args);
 
     // helper methods
     public static native void setSurfaceView(GeckoSurfaceView sv);
     public static native void putenv(String map);
     public static native void onResume();
     public static native void onLowMemory();
     public static native void callObserver(String observerKey, String topic, String data);
     public static native void removeObserver(String observerKey);
     public static native void loadLibs(String apkName, boolean shouldExtract);
-    public static native void onChangeNetworkLinkStatus(String status);
+    public static native void onChangeNetworkLinkStatus(String status, String type);
     public static native void reportJavaCrash(String stack);
 
     // A looper thread, accessed by GeckoAppShell.getHandler
     private static class LooperThread extends Thread {
         public SynchronousQueue<Handler> mHandlerQueue =
             new SynchronousQueue<Handler>();
         
         public void run() {
@@ -649,16 +654,19 @@ public class GeckoAppShell
         }
     }
 
     static void onAppShellReady()
     {
         // mLaunchState can only be Launched at this point
         GeckoApp.setLaunchState(GeckoApp.LaunchState.GeckoRunning);
         sendPendingEventsToGecko();
+
+        // Refresh the network connectivity state
+        onNetworkStateChange(false);
     }
 
     static void onXreExit() {
         // mLaunchState can only be Launched or GeckoRunning at this point
         GeckoApp.setLaunchState(GeckoApp.LaunchState.GeckoExiting);
         Log.i("GeckoAppJava", "XRE exited");
         if (gRestartScheduled) {
             GeckoApp.mAppContext.doRestart();
@@ -1005,30 +1013,102 @@ public class GeckoAppShell
         GeckoApp.mAppContext.runOnUiThread(new Runnable() {
             public void run() {
                 GeckoApp.surfaceView.setKeepScreenOn(on);
             }
         });
     }
 
     public static boolean isNetworkLinkUp() {
-        ConnectivityManager cm = (ConnectivityManager)
-            GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo info = cm.getActiveNetworkInfo();
-        if (info == null || !info.isConnected())
+        if (sNetworkState == "up")
+            return true;
+        return false;
+    }
+
+    public static boolean isNetworkLinkKnown() {
+        if (sNetworkState == "unknown")
             return false;
         return true;
     }
 
-    public static boolean isNetworkLinkKnown() {
+    public static int getNetworkLinkType() {
+        return sNetworkTypeCode;
+    }
+
+    public static void onNetworkStateChange(boolean notifyChanged) {
+        String state;
+        String type;
+        int typeCode;
+
         ConnectivityManager cm = (ConnectivityManager)
             GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-        if (cm.getActiveNetworkInfo() == null)
-            return false;
-        return true;
+        NetworkInfo info = cm.getActiveNetworkInfo();
+
+        // Note, these strings and codes correspond to those specified in
+        // nsINetworkLinkService. Make sure to keep them in sync!
+        type = "unknown";
+        typeCode = 0;
+        if (info == null) {
+            state = "unknown";
+        } else if (!info.isConnected()) {
+            state = "down";
+        } else {
+            state = "up";
+
+            int androidType = info.getType();
+
+            if (androidType == ConnectivityManager.TYPE_WIFI) {
+                type = "wifi";
+                typeCode = 3;
+            } else if (androidType == ConnectivityManager.TYPE_WIMAX) {
+                type = "wimax";
+                typeCode = 4;
+            } else if (androidType == ConnectivityManager.TYPE_MOBILE) {
+                TelephonyManager tm = (TelephonyManager)
+                    GeckoApp.mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
+                typeCode = tm.getNetworkType();
+
+                // Note that the value of some of these constants are used due
+                // to not all of these existing in API level 8.
+                //
+                // In particular, EVDO_B appears at level 9, and EHRPD and LTE
+                // appear at level 11.
+                if (androidType == TelephonyManager.NETWORK_TYPE_GPRS ||
+                    androidType == TelephonyManager.NETWORK_TYPE_EDGE ||
+                    androidType == TelephonyManager.NETWORK_TYPE_CDMA ||
+                    androidType == TelephonyManager.NETWORK_TYPE_IDEN ||
+                    androidType == TelephonyManager.NETWORK_TYPE_1xRTT) {
+                    type = "2g";
+                    typeCode = 5;
+                } else if (androidType == TelephonyManager.NETWORK_TYPE_UMTS ||
+                           androidType == TelephonyManager.NETWORK_TYPE_HSDPA ||
+                           androidType == TelephonyManager.NETWORK_TYPE_HSUPA ||
+                           androidType == TelephonyManager.NETWORK_TYPE_HSPA ||
+                           androidType == TelephonyManager.NETWORK_TYPE_EVDO_0 ||
+                           androidType == TelephonyManager.NETWORK_TYPE_EVDO_A ||
+                           androidType == 12 || // TelephonyManager.NETWORK_TYPE_EVDO_B
+                           androidType == 14) { // TelephonyManager.NETWORK_TYPE_EHRPD
+                    type = "3g";
+                    typeCode = 6;
+                } else if (androidType == 13) { // TelephonyManager.NETWORK_TYPE_LTE
+                    type = "4g";
+                    typeCode = 7;
+                }
+            }
+        }
+
+        // If the network state has changed, notify Gecko
+        if (notifyChanged && (state != sNetworkState || typeCode != sNetworkTypeCode)) {
+            Log.i("GeckoAppShell", "Network state changed: (" + state + ", " + type + ") ");
+            sNetworkState = state;
+            sNetworkType = type;
+            sNetworkTypeCode = typeCode;
+            if (GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
+                onChangeNetworkLinkStatus(sNetworkState, sNetworkType);
+        }
     }
 
     public static void setSelectedLocale(String localeCode) {
         SharedPreferences settings =
             GeckoApp.mAppContext.getPreferences(Activity.MODE_PRIVATE);
         settings.edit().putString(GeckoApp.mAppContext.getPackageName() + ".locale",
                                   localeCode).commit();
         Locale locale;
--- a/embedding/android/GeckoConnectivityReceiver.java
+++ b/embedding/android/GeckoConnectivityReceiver.java
@@ -33,30 +33,17 @@
  * 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 ***** */
 
 package org.mozilla.gecko;
 
 import android.content.*;
-import android.net.*;
 
 public class GeckoConnectivityReceiver
     extends BroadcastReceiver
 {
     @Override
     public void onReceive(Context context, Intent intent) {
-        String status;
-        ConnectivityManager cm = (ConnectivityManager)
-            context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        NetworkInfo info = cm.getActiveNetworkInfo();
-        if (info == null)
-            status = "unknown";
-        else if (!info.isConnected())
-            status = "down";
-        else
-            status = "up";
-
-        if (GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
-            GeckoAppShell.onChangeNetworkLinkStatus(status);
+        GeckoAppShell.onNetworkStateChange(true);
     }
 }
new file mode 100644
--- /dev/null
+++ b/embedding/android/GeckoPhoneStateListener.java
@@ -0,0 +1,50 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Lord <chrislord.net@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * 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 ***** */
+
+package org.mozilla.gecko;
+
+import android.telephony.*;
+
+public class GeckoPhoneStateListener
+    extends PhoneStateListener
+{
+    @Override
+    public void onDataConnectionStateChanged(int state, int networkType) {
+        GeckoAppShell.onNetworkStateChange(true);
+    }
+}
+
--- a/embedding/android/Makefile.in
+++ b/embedding/android/Makefile.in
@@ -47,16 +47,17 @@ DIRS = locales
 
 JAVAFILES = \
   GeckoApp.java \
   GeckoAppShell.java \
   GeckoConnectivityReceiver.java \
   GeckoEvent.java \
   GeckoSurfaceView.java \
   GeckoInputConnection.java \
+  GeckoPhoneStateListener.java \
   AlertNotification.java \
   $(NULL)
 
 PROCESSEDJAVAFILES = \
   App.java \
   Restarter.java \
   NotificationHandler.java \
   LauncherShortcuts.java \
--- a/netwerk/base/public/nsINetworkLinkService.idl
+++ b/netwerk/base/public/nsINetworkLinkService.idl
@@ -37,19 +37,29 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 /**
  * Network link status monitoring service.
  */
-[scriptable, uuid(61618a52-ea91-4277-a4ab-ebe10d7b9a64)]
+[scriptable, uuid(f7d3be87-7403-4a1e-b89f-2797776e9b08)]
 interface nsINetworkLinkService : nsISupports
 {
+  /* Link type constants */
+  const unsigned long LINK_TYPE_UNKNOWN = 0;
+  const unsigned long LINK_TYPE_ETHERNET = 1;
+  const unsigned long LINK_TYPE_USB = 2;
+  const unsigned long LINK_TYPE_WIFI = 3;
+  const unsigned long LINK_TYPE_WIMAX = 4;
+  const unsigned long LINK_TYPE_2G = 5;
+  const unsigned long LINK_TYPE_3G = 6;
+  const unsigned long LINK_TYPE_4G = 7;
+
   /**
    * This is set to true when the system is believed to have a usable
    * network connection.
    *
    * The link is only up when network connections can be established. For
    * example, the link is down during DHCP configuration (unless there
    * is another usable interface already configured).
    *
@@ -57,16 +67,21 @@ interface nsINetworkLinkService : nsISup
    * it is up.
    */
   readonly attribute boolean isLinkUp;
   
   /**
    * This is set to true when we believe that isLinkUp is accurate.
    */
   readonly attribute boolean linkStatusKnown;
+
+  /**
+   * The type of network connection.
+   */
+  readonly attribute unsigned long linkType;
 };
 
 %{C++
 /**
  * We send notifications through nsIObserverService with topic
  * NS_NETWORK_LINK_TOPIC whenever one of isLinkUp or linkStatusKnown
  * changes. We pass one of the NS_NETWORK_LINK_DATA_ constants below
  * as the aData parameter of the notification.
@@ -80,9 +95,41 @@ interface nsINetworkLinkService : nsISup
 /**
  * isLinkUp is now false, linkStatusKnown is true.
  */
 #define NS_NETWORK_LINK_DATA_DOWN    "down"
 /**
  * linkStatusKnown is now false.
  */
 #define NS_NETWORK_LINK_DATA_UNKNOWN "unknown"
+
+/**
+ * We send notifications through nsIObserverService with topic
+ * NS_NETWORK_LINK_TYPE_TOPIC whenever the network connection type
+ * changes. We pass one of the valid connection type constants
+ * below as the aData parameter of the notification.
+ */
+#define NS_NETWORK_LINK_TYPE_TOPIC "network:link-type-changed"
+
+/** We were unable to determine the network connection type */
+#define NS_NETWORK_LINK_TYPE_UNKNOWN  "unknown"
+
+/** A standard wired ethernet connection */
+#define NS_NETWORK_LINK_TYPE_ETHERNET    "ethernet"
+
+/** A connection via a USB port */
+#define NS_NETWORK_LINK_TYPE_USB    "usb"
+
+/** A connection via a WiFi access point (IEEE802.11) */
+#define NS_NETWORK_LINK_TYPE_WIFI "wifi"
+
+/** A connection via WiMax (IEEE802.16) */
+#define NS_NETWORK_LINK_TYPE_WIMAX "wimax"
+
+/** A '2G' mobile connection (e.g. GSM, GPRS, EDGE) */
+#define NS_NETWORK_LINK_TYPE_2G "2g"
+
+/** A '3G' mobile connection (e.g. UMTS, CDMA) */
+#define NS_NETWORK_LINK_TYPE_3G "3g"
+
+/** A '4G' mobile connection (e.g. LTE, UMB) */
+#define NS_NETWORK_LINK_TYPE_4G "4g"
 %}
--- a/netwerk/system/android/nsAndroidNetworkLinkService.cpp
+++ b/netwerk/system/android/nsAndroidNetworkLinkService.cpp
@@ -65,8 +65,18 @@ nsAndroidNetworkLinkService::GetIsLinkUp
 NS_IMETHODIMP
 nsAndroidNetworkLinkService::GetLinkStatusKnown(PRBool *aIsKnown)
 {
   NS_ENSURE_TRUE(mozilla::AndroidBridge::Bridge(), NS_ERROR_NOT_IMPLEMENTED);
 
   *aIsKnown = mozilla::AndroidBridge::Bridge()->IsNetworkLinkKnown();
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsAndroidNetworkLinkService::GetLinkType(PRUint32 *aLinkType)
+{
+  NS_ENSURE_ARG_POINTER(aLinkType);
+  NS_ENSURE_TRUE(mozilla::AndroidBridge::Bridge(), NS_ERROR_UNEXPECTED);
+
+  *aLinkType = mozilla::AndroidBridge::Bridge()->GetNetworkLinkType();
+  return NS_OK;
+}
--- a/netwerk/system/mac/nsNetworkLinkService.mm
+++ b/netwerk/system/mac/nsNetworkLinkService.mm
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsNetworkLinkService.h"
 #include "nsCOMPtr.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
+#include "nsCRT.h"
 
 #import <Cocoa/Cocoa.h>
 #import <netinet/in.h>
 
 NS_IMPL_ISUPPORTS2(nsNetworkLinkService,
                    nsINetworkLinkService,
                    nsIObserver)
 
@@ -71,16 +72,26 @@ nsNetworkLinkService::GetIsLinkUp(PRBool
 NS_IMETHODIMP
 nsNetworkLinkService::GetLinkStatusKnown(PRBool *aIsUp)
 {
     *aIsUp = mStatusKnown;
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNetworkLinkService::GetLinkType(PRUint32 *aLinkType)
+{
+  NS_ENSURE_ARG_POINTER(aLinkType);
+
+  // XXX This function has not yet been implemented for this platform
+  *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsNetworkLinkService::Observe(nsISupports *subject,
                               const char *topic,
                               const PRUnichar *data)
 {
     if (!strcmp(topic, "xpcom-shutdown")) {
         Shutdown();
     }
 
--- a/netwerk/system/maemo/nsMaemoNetworkLinkService.cpp
+++ b/netwerk/system/maemo/nsMaemoNetworkLinkService.cpp
@@ -38,16 +38,17 @@
 
 #include "nsMaemoNetworkLinkService.h"
 #include "nsCOMPtr.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsMaemoNetworkManager.h"
 #include "mozilla/Services.h"
+#include "nsCRT.h"
 
 NS_IMPL_ISUPPORTS2(nsMaemoNetworkLinkService,
                    nsINetworkLinkService,
                    nsIObserver)
 
 nsMaemoNetworkLinkService::nsMaemoNetworkLinkService()
 {
 }
@@ -66,16 +67,26 @@ nsMaemoNetworkLinkService::GetIsLinkUp(P
 NS_IMETHODIMP
 nsMaemoNetworkLinkService::GetLinkStatusKnown(PRBool *aIsKnown)
 {
   *aIsKnown = nsMaemoNetworkManager::GetLinkStatusKnown();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsMaemoNetworkLinkService::GetLinkType(PRUint32 *aLinkType)
+{
+  NS_ENSURE_ARG_POINTER(aLinkType);
+
+  // XXX This function has not yet been implemented for this platform
+  *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsMaemoNetworkLinkService::Observe(nsISupports *aSubject,
                                    const char *aTopic,
                                    const PRUnichar *aData)
 {
   if (!strcmp(aTopic, "xpcom-shutdown"))
     Shutdown();
 
   return NS_OK;
--- a/netwerk/system/qt/nsQtNetworkLinkService.cpp
+++ b/netwerk/system/qt/nsQtNetworkLinkService.cpp
@@ -37,16 +37,17 @@
 
 #include "nsQtNetworkManager.h"
 #include "nsQtNetworkLinkService.h"
 #include "nsCOMPtr.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "mozilla/Services.h"
+#include "nsCRT.h"
 
 NS_IMPL_ISUPPORTS2(nsQtNetworkLinkService,
                    nsINetworkLinkService,
                    nsIObserver)
 
 nsQtNetworkLinkService::nsQtNetworkLinkService()
 {
 }
@@ -65,16 +66,26 @@ nsQtNetworkLinkService::GetIsLinkUp(PRBo
 NS_IMETHODIMP
 nsQtNetworkLinkService::GetLinkStatusKnown(PRBool* aIsKnown)
 {
   *aIsKnown = nsQtNetworkManager::get()->isOnline();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsQtNetworkLinkService::GetLinkType(PRUint32 *aLinkType)
+{
+  NS_ENSURE_ARG_POINTER(aLinkType);
+
+  // XXX This function has not yet been implemented for this platform
+  *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsQtNetworkLinkService::Observe(nsISupports* aSubject,
                                 const char* aTopic,
                                 const PRUnichar* aData)
 {
   if (!strcmp(aTopic, "xpcom-shutdown")) {
     Shutdown();
     nsQtNetworkManager::get()->destroy();
   }
--- a/netwerk/system/win32/nsNotifyAddrListener.cpp
+++ b/netwerk/system/win32/nsNotifyAddrListener.cpp
@@ -49,16 +49,17 @@
 #include "plstr.h"
 #include "nsThreadUtils.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsNotifyAddrListener.h"
 #include "nsString.h"
 #include "nsAutoPtr.h"
 #include "mozilla/Services.h"
+#include "nsCRT.h"
 
 #include <iptypes.h>
 #include <iphlpapi.h>
 
 typedef DWORD (WINAPI *GetAdaptersAddressesFunc)(ULONG, DWORD, PVOID,
                                                  PIP_ADAPTER_ADDRESSES,
                                                  PULONG);
 typedef DWORD (WINAPI *GetAdaptersInfoFunc)(PIP_ADAPTER_INFO, PULONG);
@@ -162,16 +163,26 @@ nsNotifyAddrListener::GetIsLinkUp(PRBool
 NS_IMETHODIMP
 nsNotifyAddrListener::GetLinkStatusKnown(PRBool *aIsUp)
 {
     *aIsUp = mStatusKnown;
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNotifyAddrListener::GetLinkType(PRUint32 *aLinkType)
+{
+  NS_ENSURE_ARG_POINTER(aLinkType);
+
+  // XXX This function has not yet been implemented for this platform
+  *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsNotifyAddrListener::Run()
 {
     HANDLE ev = CreateEvent(nsnull, FALSE, FALSE, nsnull);
     NS_ENSURE_TRUE(ev, NS_ERROR_OUT_OF_MEMORY);
 
     HANDLE handles[2] = { ev, mShutdownEvent };
     OVERLAPPED overlapped = { 0 };
     PRBool shuttingDown = PR_FALSE;
--- a/other-licenses/android/APKOpen.cpp
+++ b/other-licenses/android/APKOpen.cpp
@@ -233,17 +233,17 @@ Java_org_mozilla_gecko_GeckoAppShell_ ##
 SHELL_WRAPPER0(nativeInit)
 SHELL_WRAPPER1(nativeRun, jstring)
 SHELL_WRAPPER1(notifyGeckoOfEvent, jobject)
 SHELL_WRAPPER1(setSurfaceView, jobject)
 SHELL_WRAPPER0(onResume)
 SHELL_WRAPPER0(onLowMemory)
 SHELL_WRAPPER3(callObserver, jstring, jstring, jstring)
 SHELL_WRAPPER1(removeObserver, jstring)
-SHELL_WRAPPER1(onChangeNetworkLinkStatus, jstring)
+SHELL_WRAPPER2(onChangeNetworkLinkStatus, jstring, jstring)
 SHELL_WRAPPER1(reportJavaCrash, jstring)
 
 static void * xul_handle = NULL;
 static time_t apk_mtime = 0;
 #ifdef DEBUG
 extern "C" int extractLibs = 1;
 #else
 extern "C" int extractLibs = 0;
--- a/toolkit/system/dbus/nsNetworkManagerListener.cpp
+++ b/toolkit/system/dbus/nsNetworkManagerListener.cpp
@@ -40,16 +40,17 @@
  * ***** END LICENSE BLOCK ***** */
  
 #include "nsNetworkManagerListener.h"
 
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIObserverService.h"
 #include "nsStringAPI.h"
+#include "nsCRT.h"
 
 // Define NetworkManager API constants. This avoids a dependency on
 // NetworkManager-devel.
 #define NM_DBUS_SERVICE                 "org.freedesktop.NetworkManager"
 #define NM_DBUS_PATH                            "/org/freedesktop/NetworkManager"
 #define NM_DBUS_INTERFACE                       "org.freedesktop.NetworkManager"
 #define NM_DBUS_SIGNAL_STATE_CHANGE             "StateChange"
 typedef enum NMState
@@ -83,16 +84,26 @@ nsNetworkManagerListener::GetIsLinkUp(PR
 
 nsresult
 nsNetworkManagerListener::GetLinkStatusKnown(PRBool* aKnown) {
   *aKnown = mNetworkManagerActive;
   return NS_OK;
 }
 
 nsresult
+nsNetworkManagerListener::GetLinkType(PRUint32 *aLinkType)
+{
+  NS_ENSURE_ARG_POINTER(aLinkType);
+
+  // XXX This function has not yet been implemented for this platform
+  *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
+  return NS_OK;
+}
+
+nsresult
 nsNetworkManagerListener::Init() {
   mDBUS = nsDBusService::Get();
   if (!mDBUS)
     return NS_ERROR_OUT_OF_MEMORY;
   nsresult rv = mDBUS->AddClient(this);
   if (NS_FAILED(rv)) {
     mDBUS = nsnull;
     return rv;
--- a/widget/src/android/AndroidBridge.cpp
+++ b/widget/src/android/AndroidBridge.cpp
@@ -43,16 +43,17 @@
 #include <prthread.h>
 #include "nsXPCOMStrings.h"
 
 #include "AndroidBridge.h"
 #include "nsAppShell.h"
 #include "nsOSHelperAppService.h"
 #include "nsWindow.h"
 #include "mozilla/Preferences.h"
+#include "nsINetworkLinkService.h"
 
 #ifdef DEBUG
 #define ALOG_BRIDGE(args...) ALOG(args)
 #else
 #define ALOG_BRIDGE(args...)
 #endif
 
 #define IME_FULLSCREEN_PREF "widget.ime.android.landscape_fullscreen"
@@ -136,16 +137,17 @@ AndroidBridge::Init(JNIEnv *jEnv,
     jGetDpi = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getDpi", "()I");
     jSetFullScreen = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setFullScreen", "(Z)V");
     jShowInputMethodPicker = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showInputMethodPicker", "()V");
     jHideProgressDialog = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "hideProgressDialog", "()V");
     jPerformHapticFeedback = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "performHapticFeedback", "(Z)V");
     jSetKeepScreenOn = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setKeepScreenOn", "(Z)V");
     jIsNetworkLinkUp = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isNetworkLinkUp", "()Z");
     jIsNetworkLinkKnown = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isNetworkLinkKnown", "()Z");
+    jGetNetworkLinkType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getNetworkLinkType", "()I");
     jSetSelectedLocale = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setSelectedLocale", "(Ljava/lang/String;)V");
     jScanMedia = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "scanMedia", "(Ljava/lang/String;Ljava/lang/String;)V");
     jGetSystemColors = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getSystemColors", "()[I");
     jGetIconForExtension = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getIconForExtension", "(Ljava/lang/String;I)[B");
 
     jEGLContextClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGLContext"));
     jEGL10Class = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGL10"));
     jEGLSurfaceImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLSurfaceImpl"));
@@ -664,16 +666,23 @@ AndroidBridge::IsNetworkLinkUp()
 
 bool
 AndroidBridge::IsNetworkLinkKnown()
 {
     ALOG_BRIDGE("AndroidBridge::IsNetworkLinkKnown");
     return !!mJNIEnv->CallStaticBooleanMethod(mGeckoAppShellClass, jIsNetworkLinkKnown);
 }
 
+int
+AndroidBridge::GetNetworkLinkType()
+{
+    ALOG_BRIDGE("AndroidBridge::GetNetworkLinkType");
+    return (int) mJNIEnv->CallStaticIntMethod(mGeckoAppShellClass, jGetNetworkLinkType);
+}
+
 void
 AndroidBridge::SetSelectedLocale(const nsAString& aLocale)
 {
     ALOG_BRIDGE("AndroidBridge::SetSelectedLocale");
     AutoLocalJNIFrame jniFrame;
     jstring jLocale = GetJNIForThread()->NewString(PromiseFlatString(aLocale).get(), aLocale.Length());
     GetJNIForThread()->CallStaticVoidMethod(mGeckoAppShellClass, jSetSelectedLocale, jLocale);
 }
--- a/widget/src/android/AndroidBridge.h
+++ b/widget/src/android/AndroidBridge.h
@@ -199,16 +199,18 @@ public:
     void ShowInputMethodPicker();
 
     void HideProgressDialogOnce();
 
     bool IsNetworkLinkUp();
 
     bool IsNetworkLinkKnown();
 
+    int GetNetworkLinkType();
+
     void SetSelectedLocale(const nsAString&);
 
     void GetSystemColors(AndroidSystemColors *aColors);
 
     void GetIconForExtension(const nsACString& aFileExt, PRUint32 aIconSize, PRUint8 * const aBuf);
 
     struct AutoLocalJNIFrame {
         AutoLocalJNIFrame(int nEntries = 128) : mEntries(nEntries) {
@@ -307,16 +309,17 @@ protected:
     jmethodID jGetDpi;
     jmethodID jSetFullScreen;
     jmethodID jShowInputMethodPicker;
     jmethodID jHideProgressDialog;
     jmethodID jPerformHapticFeedback;
     jmethodID jSetKeepScreenOn;
     jmethodID jIsNetworkLinkUp;
     jmethodID jIsNetworkLinkKnown;
+    jmethodID jGetNetworkLinkType;
     jmethodID jSetSelectedLocale;
     jmethodID jScanMedia;
     jmethodID jGetSystemColors;
     jmethodID jGetIconForExtension;
 
     // stuff we need for CallEglCreateWindowSurface
     jclass jEGLSurfaceImplClass;
     jclass jEGLContextImplClass;
--- a/widget/src/android/AndroidJNI.cpp
+++ b/widget/src/android/AndroidJNI.cpp
@@ -64,17 +64,17 @@ using namespace mozilla;
 extern "C" {
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *, jclass);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *, jclass, jobject event);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject sv);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *, jclass);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *, jclass);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_callObserver(JNIEnv *, jclass, jstring observerKey, jstring topic, jstring data);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_removeObserver(JNIEnv *jenv, jclass, jstring jObserverKey);
-    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(JNIEnv *, jclass, jstring status);
+    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(JNIEnv *, jclass, jstring status, jstring type);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash(JNIEnv *, jclass, jstring stack);
 }
 
 
 /*
  * Incoming JNI methods
  */
 
@@ -138,26 +138,32 @@ Java_org_mozilla_gecko_GeckoAppShell_rem
     nsString sObserverKey(observerKey);
     sObserverKey.SetLength(jenv->GetStringLength(jObserverKey));
     jenv->ReleaseStringChars(jObserverKey, observerKey);
 
     nsAppShell::gAppShell->RemoveObserver(sObserverKey);
 }
 
 NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(JNIEnv *jenv, jclass, jstring jStatus)
+Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(JNIEnv *jenv, jclass, jstring jStatus, jstring jType)
 {
     if (!nsAppShell::gAppShell)
         return;
 
     nsJNIString sStatus(jStatus, jenv);
 
     nsAppShell::gAppShell->NotifyObservers(nsnull,
                                            NS_NETWORK_LINK_TOPIC,
                                            sStatus.get());
+
+    nsJNIString sType(jType, jenv);
+
+    nsAppShell::gAppShell->NotifyObservers(nsnull,
+                                           NS_NETWORK_LINK_TYPE_TOPIC,
+                                           sType.get());
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash(JNIEnv *, jclass, jstring stack)
 {
 #ifdef MOZ_CRASHREPORTER
      nsJNIString javaStack(stack);
      CrashReporter::AppendAppNotesToCrashReport(