bug 882196 - Android crash in nsXPCWrappedJS::AddRef, remove nsAppShell::NotifyObservers r=kats
authorBrad Lassey <blassey@mozilla.com>
Mon, 17 Jun 2013 17:09:09 -0400
changeset 135367 35b943a379dded84def27a77d9d8dc907a4abfe5
parent 135366 f7628d639dd1b86866c66a58ba511f962ce2c6b7
child 135368 c873354a92420a0bdbc2a8e54591f41598cf4aff
push idunknown
push userunknown
push dateunknown
reviewerskats
bugs882196
milestone24.0a1
bug 882196 - Android crash in nsXPCWrappedJS::AddRef, remove nsAppShell::NotifyObservers r=kats
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoConnectivityReceiver.java
mobile/android/base/GeckoEvent.java
mobile/android/base/MemoryMonitor.java
mozglue/android/jni-stubs.inc
widget/android/AndroidJNI.cpp
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
widget/android/nsAppShell.cpp
widget/android/nsAppShell.h
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -166,22 +166,20 @@ public class GeckoAppShell
 
     // Initialization methods
     public static native void nativeInit();
 
     // helper methods
     //    public static native void setSurfaceView(GeckoSurfaceView sv);
     public static native void setLayerClient(GeckoLayerClient client);
     public static native void onResume();
-    public static native void onLowMemory();
     public static void callObserver(String observerKey, String topic, String data) {
         sendEventToGecko(GeckoEvent.createCallObserverEvent(observerKey, topic, data));
     }
     public static native void removeObserver(String observerKey);
-    public static native void onChangeNetworkLinkStatus(String status);
     public static native Message getNextMessageFromQueue(MessageQueue queue);
     public static native void onSurfaceTextureFrameAvailable(Object surfaceTexture, int id);
 
     public static void registerGlobalExceptionHandler() {
         Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
             @Override
             public void uncaughtException(Thread thread, Throwable e) {
                 // If the uncaught exception was rethrown, walk the exception `cause` chain to find
--- a/mobile/android/base/GeckoConnectivityReceiver.java
+++ b/mobile/android/base/GeckoConnectivityReceiver.java
@@ -71,12 +71,12 @@ public class GeckoConnectivityReceiver e
             status = LINK_DATA_UNKNOWN;
         } else if (!info.isConnected()) {
             status = LINK_DATA_DOWN;
         } else {
             status = LINK_DATA_UP;
         }
 
         if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
-            GeckoAppShell.onChangeNetworkLinkStatus(status);
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkLinkChangeEvent(status));
         }
     }
 }
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -58,17 +58,19 @@ public class GeckoEvent {
         NETWORK_CHANGED(22),
         THUMBNAIL(25),
         SCREENORIENTATION_CHANGED(27),
         COMPOSITOR_CREATE(28),
         COMPOSITOR_PAUSE(29),
         COMPOSITOR_RESUME(30),
         NATIVE_GESTURE_EVENT(31),
         IME_KEY_EVENT(32),
-        CALL_OBSERVER(33);
+        CALL_OBSERVER(33),
+        LOW_MEMORY(34),
+        NETWORK_LINK_CHANGE(35);
 
         public final int value;
 
         private NativeGeckoEvent(int value) {
             this.value = value;
          }
     }
 
@@ -662,12 +664,24 @@ public class GeckoEvent {
     public static GeckoEvent createCallObserverEvent(String observerKey, String topic, String data) {
         GeckoEvent event = new GeckoEvent(NativeGeckoEvent.CALL_OBSERVER);
         event.mCharacters = observerKey;
         event.mCharactersExtra = topic;
         event.mData = data;
         return event;
     }
 
+    public static GeckoEvent createLowMemoryEvent(int level) {
+        GeckoEvent event = new GeckoEvent(NativeGeckoEvent.LOW_MEMORY);
+        event.mMetaState = level;
+        return event;
+    }
+
+    public static GeckoEvent createNetworkLinkChangeEvent(String status) {
+        GeckoEvent event = new GeckoEvent(NativeGeckoEvent.NETWORK_LINK_CHANGE);
+        event.mCharacters = status;
+        return event;
+    }
+
     public void setAckNeeded(boolean ackNeeded) {
         mAckNeeded = ackNeeded;
     }
 }
