Bug 969637 - NullPointerException in org.mozilla.gecko.GeckoConnectivityReceiver.start. r=wesj
authorRichard Newman <rnewman@mozilla.com>
Thu, 27 Feb 2014 12:32:34 -0800
changeset 171529 d4c7d7ed21969d64a1942bac871464bd32548a41
parent 171528 1eebddd614ebdc8aadb40d28a8a144e3b7507834
child 171530 46ff4f074276035d8add88e6421c14d901623d6f
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerswesj
bugs969637
milestone30.0a1
Bug 969637 - NullPointerException in org.mozilla.gecko.GeckoConnectivityReceiver.start. r=wesj
mobile/android/base/BrowserApp.java
mobile/android/base/FilePicker.java
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoApplication.java
mobile/android/base/GeckoBatteryManager.java
mobile/android/base/GeckoConnectivityReceiver.java
mobile/android/base/GeckoNetworkManager.java
mobile/android/base/GeckoView.java
mobile/android/base/MemoryMonitor.java
mobile/android/base/util/Clipboard.java
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -438,24 +438,28 @@ abstract public class BrowserApp extends
 
         String args = intent.getStringExtra("args");
         if (args != null && args.contains(GUEST_BROWSING_ARG)) {
             mProfile = GeckoProfile.createGuestProfile(this);
         } else {
             GeckoProfile.maybeCleanupGuestProfile(this);
         }
 
+        // This has to be prepared prior to calling GeckoApp.onCreate, because
+        // widget code and BrowserToolbar need it, and they're created by the
+        // layout, which GeckoApp takes care of.
+        ((GeckoApplication) getApplication()).prepareLightweightTheme();
         super.onCreate(savedInstanceState);
 
         mViewFlipper = (ViewFlipper) findViewById(R.id.browser_actionbar);
         mActionBar = (ActionModeCompatView) findViewById(R.id.actionbar);
 
         mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar);
         if (Intent.ACTION_VIEW.equals(intent.getAction())) {
-            // Show the target URL immediately in the toolbar
+            // Show the target URL immediately in the toolbar.
             mBrowserToolbar.setTitle(intent.getDataString());
         }
 
         ((GeckoApp.MainLayout) mMainLayout).setTouchEventInterceptor(new HideTabsTouchListener());
         ((GeckoApp.MainLayout) mMainLayout).setMotionEventInterceptor(new MotionEventInterceptor() {
             @Override
             public boolean onInterceptMotionEvent(View view, MotionEvent event) {
                 // If we get a gamepad panning MotionEvent while the focus is not on the layerview,
--- a/mobile/android/base/FilePicker.java
+++ b/mobile/android/base/FilePicker.java
@@ -36,17 +36,17 @@ public class FilePicker implements Gecko
     private final Context mContext;
 
     public interface ResultHandler {
         public void gotFile(String filename);
     }
 
     public static void init(Context context) {
         if (sFilePicker == null) {
-            sFilePicker = new FilePicker(context);
+            sFilePicker = new FilePicker(context.getApplicationContext());
         }
     }
 
     protected FilePicker(Context context) {
         mContext = context;
         GeckoAppShell.getEventDispatcher().registerEventListener("FilePicker:Show", this);
     }
 
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1208,17 +1208,23 @@ public abstract class GeckoApp
 
                 if (profileName != null || profilePath != null) {
                     mProfile = GeckoProfile.get(this, profileName, profilePath);
                 }
             }
         }
 
         BrowserDB.initialize(getProfile().getName());
-        ((GeckoApplication) getApplication()).initialize();
+
+        // Workaround for <http://code.google.com/p/android/issues/detail?id=20915>.
+        try {
+            Class.forName("android.os.AsyncTask");
+        } catch (ClassNotFoundException e) {}
+
+        MemoryMonitor.getInstance().init(getApplicationContext());
 
         sAppContext = this;
         GeckoAppShell.setContextGetter(this);
         GeckoAppShell.setGeckoInterface(this);
         ThreadUtils.setUiThread(Thread.currentThread(), new Handler());
 
         Tabs.getInstance().attachToContext(this);
         try {
@@ -1273,17 +1279,16 @@ public abstract class GeckoApp
                 prefs.edit().remove(PREFS_ALLOW_STATE_BUNDLE).commit();
                 savedInstanceState = stateBundle;
             }
         } else if (savedInstanceState != null) {
             // Bug 896992 - This intent has already been handled; reset the intent.
             setIntent(new Intent(Intent.ACTION_MAIN));
         }
 
