Bug 674725 - Part AL - Store Android Cursors to keep track of created message lists. r=cjones
authorMounir Lamouri <mounir.lamouri@gmail.com>
Thu, 22 Dec 2011 23:16:22 +0100
changeset 85948 547e3a3d109ee4d1a250b0f08f5fe5220283612c
parent 85947 0da63ad71e41b11e0fd4c98ce52afa5f2db0429c
child 85949 3b95e40950156f9fa9298272bd5d65225975dd43
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs674725
milestone12.0a1
Bug 674725 - Part AL - Store Android Cursors to keep track of created message lists. r=cjones
embedding/android/GeckoAppShell.java
embedding/android/GeckoSmsManager.java
mozglue/android/APKOpen.cpp
widget/android/AndroidJNI.cpp
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -125,16 +125,18 @@ public class GeckoAppShell
     public static native int  saveMessageInSentbox(String aReceiver, String aBody, long aTimestamp);
     public static native void notifySmsSent(int aId, String aReceiver, String aBody, long aTimestamp, int aRequestId, long aProcessId);
     public static native void notifySmsDelivered(int aId, String aReceiver, String aBody, long aTimestamp);
     public static native void notifySmsSendFailed(int aError, int aRequestId, long aProcessId);
     public static native void notifyGetSms(int aId, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId, long aProcessId);
     public static native void notifyGetSmsFailed(int aError, int aRequestId, long aProcessId);
     public static native void notifySmsDeleted(boolean aDeleted, int aRequestId, long aProcessId);
     public static native void notifySmsDeleteFailed(int aError, int aRequestId, long aProcessId);
