Bug 667980: Expose network connection type to chrome. r=dougt
authorDoug Turner <dougt@dougt.org>
Sun, 10 Jul 2011 15:24:05 -0700
changeset 73007 49d539befa07ea32ff1194227ecf314fdf401d0a
parent 73006 f85107f85c644a57d77136c4d77c9a23a3c6c51a
child 73008 ed03d172ff0617554e89d00508f0bad778d3df9b
push id313
push usereakhgari@mozilla.com
push dateTue, 16 Aug 2011 19:58:41 +0000
treeherdermozilla-aurora@ef9d1c90dcbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdougt
bugs667980
milestone8.0a1
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(