--- a/mobile/android/base/MemoryMonitor.java
+++ b/mobile/android/base/MemoryMonitor.java
@@ -34,16 +34,17 @@ import android.util.Log;
   * be thread-safe. In terms of lock ordering, code holding the PressureDecrementer lock
   * is allowed to pick up the MemoryMonitor lock, but not vice-versa.
   */
 class MemoryMonitor extends BroadcastReceiver {
     private static final String LOGTAG = "GeckoMemoryMonitor";
     private static final String ACTION_MEMORY_DUMP = "org.mozilla.gecko.MEMORY_DUMP";
     private static final String ACTION_FORCE_PRESSURE = "org.mozilla.gecko.FORCE_MEMORY_PRESSURE";
 
+    // Memory pressue levels, keep in sync with those in AndroidJavaWrappers.h
     private static final int MEMORY_PRESSURE_NONE = 0;
     private static final int MEMORY_PRESSURE_CLEANUP = 1;
     private static final int MEMORY_PRESSURE_LOW = 2;
     private static final int MEMORY_PRESSURE_MEDIUM = 3;
     private static final int MEMORY_PRESSURE_HIGH = 4;
 
     private static MemoryMonitor sInstance = new MemoryMonitor();
 
@@ -143,18 +144,19 @@ class MemoryMonitor extends BroadcastRec
             // if we're not going to a higher level we probably don't
             // need to run another round of the same memory reductions
             // we did on the last memory pressure increase.
             return false;
         }
 
         // TODO hook in memory-reduction stuff for different levels here
         if (level >= MEMORY_PRESSURE_MEDIUM) {
+            //Only send medium or higher events because that's all that is used right now
             if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
-                GeckoAppShell.onLowMemory();
+                GeckoAppShell.sendEventToGecko(GeckoEvent.createLowMemoryEvent(level));
             }
 
             Favicons.getInstance().clearMemCache();
         }
         return true;
     }
 
     private boolean decreaseMemoryPressure() {
--- a/mozglue/android/jni-stubs.inc
+++ b/mozglue/android/jni-stubs.inc
@@ -53,35 +53,16 @@ Java_org_mozilla_gecko_GeckoAppShell_onR
 #endif
 
 #ifdef JNI_BINDINGS
   xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_onResume", &f_Java_org_mozilla_gecko_GeckoAppShell_onResume);
 #endif
 
 #ifdef JNI_STUBS
 
-typedef void (*Java_org_mozilla_gecko_GeckoAppShell_onLowMemory_t)(JNIEnv *, jclass);
-static Java_org_mozilla_gecko_GeckoAppShell_onLowMemory_t f_Java_org_mozilla_gecko_GeckoAppShell_onLowMemory;
-extern "C" NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv * arg0, jclass arg1) {
-    if (!f_Java_org_mozilla_gecko_GeckoAppShell_onLowMemory) {
-        arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
-                       "JNI Function called before it was loaded");
-        return ;
-    }
-     f_Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(arg0, arg1);
-}
-#endif
-
-#ifdef JNI_BINDINGS
-  xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_onLowMemory", &f_Java_org_mozilla_gecko_GeckoAppShell_onLowMemory);
-#endif
-
-#ifdef JNI_STUBS
-
 typedef void (*Java_org_mozilla_gecko_GeckoAppShell_removeObserver_t)(JNIEnv *, jclass, jstring);
 static Java_org_mozilla_gecko_GeckoAppShell_removeObserver_t f_Java_org_mozilla_gecko_GeckoAppShell_removeObserver;
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_removeObserver(JNIEnv * arg0, jclass arg1, jstring arg2) {
     if (!f_Java_org_mozilla_gecko_GeckoAppShell_removeObserver) {
         arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
                        "JNI Function called before it was loaded");
         return ;
@@ -91,35 +72,16 @@ Java_org_mozilla_gecko_GeckoAppShell_rem
 #endif
 
 #ifdef JNI_BINDINGS
   xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_removeObserver", &f_Java_org_mozilla_gecko_GeckoAppShell_removeObserver);
 #endif
 
 #ifdef JNI_STUBS
 
-typedef void (*Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus_t)(JNIEnv *, jclass, jstring);
-static Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus_t f_Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus;
-extern "C" NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(JNIEnv * arg0, jclass arg1, jstring arg2) {
-    if (!f_Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus) {
-        arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
-                       "JNI Function called before it was loaded");
-        return ;
-    }
-     f_Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(arg0, arg1, arg2);
-}
-#endif
-
-#ifdef JNI_BINDINGS
-  xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus", &f_Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus);
-#endif
-
-#ifdef JNI_STUBS
-
 typedef jobject (*Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue_t)(JNIEnv *, jclass, jobject);
 static Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue_t f_Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue;
 extern "C" NS_EXPORT jobject JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue(JNIEnv * arg0, jclass arg1, jobject arg2) {
     if (!f_Java_org_mozilla_gecko_GeckoAppShell_getNextMessageFromQueue) {
         arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
                        "JNI Function called before it was loaded");
         return NULL;
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -17,17 +17,16 @@
 #include <unistd.h>
 #include <sched.h>
 
 #include "nsAppShell.h"
 #include "nsWindow.h"
 #include <android/log.h>
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
-#include "nsINetworkLinkService.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsICrashReporter.h"
 #include "nsExceptionHandler.h"
 #endif
 
 #include "mozilla/unused.h"
 
@@ -80,26 +79,16 @@ Java_org_mozilla_gecko_GeckoAppShell_pro
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_setLayerClient(JNIEnv *jenv, jclass, jobject obj)
 {
     AndroidBridge::Bridge()->SetLayerClient(jenv, obj);
 }
 
 NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *jenv, jclass jc)