+    public static native void notifyNoMessageInList(int aRequestId, long aProcessId);
+    public static native void notifyListCreated(int aListId, int aMessageId, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId, long aProcessId);
 
     // A looper thread, accessed by GeckoAppShell.getHandler
     private static class LooperThread extends Thread {
         public SynchronousQueue<Handler> mHandlerQueue =
             new SynchronousQueue<Handler>();
         
         public void run() {
             Looper.prepare();
--- a/embedding/android/GeckoSmsManager.java
+++ b/embedding/android/GeckoSmsManager.java
@@ -247,16 +247,81 @@ class SmsIOThread extends Thread {
     Looper.prepare();
 
     mHandler = new Handler();
 
     Looper.loop();
   }
 }
 
+class MessagesListManager
+{
+  private static final MessagesListManager sInstance = new MessagesListManager();
+
+  public static MessagesListManager getInstance() {
+    return sInstance;
+  }
+
+  private ArrayList<Cursor> mCursors = new ArrayList<Cursor>(0);
+
+  public int add(Cursor aCursor) {
+    int size = mCursors.size();
+
+    for (int i=0; i<size; ++i) {
+      if (mCursors.get(i) == null) {
+        mCursors.set(i, aCursor);
+        return i;
+      }
+    }
+
+    mCursors.add(aCursor);
+    return size;
+  }
+
+  public Cursor get(int aId) {
+    if (aId < 0 || mCursors.size() <= aId) {
+      Log.e("GeckoSmsManager", "Trying to get an unknown list!");
+      return null;
+    }
+
+    Cursor cursor = mCursors.get(aId);
+    if (cursor == null) {
+      Log.e("GeckoSmsManager", "Trying to get an empty list!");
+    }
+
+    return cursor;
+  }
+
+  public void remove(int aId) {
+    if (aId < 0 || mCursors.size() <= aId) {
+      Log.e("GeckoSmsManager", "Trying to destroy an unknown list!");
+      return;
+    }
+
+    Cursor cursor = mCursors.set(aId, null);
+    if (cursor == null) {
+      Log.e("GeckoSmsManager", "Trying to destroy an empty list!");
+      return;
+    }
+
+    cursor.close();
+  }
+
+  public void clear() {
+    for (int i=0; i<mCursors.size(); ++i) {
+      Cursor c = mCursors.get(i);
+      if (c != null) {
+        c.close();
+      }
+    }
+
+    mCursors.clear();
+  }
+}
+
 public class GeckoSmsManager
   extends BroadcastReceiver
 {
   public final static String ACTION_SMS_RECEIVED  = "android.provider.Telephony.SMS_RECEIVED";
   public final static String ACTION_SMS_SENT      = "org.mozilla.gecko.SMS_SENT";
   public final static String ACTION_SMS_DELIVERED = "org.mozilla.gecko.SMS_DELIVERED";
 
   /*
@@ -685,18 +750,20 @@ public class GeckoSmsManager
         mReverse = aReverse;
         mRequestId = aRequestId;
         mProcessId = aProcessId;
       }
 
       @Override
       public void run() {
         class UnexpectedDeliveryStateException extends Exception { };
+        class InvalidTypeException extends Exception { }
 
-        Cursor c = null;
+        Cursor cursor = null;
+        boolean closeCursor = true;
 
         try {
           // TODO: should use the |selectionArgs| argument in |ContentResolver.query()|.
           ArrayList<String> restrictions = new ArrayList<String>();
 
           if (mStartDate != 0) {
             restrictions.add("date >= " + mStartDate);
           }
@@ -711,56 +778,83 @@ public class GeckoSmsManager
             for (int i=1; i<mNumbersCount; ++i) {
               numberRestriction += ", '" + mNumbers[i] + "'";
             }
             numberRestriction += ")";
 
             restrictions.add(numberRestriction);
           }
 
-          if (mDeliveryState != kDeliveryStateUnknown) {
-            if (mDeliveryState == kDeliveryStateSent) {
-              restrictions.add("type = " + kSmsTypeSentbox);
-            } else if (mDeliveryState == kDeliveryStateReceived) {
-              restrictions.add("type = " + kSmsTypeInbox);
-            } else {
-              throw new UnexpectedDeliveryStateException();
-            }
+          if (mDeliveryState == kDeliveryStateUnknown) {
+            restrictions.add("type IN ('" + kSmsTypeSentbox + "', '" + kSmsTypeInbox + "')");
+          } else if (mDeliveryState == kDeliveryStateSent) {
+            restrictions.add("type = " + kSmsTypeSentbox);
+          } else if (mDeliveryState == kDeliveryStateReceived) {
+            restrictions.add("type = " + kSmsTypeInbox);
+          } else {
+            throw new UnexpectedDeliveryStateException();
           }
 
           String restrictionText = restrictions.size() > 0 ? restrictions.get(0) : "";
 
           for (int i=1; i<restrictions.size(); ++i) {
             restrictionText += " AND " + restrictions.get(i);
           }
 
           ContentResolver cr = GeckoApp.surfaceView.getContext().getContentResolver();
-          c = cr.query(kSmsContentUri, kRequiredMessageRows, restrictionText, null,
-                       mReverse ? "date DESC" : "date ASC");
+          cursor = cr.query(kSmsContentUri, kRequiredMessageRows, restrictionText, null,
+                            mReverse ? "date DESC" : "date ASC");
+
+          if (cursor.getCount() == 0) {
+            GeckoAppShell.notifyNoMessageInList(mRequestId, mProcessId);
+            return;
+          }
+
+          cursor.moveToFirst();
+
+          int type = cursor.getInt(cursor.getColumnIndex("type"));
+          String sender = "";
+          String receiver = "";
 
-          // TODO: store the cursor
-          // TODO: create a list id
-          // TODO: send a notification
+          if (type == kSmsTypeInbox) {
+            sender = cursor.getString(cursor.getColumnIndex("address"));
+          } else if (type == kSmsTypeSentbox) {
+            receiver = cursor.getString(cursor.getColumnIndex("address"));
+          } else {
+            throw new UnexpectedDeliveryStateException();
+          }
+
+          int listId = MessagesListManager.getInstance().add(cursor);
+          closeCursor = false;
+          GeckoAppShell.notifyListCreated(listId,
+                                          cursor.getInt(cursor.getColumnIndex("_id")),
+                                          receiver, sender,
+                                          cursor.getString(cursor.getColumnIndex("body")),
+                                          cursor.getLong(cursor.getColumnIndex("date")),
+                                          mRequestId, mProcessId);
         } catch (UnexpectedDeliveryStateException e) {
           Log.e("GeckoSmsManager", "Unexcepted delivery state type: " + e);
           // TODO: send an error notification
         } catch (Exception e) {
           Log.e("GeckoSmsManager", "Error while trying to create a message list cursor: " + e);
           // TODO: send an error notification
         } finally {
-          // TODO: remove when close will be callable from the DOM side.
-          if (c != null) {
-            c.close();
+          // Close the cursor if MessagesListManager isn't taking care of it.
+          // We could also just check if it is in the MessagesListManager list but
+          // that would be less efficient.
+          if (cursor != null && closeCursor) {
+            cursor.close();
           }
         }
       }
     }
 
     if (!SmsIOThread.getInstance().execute(new CreateMessageListRunnable(aStartDate, aEndDate, aNumbers, aNumbersCount, aDeliveryState, aReverse, aRequestId, aProcessId))) {
       Log.e("GeckoSmsManager", "Failed to add CreateMessageListRunnable to the SmsIOThread");
       // TODO: send an error notification
     }
   }
 
   public static void shutdown() {
     SmsIOThread.getInstance().interrupt();
+    MessagesListManager.getInstance().clear();
   }
 }
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -247,16 +247,34 @@ Java_org_mozilla_gecko_GeckoAppShell_ ##
 typedef return_type (*name ## _t)(JNIEnv *, jclass, type1 one, type2 two, type3 three, type4 four, type5 five, type6 six, type7 seven); \
 static name ## _t f_ ## name; \
 extern "C" NS_EXPORT return_type JNICALL \
 Java_org_mozilla_gecko_GeckoAppShell_ ## name(JNIEnv *jenv, jclass jc, type1 one, type2 two, type3 three, type4 four, type5 five, type6 six, type7 seven) \
 { \
   return f_ ## name(jenv, jc, one, two, three, four, five, six, seven); \
 }
 
+#define SHELL_WRAPPER8(name,type1,type2,type3,type4,type5,type6,type7,type8) \
+typedef void (*name ## _t)(JNIEnv *, jclass, type1 one, type2 two, type3 three, type4 four, type5 five, type6 six, type7 seven, type8 eight); \
+static name ## _t f_ ## name; \
+extern "C" NS_EXPORT void JNICALL \
+Java_org_mozilla_gecko_GeckoAppShell_ ## name(JNIEnv *jenv, jclass jc, type1 one, type2 two, type3 three, type4 four, type5 five, type6 six, type7 seven, type8 eight) \
+{ \
+  f_ ## name(jenv, jc, one, two, three, four, five, six, seven, eight); \
+}
+
+#define SHELL_WRAPPER8_WITH_RETURN(name, return_type, type1, type2, type3, type4, type5, type6, type7, type8) \
+typedef return_type (*name ## _t)(JNIEnv *, jclass, type1 one, type2 two, type3 three, type4 four, type5 five, type6 six, type7 seven, type8 eight); \
+static name ## _t f_ ## name; \
+extern "C" NS_EXPORT return_type JNICALL \
+Java_org_mozilla_gecko_GeckoAppShell_ ## name(JNIEnv *jenv, jclass jc, type1 one, type2 two, type3 three, type4 four, type5 five, type6 six, type7 seven, type8 eight) \
+{ \
+  return f_ ## name(jenv, jc, one, two, three, four, five, six, seven, eight); \
+}
+
 SHELL_WRAPPER0(nativeInit)
 SHELL_WRAPPER1(nativeRun, jstring)
 SHELL_WRAPPER1(notifyGeckoOfEvent, jobject)
 SHELL_WRAPPER0(processNextNativeEvent)
 SHELL_WRAPPER1(setSurfaceView, jobject)
 SHELL_WRAPPER1(setSoftwareLayerClient, jobject)
 SHELL_WRAPPER0(onResume)
 SHELL_WRAPPER0(onLowMemory)
@@ -273,16 +291,18 @@ SHELL_WRAPPER0_WITH_RETURN(testDirectTex
 SHELL_WRAPPER3_WITH_RETURN(saveMessageInSentbox, jint, jstring, jstring, jlong);
 SHELL_WRAPPER6(notifySmsSent, jint, jstring, jstring, jlong, jint, jlong);
 SHELL_WRAPPER4(notifySmsDelivered, jint, jstring, jstring, jlong);
 SHELL_WRAPPER3(notifySmsSendFailed, jint, jint, jlong);
 SHELL_WRAPPER7(notifyGetSms, jint, jstring, jstring, jstring, jlong, jint, jlong);
 SHELL_WRAPPER3(notifyGetSmsFailed, jint, jint, jlong);
 SHELL_WRAPPER3(notifySmsDeleted, jboolean, jint, jlong);
 SHELL_WRAPPER3(notifySmsDeleteFailed, jint, jint, jlong);
+SHELL_WRAPPER2(notifyNoMessageInList, jint, jlong);
+SHELL_WRAPPER8(notifyListCreated, jint, jint, jstring, jstring, jstring, jlong, jint, jlong);
 
 static void * xul_handle = NULL;
 static time_t apk_mtime = 0;
 #ifdef DEBUG
 extern "C" int extractLibs = 1;
 #else
 extern "C" int extractLibs = 0;
 #endif
@@ -668,16 +688,18 @@ loadLibs(const char *apkName)
   GETFUNC(saveMessageInSentbox);
   GETFUNC(notifySmsSent);
   GETFUNC(notifySmsDelivered);
   GETFUNC(notifySmsSendFailed);
   GETFUNC(notifyGetSms);
   GETFUNC(notifyGetSmsFailed);
   GETFUNC(notifySmsDeleted);
   GETFUNC(notifySmsDeleteFailed);
+  GETFUNC(notifyNoMessageInList);
+  GETFUNC(notifyListCreated);
 #undef GETFUNC
   sStartupTimeline = (uint64_t *)__wrap_dlsym(xul_handle, "_ZN7mozilla15StartupTimeline16sStartupTimelineE");
   gettimeofday(&t1, 0);
   struct rusage usage2;
   getrusage(RUSAGE_THREAD, &usage2);
   __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %dms total, %dms user, %dms system, %d faults",
                       (t1.tv_sec - t0.tv_sec)*1000 + (t1.tv_usec - t0.tv_usec)/1000, 
                       (usage2.ru_utime.tv_sec - usage1.ru_utime.tv_sec)*1000 + (usage2.ru_utime.tv_usec - usage1.ru_utime.tv_usec)/1000,
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -94,16 +94,18 @@ extern "C" {
     NS_EXPORT PRInt32 JNICALL Java_org_mozilla_gecko_GeckoAppShell_saveMessageInSentbox(JNIEnv* jenv, jclass, jstring, jstring, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifySmsSent(JNIEnv* jenv, jclass, jint, jstring, jstring, jlong, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifySmsDelivered(JNIEnv* jenv, jclass, jint, jstring, jstring, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifySmsSendFailed(JNIEnv* jenv, jclass, jint, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGetSms(JNIEnv* jenv, jclass, jint, jstring, jstring, jstring, jlong, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGetSmsFailed(JNIEnv* jenv, jclass, jint, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifySmsDeleted(JNIEnv* jenv, jclass, jboolean, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifySmsDeleteFailed(JNIEnv* jenv, jclass, jint, jint, jlong);
+    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyNoMessageInList(JNIEnv* jenv, jclass, jint, jlong);
+    NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyListCreated(JNIEnv* jenv, jclass, jint, jint, jstring, jstring, jstring, jlong, jint, jlong);
 
 #ifdef MOZ_JAVA_COMPOSITOR
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_bindWidgetTexture(JNIEnv* jenv, jclass);
     NS_EXPORT bool JNICALL Java_org_mozilla_gecko_GeckoAppShell_testDirectTexture(JNIEnv* jenv, jclass);
 #endif
 }
 
 
@@ -631,16 +633,28 @@ Java_org_mozilla_gecko_GeckoAppShell_not
     };
 
 
     nsCOMPtr<nsIRunnable> runnable =
       new NotifySmsDeleteFailedRunnable(SmsRequest::ErrorType(aError), aRequestId, aProcessId);
     NS_DispatchToMainThread(runnable);
 }
 
+NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_notifyNoMessageInList(JNIEnv* jenv, jclass, jint, jlong)
+{
+  // TODO: implement
+}
+
+NS_EXPORT void JNICALL
+Java_org_mozilla_gecko_GeckoAppShell_notifyListCreated(JNIEnv* jenv, jclass, jint, jint, jstring, jstring, jstring, jlong, jint, jlong)
+{
+  // TODO: implement
+}
+
 #ifdef MOZ_JAVA_COMPOSITOR
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_bindWidgetTexture(JNIEnv* jenv, jclass)
 {
     nsWindow::BindToTexture();
 }