-
         super.onCreate(savedInstanceState);
 
         GeckoScreenOrientation.getInstance().update(getResources().getConfiguration().orientation);
 
         setContentView(getLayout());
 
         // Set up Gecko layout.
         mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout);
--- a/mobile/android/base/GeckoApplication.java
+++ b/mobile/android/base/GeckoApplication.java
@@ -8,23 +8,23 @@ import org.mozilla.gecko.db.BrowserContr
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.home.HomeConfigInvalidator;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.app.Application;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.util.Log;
 
 public class GeckoApplication extends Application {
     private static final String LOG_TAG = "GeckoApplication";
 
-    private boolean mInited;
     private boolean mInBackground;
     private boolean mPausedGecko;
 
     private LightweightTheme mLightweightTheme;
 
     /**
      * We need to do locale work here, because we need to intercept
      * each hit to onConfigurationChanged.
@@ -48,36 +48,16 @@ public class GeckoApplication extends Ap
         } catch (IllegalStateException ex) {
             // GeckoApp hasn't started, so we have no ContextGetter in LocaleManager.
             Log.w(LOG_TAG, "Couldn't correct locale.", ex);
         }
 
         super.onConfigurationChanged(config);
     }
 
-    protected void initialize() {
-        if (mInited)
-            return;
-
-        // workaround for http://code.google.com/p/android/issues/detail?id=20915
-        try {
-            Class.forName("android.os.AsyncTask");
-        } catch (ClassNotFoundException e) {}
-
-        mLightweightTheme = new LightweightTheme(this);
-
-        GeckoConnectivityReceiver.getInstance().init(getApplicationContext());
-        GeckoBatteryManager.getInstance().init(getApplicationContext());
-        GeckoBatteryManager.getInstance().start();
-        GeckoNetworkManager.getInstance().init(getApplicationContext());
-        MemoryMonitor.getInstance().init(getApplicationContext());
-
-        mInited = true;
-    }
-
     public void onActivityPause(GeckoActivityStatus activity) {
         mInBackground = true;
 
         if ((activity.isFinishing() == false) &&
             (activity.isGeckoActivityOpened() == false)) {
             // Notify Gecko that we are pausing; the cache service will be
             // shutdown, closing the disk cache cleanly. If the android
             // low memory killer subsequently kills us, the disk cache will
@@ -98,18 +78,21 @@ public class GeckoApplication extends Ap
         GeckoNetworkManager.getInstance().stop();
     }
 
     public void onActivityResume(GeckoActivityStatus activity) {
         if (mPausedGecko) {
             GeckoAppShell.sendEventToGecko(GeckoEvent.createAppForegroundingEvent());
             mPausedGecko = false;
         }
-        GeckoConnectivityReceiver.getInstance().start();
-        GeckoNetworkManager.getInstance().start();
+
+        final Context applicationContext = getApplicationContext();
+        GeckoBatteryManager.getInstance().start(applicationContext);
+        GeckoConnectivityReceiver.getInstance().start(applicationContext);
+        GeckoNetworkManager.getInstance().start(applicationContext);
 
         mInBackground = false;
     }
 
     @Override
     public void onCreate() {
         HardwareUtils.init(getApplicationContext());
         Clipboard.init(getApplicationContext());
@@ -121,9 +104,13 @@ public class GeckoApplication extends Ap
 
     public boolean isApplicationInBackground() {
         return mInBackground;
     }
 
     public LightweightTheme getLightweightTheme() {
         return mLightweightTheme;
     }
+
+    public void prepareLightweightTheme() {
+        mLightweightTheme = new LightweightTheme(this);
+    }
 }
--- a/mobile/android/base/GeckoBatteryManager.java
+++ b/mobile/android/base/GeckoBatteryManager.java
@@ -27,49 +27,53 @@ public class GeckoBatteryManager extends
     private static long    sLastLevelChange            = 0;
     private static boolean sNotificationsEnabled       = false;
     private static double  sLevel                      = kDefaultLevel;
     private static boolean sCharging                   = kDefaultCharging;
     private static double  sRemainingTime              = kDefaultRemainingTime;
 
     private static GeckoBatteryManager sInstance = new GeckoBatteryManager();
 
-    private IntentFilter mFilter;
+    private final IntentFilter mFilter;
     private Context mApplicationContext;
     private boolean mIsEnabled;
 
     public static GeckoBatteryManager getInstance() {
         return sInstance;
     }
 
     private GeckoBatteryManager() {
         mFilter = new IntentFilter();
         mFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
     }
 
-    public void init(Context context) {
-        mApplicationContext = context.getApplicationContext();
-    }
+    public synchronized void start(final Context context) {
+        if (mIsEnabled) {
+            Log.w(LOGTAG, "Already started!");
+            return;
+        }
 
-    public synchronized void start() {
-        if (!mIsEnabled) {
-            // registerReceiver will return null if registering fails
-            if (mApplicationContext.registerReceiver(this, mFilter) == null) {
-                Log.e(LOGTAG, "Registering receiver failed");
-            } else {
-                mIsEnabled = true;
-            }
+        mApplicationContext = context.getApplicationContext();
+        // registerReceiver will return null if registering fails.
+        if (mApplicationContext.registerReceiver(this, mFilter) == null) {
+            Log.e(LOGTAG, "Registering receiver failed");
+        } else {
+            mIsEnabled = true;
         }
     }
 
     public synchronized void stop() {
-        if (mIsEnabled) {
-            mApplicationContext.unregisterReceiver(this);
-            mIsEnabled = false;
+        if (!mIsEnabled) {
+            Log.w(LOGTAG, "Already stopped!");
+            return;
         }
+
+        mApplicationContext.unregisterReceiver(this);
+        mApplicationContext = null;
+        mIsEnabled = false;
     }
 
     @Override
     public void onReceive(Context context, Intent intent) {
         if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
             Log.e(LOGTAG, "Got an unexpected intent!");
             return;
         }
--- a/mobile/android/base/GeckoConnectivityReceiver.java
+++ b/mobile/android/base/GeckoConnectivityReceiver.java
@@ -21,57 +21,62 @@ public class GeckoConnectivityReceiver e
     private static final String LINK_DATA_UP = "up";
     private static final String LINK_DATA_DOWN = "down";
     private static final String LINK_DATA_UNKNOWN = "unknown";
 
     private static final String LOGTAG = "GeckoConnectivityReceiver";
 
     private static GeckoConnectivityReceiver sInstance = new GeckoConnectivityReceiver();
 
-    private IntentFilter mFilter;
+    private final IntentFilter mFilter;
     private Context mApplicationContext;
     private boolean mIsEnabled;
 
     public static GeckoConnectivityReceiver getInstance() {
         return sInstance;
     }
 
     private GeckoConnectivityReceiver() {
         mFilter = new IntentFilter();
         mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
     }
 
-    public void init(Context context) {
-        mApplicationContext = context.getApplicationContext();
-    }
+    public synchronized void start(Context context) {
+        if (mIsEnabled) {
+            Log.w(LOGTAG, "Already started!");
+            return;
+        }
 
-    public synchronized void start() {
-        if (!mIsEnabled) {
-            // registerReceiver will return null if registering fails
-            if (mApplicationContext.registerReceiver(this, mFilter) == null) {
-                Log.e(LOGTAG, "Registering receiver failed");
-            } else {
-                mIsEnabled = true;
-            }
+        mApplicationContext = context.getApplicationContext();
+
+        // registerReceiver will return null if registering fails.
+        if (mApplicationContext.registerReceiver(this, mFilter) == null) {
+            Log.e(LOGTAG, "Registering receiver failed");
+        } else {
+            mIsEnabled = true;
         }
     }
 
     public synchronized void stop() {
-        if (mIsEnabled) {
-            mApplicationContext.unregisterReceiver(this);
-            mIsEnabled = false;
+        if (!mIsEnabled) {
+            Log.w(LOGTAG, "Already stopped!");
+            return;
         }
+
+        mApplicationContext.unregisterReceiver(this);
+        mApplicationContext = null;
+        mIsEnabled = false;
     }
 
     @Override
     public void onReceive(Context context, Intent intent) {
         ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
         NetworkInfo info = cm.getActiveNetworkInfo();
 
-        String status;
+        final String status;
         if (info == null) {
             status = LINK_DATA_UNKNOWN;
         } else if (!info.isConnected()) {
             status = LINK_DATA_DOWN;
         } else {
             status = LINK_DATA_UP;
         }
 
--- a/mobile/android/base/GeckoNetworkManager.java
+++ b/mobile/android/base/GeckoNetworkManager.java
@@ -98,57 +98,60 @@ public class GeckoNetworkManager extends
     }
 
     private enum InfoType {
         MCC,
         MNC
     }
 
     private Context mApplicationContext;
-    private NetworkType  mNetworkType = NetworkType.NETWORK_NONE;
-    private IntentFilter mNetworkFilter = new IntentFilter();
+    private NetworkType mNetworkType = NetworkType.NETWORK_NONE;
+    private final IntentFilter mNetworkFilter = new IntentFilter();
+
     // 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;
+    private boolean mShouldNotify = false;
 
     public static GeckoNetworkManager getInstance() {
         return sInstance;
     }
 
     @Override
     public void onReceive(Context aContext, Intent aIntent) {
         updateNetworkType();
     }
 
-    public void init(Context context) {
-        mApplicationContext = context.getApplicationContext();
-        mNetworkFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
-        mNetworkType = getNetworkType();
-    }
+    public void start(final Context context) {
+        // Note that this initialization clause only runs once.
+        if (mApplicationContext == null) {
+            mApplicationContext = context.getApplicationContext();
+            mNetworkFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+            mNetworkType = getNetworkType();
+        }
 
-    public void start() {
         mShouldBeListening = true;
         updateNetworkType();
 
         if (mShouldNotify) {
             startListening();
         }
     }
 
     private void startListening() {
         mApplicationContext.registerReceiver(sInstance, mNetworkFilter);
     }
 
     public void stop() {
         mShouldBeListening = false;
 
         if (mShouldNotify) {
-        stopListening();
+            stopListening();
         }
     }
 
     private void stopListening() {
         mApplicationContext.unregisterReceiver(sInstance);
     }
 
     private int wifiDhcpGatewayAddress() {
--- a/mobile/android/base/GeckoView.java
+++ b/mobile/android/base/GeckoView.java
@@ -70,17 +70,18 @@ public class GeckoView extends LayerView
             // Set the GeckoInterface if the context is an activity and the GeckoInterface
             // has not already been set
             if (context instanceof Activity && getGeckoInterface() == null) {
                 setGeckoInterface(new BaseGeckoInterface(context));
             }
 
             Clipboard.init(context);
             HardwareUtils.init(context);
-            GeckoNetworkManager.getInstance().init(context);
+
+            // If you want to use GeckoNetworkManager, start it.
 
             GeckoLoader.loadMozGlue();
             BrowserDB.setEnableContentProviders(false);
          }
 
         if (url != null) {
             GeckoThread.setUri(url);
             GeckoThread.setAction(Intent.ACTION_VIEW);
--- a/mobile/android/base/MemoryMonitor.java
+++ b/mobile/android/base/MemoryMonitor.java
@@ -51,30 +51,36 @@ class MemoryMonitor extends BroadcastRec
 
     static MemoryMonitor getInstance() {
         return sInstance;
     }
 
     private final PressureDecrementer mPressureDecrementer;
     private int mMemoryPressure;
     private boolean mStoragePressure;
+    private boolean mInited;
 
     private MemoryMonitor() {
         mPressureDecrementer = new PressureDecrementer();
         mMemoryPressure = MEMORY_PRESSURE_NONE;
         mStoragePressure = false;
     }
 
-    public void init(Context context) {
+    public void init(final Context context) {
+        if (mInited) {
+            return;
+        }
+
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
         filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
         filter.addAction(ACTION_MEMORY_DUMP);
         filter.addAction(ACTION_FORCE_PRESSURE);
         context.getApplicationContext().registerReceiver(this, filter);
+        mInited = true;
     }
 
     public void onLowMemory() {
         Log.d(LOGTAG, "onLowMemory() notification received");
         if (increaseMemoryPressure(MEMORY_PRESSURE_HIGH)) {
             // We need to wait on Gecko here, because if we haven't reduced
             // memory usage enough when we return from this, Android will kill us.
             GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createNoOpEvent());
--- a/mobile/android/base/util/Clipboard.java
+++ b/mobile/android/base/util/Clipboard.java
@@ -16,22 +16,22 @@ import java.util.concurrent.SynchronousQ
 public final class Clipboard {
     private static Context mContext;
     private final static String LOGTAG = "GeckoClipboard";
     private final static SynchronousQueue<String> sClipboardQueue = new SynchronousQueue<String>();
 
     private Clipboard() {
     }
 
-    public static void init(Context c) {
+    public static void init(final Context c) {
         if (mContext != null) {
             Log.w(LOGTAG, "Clipboard.init() called twice!");
             return;
         }
-        mContext = c;
+        mContext = c.getApplicationContext();
     }
 
     @WrapElementForJNI(stubName = "GetClipboardTextWrapper")
     public static String getText() {
         // If we're on the UI thread or the background thread, we have a looper on the thread
         // and can just call this directly. For any other threads, post the call to the
         // background thread.