-{
-    if (nsAppShell::gAppShell) {
-        nsAppShell::gAppShell->NotifyObservers(nullptr,
-                                               "memory-pressure",
-                                               NS_LITERAL_STRING("low-memory").get());
-    }
-}
-
-NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *jenv, jclass jc)
 {
     if (nsAppShell::gAppShell)
         nsAppShell::gAppShell->OnResume();
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_removeObserver(JNIEnv *jenv, jclass, jstring jObserverKey)
@@ -111,29 +100,16 @@ 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)
-{
-    if (!nsAppShell::gAppShell)
-        return;
-
-    nsJNIString sStatus(jStatus, jenv);
-
-    nsAppShell::gAppShell->NotifyObservers(nullptr,
-                                           NS_NETWORK_LINK_TOPIC,
-                                           sStatus.get());
-}
-
-NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash(JNIEnv *jenv, jclass, jstring jStackTrace)
 {
 #ifdef MOZ_CRASHREPORTER
     const nsJNIString stackTrace16(jStackTrace, jenv);
     const NS_ConvertUTF16toUTF8 stackTrace8(stackTrace16);
     CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("JavaStackTrace"), stackTrace8);
 #endif // MOZ_CRASHREPORTER
 
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -685,16 +685,26 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jo
 
         case CALL_OBSERVER: {
             ReadCharactersField(jenv);
             ReadCharactersExtraField(jenv);
             ReadDataField(jenv);
             break;
         }
 
+        case LOW_MEMORY: {
+            mMetaState = jenv->GetIntField(jobj, jMetaStateField);
+            break;
+        }
+
+        case NETWORK_LINK_CHANGE: {
+            ReadCharactersField(jenv);
+            break;
+        }
+
         default:
             break;
     }
 
 #ifdef DEBUG_ANDROID_EVENTS
     ALOG("AndroidGeckoEvent: %p : %d", (void*)jobj, mType);
 #endif
 }
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -702,20 +702,31 @@ public:
         THUMBNAIL = 25,
         SCREENORIENTATION_CHANGED = 27,
         COMPOSITOR_CREATE = 28,
         COMPOSITOR_PAUSE = 29,
         COMPOSITOR_RESUME = 30,
         NATIVE_GESTURE_EVENT = 31,
         IME_KEY_EVENT = 32,
         CALL_OBSERVER = 33,
