Bug 1020412 - Proper handling of Context in GeckoNetworkManager. r=wesj, a=sledru
authorRichard Newman <rnewman@mozilla.com>
Wed, 18 Jun 2014 09:50:31 -0700
changeset 199547 b0b418b9709a5dcefcdd43aff4054fef62b3b1e3
parent 199546 87425f159ac4385574b6e2d2c372d1e47d8b5190
child 199548 bce3c9c7d8f7f951592704f5dc6cc7212011c189
push id3665
push userryanvm@gmail.com
push dateMon, 23 Jun 2014 19:23:15 +0000
treeherdermozilla-beta@c7d17b1ecef8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswesj, sledru
bugs1020412
milestone31.0
Bug 1020412 - Proper handling of Context in GeckoNetworkManager. r=wesj, a=sledru
mobile/android/base/GeckoNetworkManager.java
--- a/mobile/android/base/GeckoNetworkManager.java
+++ b/mobile/android/base/GeckoNetworkManager.java
@@ -18,20 +18,20 @@ import android.net.wifi.WifiManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
 /*
  * A part of the work of GeckoNetworkManager is to give an general connection
  * type based on the current connection. According to spec of NetworkInformation
  * API version 3, connection types include: bluetooth, cellular, ethernet, none,
  * wifi and other. The objective of providing such general connection is due to
- * some securtiy concerns. In short, we don't want to expose the information of
+ * some security concerns. In short, we don't want to expose the information of
  * exact network type, especially the cellular network type.
  *
- * Currnet connection is firstly obtained from Android's ConnectivityManager,
+ * Current connection is firstly obtained from Android's ConnectivityManager,
  * which is represented by the constant, and then will be mapped into the
  * connection type defined in Network Information API version 3.
  */
 
 public class GeckoNetworkManager extends BroadcastReceiver {
     private static final String LOGTAG = "GeckoNetworkManager";
 
     static private final GeckoNetworkManager sInstance = new GeckoNetworkManager();
@@ -52,97 +52,103 @@ public class GeckoNetworkManager extends
         }
     }
 
     private enum InfoType {
         MCC,
         MNC
     }
 
-    static private final ConnectionType kDefaultConnectionType = ConnectionType.NONE;
-
-    private static Context getApplicationContext() {
-        Context context = GeckoAppShell.getContext();
-        if (null == context)
-            return null;
-        return context.getApplicationContext();
-    }
     private ConnectionType mConnectionType = ConnectionType.NONE;
     private final IntentFilter mNetworkFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
 
     // Whether the manager should be listening to Network Information changes.
     private boolean mShouldBeListening = false;
 
     // Whether the manager should notify Gecko that a change in Network
     // Information happened.
     private boolean mShouldNotify = false;
 