+        LOW_MEMORY = 34,
+        NETWORK_LINK_CHANGE = 35,
         dummy_java_enum_list_end
     };
 
     enum {
+        // Memory pressue levels, keep in sync with those in MemoryMonitor.java
+        MEMORY_PRESSURE_NONE = 0,
+        MEMORY_PRESSURE_CLEANUP = 1,
+        MEMORY_PRESSURE_LOW = 2,
+        MEMORY_PRESSURE_MEDIUM = 3,
+        MEMORY_PRESSURE_HIGH = 4
+    };
+
+    enum {
         // Internal Gecko events
         IME_FLUSH_CHANGES = -2,
         IME_UPDATE_CONTEXT = -1,
         // Events from Java to Gecko
         IME_SYNCHRONIZE = 0,
         IME_REPLACE_TEXT = 1,
         IME_SET_SELECTION = 2,
         IME_ADD_COMPOSITION_RANGE = 3,
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -20,16 +20,17 @@
 #include "nsCacheService.h"
 #include "nsIDOMEventListener.h"
 #include "nsDOMNotifyPaintEvent.h"
 #include "nsIDOMClientRectList.h"
 #include "nsIDOMClientRect.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIPowerManagerService.h"
 #include "nsFrameManager.h"
+#include "nsINetworkLinkService.h"
 
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Hal.h"
 #include "prenv.h"
 
 #include "AndroidBridge.h"
@@ -517,16 +518,39 @@ nsAppShell::ProcessNextNativeEvent(bool 
             hal::ScreenConfiguration(rect, orientation, colorDepth, pixelDepth));
         break;
     }
 
     case AndroidGeckoEvent::CALL_OBSERVER:
         CallObserver(curEvent->Characters(), curEvent->CharactersExtra(), curEvent->Data());
         break;
 
+    case AndroidGeckoEvent::LOW_MEMORY:
+        // TODO hook in memory-reduction stuff for different levels here
+        if (curEvent->MetaState() >= AndroidGeckoEvent::MEMORY_PRESSURE_MEDIUM) {
+            nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+            if (os) {
+                os->NotifyObservers(nullptr,
+                                    "memory-pressure",
+                                    NS_LITERAL_STRING("low-memory").get());
+            }
+        }
+        break;
+
+    case AndroidGeckoEvent::NETWORK_LINK_CHANGE:
+    {
+        nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+        if (os) {
+            os->NotifyObservers(nullptr,
+                                NS_NETWORK_LINK_TOPIC,
+                                nsString(curEvent->Characters()).get());
+        }
+        break;
+    }
+
     case AndroidGeckoEvent::NOOP:
         break;
 
     default:
         nsWindow::OnGlobalAndroidEvent(curEvent);
         break;
     }
 
@@ -731,50 +755,16 @@ nsAppShell::CallObserver(const nsAString
 }
 
 void
 nsAppShell::RemoveObserver(const nsAString &aObserverKey)
 {
     mObserversHash.Remove(aObserverKey);
 }
 
-// NotifyObservers support.  NotifyObservers only works on main thread.
-
-class NotifyObserversCaller : public nsRunnable {
-public:
-    NotifyObserversCaller(nsISupports *aSupports,
-                          const char *aTopic, const PRUnichar *aData) :
-        mSupports(aSupports), mTopic(aTopic), mData(aData) {
-    }
-
-    NS_IMETHOD Run() {
-        nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-        if (os)
-            os->NotifyObservers(mSupports, mTopic.get(), mData.get());
-
-        return NS_OK;
-    }
-
-private:
-    nsCOMPtr<nsISupports> mSupports;
-    nsCString mTopic;
-    nsString mData;
-};
-
-void
-nsAppShell::NotifyObservers(nsISupports *aSupports,
-                            const char *aTopic,
-                            const PRUnichar *aData)
-{
-    // This isn't main thread, so post this to main thread
-    nsCOMPtr<nsIRunnable> caller =
-        new NotifyObserversCaller(aSupports, aTopic, aData);
-    NS_DispatchToMainThread(caller);
-}
-
 // Used by IPC code
 namespace mozilla {
 
 bool ProcessNextEvent()
 {
     return nsAppShell::gAppShell->ProcessNextNativeEvent(true) ? true : false;
 }
 
--- a/widget/android/nsAppShell.h
+++ b/widget/android/nsAppShell.h
@@ -44,17 +44,16 @@ public:
     virtual bool ProcessNextNativeEvent(bool mayWait);
 
     void PostEvent(mozilla::AndroidGeckoEvent *event);
     void OnResume();
 
     nsresult AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver);
     void CallObserver(const nsAString &aObserverKey, const nsAString &aTopic, const nsAString &aData);
     void RemoveObserver(const nsAString &aObserverKey);
-    void NotifyObservers(nsISupports *aSupports, const char *aTopic, const PRUnichar *aData);
     void ResendLastResizeEvent(nsWindow* aDest);
 
     void SetBrowserApp(nsIAndroidBrowserApp* aBrowserApp) {
         mBrowserApp = aBrowserApp;
     }
 
     void GetBrowserApp(nsIAndroidBrowserApp* *aBrowserApp) {
         *aBrowserApp = mBrowserApp;