+    // The application context used for registering receivers, so
+    // we can unregister them again later.
+    private volatile Context mApplicationContext;
+
     public static GeckoNetworkManager getInstance() {
         return sInstance;
     }
 
     @Override
     public void onReceive(Context aContext, Intent aIntent) {
         updateConnectionType();
     }
 
     public void start(final Context context) {
         // Note that this initialization clause only runs once.
+        mApplicationContext = context.getApplicationContext();
         if (mConnectionType == ConnectionType.NONE) {
             mConnectionType = getConnectionType();
         }
 
         mShouldBeListening = true;
         updateConnectionType();
 
         if (mShouldNotify) {
             startListening();
         }
     }
 
     private void startListening() {
-        if (null !=getApplicationContext())
-            getApplicationContext().registerReceiver(sInstance, mNetworkFilter);
+        final Context appContext = mApplicationContext;
+        if (appContext == null) {
+            Log.w(LOGTAG, "Not registering receiver: no context!");
+            return;
+        }
+
+        Log.v(LOGTAG, "Registering receiver.");
+        appContext.registerReceiver(this, mNetworkFilter);
     }
 
     public void stop() {
         mShouldBeListening = false;
 
         if (mShouldNotify) {
             stopListening();
         }
     }
 
     private void stopListening() {
-        if (null != getApplicationContext())
-            getApplicationContext().unregisterReceiver(sInstance);
+        if (null == mApplicationContext) {
+            return;
+        }
+
+        mApplicationContext.unregisterReceiver(this);
     }
 
     private int wifiDhcpGatewayAddress() {
         if (mConnectionType != ConnectionType.WIFI) {
             return 0;
         }
 
-        if (null == getApplicationContext()) {
+        if (null == mApplicationContext) {
             return 0;
         }
 
         try {
-            WifiManager mgr = (WifiManager)getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+            WifiManager mgr = (WifiManager) mApplicationContext.getSystemService(Context.WIFI_SERVICE);
             DhcpInfo d = mgr.getDhcpInfo();
             if (d == null) {
                 return 0;
             }
 
             return d.gateway;
 
         } catch (Exception ex) {
             // getDhcpInfo() is not documented to require any permissions, but on some devices
-            // requires android.permission.ACCESS_WIFI_STATE. Just catching the generic exeption
-            // here and returning 0. Not logging because this could be noisy
+            // requires android.permission.ACCESS_WIFI_STATE. Just catch the generic exception
+            // here and returning 0. Not logging because this could be noisy.
             return 0;
         }
     }
 
     private void updateConnectionType() {
         ConnectionType previousConnectionType = mConnectionType;
         mConnectionType = getConnectionType();
 
@@ -177,23 +183,24 @@ public class GeckoNetworkManager extends
     public void disableNotifications() {
         mShouldNotify = false;
 
         if (mShouldBeListening) {
             stopListening();
         }
     }
 
-    private static ConnectionType getConnectionType() {
-        if (null == getApplicationContext()) {
+    private ConnectionType getConnectionType() {
+        final Context appContext = mApplicationContext;
+
+        if (null == appContext) {
             return ConnectionType.NONE;
         }
 
-        ConnectivityManager cm =
-            (ConnectivityManager)getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        ConnectivityManager cm = (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         if (cm == null) {
             Log.e(LOGTAG, "Connectivity service does not exist");
             return ConnectionType.NONE;
         }
 
         NetworkInfo ni = null;
         try {
             ni = cm.getActiveNetworkInfo();
@@ -214,21 +221,22 @@ public class GeckoNetworkManager extends
         case ConnectivityManager.TYPE_WIFI:
             return ConnectionType.WIFI;
         default:
             Log.w(LOGTAG, "Ignoring the current network type.");
             return ConnectionType.OTHER;
         }
     }
 
-    private static int getNetworkOperator(InfoType type) {
-        if (null == getApplicationContext())
+    private static int getNetworkOperator(InfoType type, Context context) {
+        if (null == context) {
             return -1;
+        }
 
-        TelephonyManager tel = (TelephonyManager)getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
+        TelephonyManager tel = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         if (tel == null) {
             Log.e(LOGTAG, "Telephony service does not exist");
             return -1;
         }
 
         String networkOperator = tel.getNetworkOperator();
         if (networkOperator == null || networkOperator.length() <= 3) {
             return -1;
@@ -237,19 +245,24 @@ public class GeckoNetworkManager extends
             return Integer.parseInt(networkOperator.substring(3));
         } else if (type == InfoType.MCC) {
             return Integer.parseInt(networkOperator.substring(0, 3));
         }
 
         return -1;
     }
 
-    /* These are called from javascript c-types. Avoid letting pro-guard delete them */
+    /**
+     * These are called from JavaScript ctypes. Avoid letting ProGuard delete them.
+     *
+     * Note that these methods must only be called after GeckoAppShell has been
+     * initialized: they depend on access to the context.
+     */
     @JNITarget
     public static int getMCC() {
-        return getNetworkOperator(InfoType.MCC);
+        return getNetworkOperator(InfoType.MCC, GeckoAppShell.getContext().getApplicationContext());
     }
 
     @JNITarget
     public static int getMNC() {
-        return getNetworkOperator(InfoType.MNC);
+        return getNetworkOperator(InfoType.MNC, GeckoAppShell.getContext().getApplicationContext());
     }
 }