Bug 1197010 - Implement Android backend for createMessageCursor/createThreadCursor. r=snorp
authorReuben Morais <reuben.morais@gmail.com>
Tue, 06 Oct 2015 19:40:38 -0300
changeset 266632 92a91d5204883f53f72e1fe279a16d0f7d6cfe5f
parent 266631 90c5b8a1bce41c17376ebe813b468ec1b46895c6
child 266633 d91b3618f990fce14d8459fd2a6822ea6a4353c6
push id15577
push usercbook@mozilla.com
push dateThu, 08 Oct 2015 14:12:14 +0000
treeherderfx-team@741ee8870a8d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1197010
milestone44.0a1
Bug 1197010 - Implement Android backend for createMessageCursor/createThreadCursor. r=snorp
dom/mobilemessage/android/MobileMessageDatabaseService.cpp
dom/mobilemessage/android/MobileMessageDatabaseService.h
dom/mobilemessage/android/SmsManager.cpp
dom/mobilemessage/android/SmsManager.h
dom/mobilemessage/moz.build
mobile/android/b2gdroid/app/src/main/AndroidManifest.xml
mobile/android/b2gdroid/confvars.sh
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoSmsManager.java
mobile/android/base/SmsManager.java
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/AndroidJNI.cpp
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
--- a/dom/mobilemessage/android/MobileMessageDatabaseService.cpp
+++ b/dom/mobilemessage/android/MobileMessageDatabaseService.cpp
@@ -1,23 +1,30 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MobileMessageDatabaseService.h"
+
 #include "AndroidBridge.h"
+#include "SmsManager.h"
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 NS_IMPL_ISUPPORTS(MobileMessageDatabaseService, nsIMobileMessageDatabaseService)
 
+MobileMessageDatabaseService::MobileMessageDatabaseService()
+{
+  SmsManager::Init();
+}
+
 NS_IMETHODIMP
 MobileMessageDatabaseService::GetMessageMoz(int32_t aMessageId,
                                             nsIMobileMessageCallback* aRequest)
 {
   if (!AndroidBridge::Bridge()) {
     return NS_OK;
   }
 
@@ -55,34 +62,61 @@ MobileMessageDatabaseService::CreateMess
                                                   uint32_t aNumbersCount,
                                                   const nsAString& aDelivery,
                                                   bool aHasRead,
                                                   bool aRead,
                                                   bool aHasThreadId,
                                                   uint64_t aThreadId,
                                                   bool aReverse,
                                                   nsIMobileMessageCursorCallback* aCallback,
-                                                  nsICursorContinueCallback** aResult)
+                                                  nsICursorContinueCallback** aCursor)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  if (!AndroidBridge::Bridge()) {
+    *aCursor = nullptr;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsICursorContinueCallback> cursor =
+    AndroidBridge::Bridge()->CreateMessageCursor(aHasStartDate,
+                                                 aStartDate,
+                                                 aHasEndDate,
+                                                 aEndDate,
+                                                 aNumbers,
+                                                 aNumbersCount,
+                                                 aDelivery,
+                                                 aHasRead,
+                                                 aRead,
+                                                 aHasThreadId,
+                                                 aThreadId,
+                                                 aReverse,
+                                                 aCallback);
+  cursor.forget(aCursor);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 MobileMessageDatabaseService::MarkMessageRead(int32_t aMessageId,
                                               bool aValue,
                                               bool aSendReadReport,
                                               nsIMobileMessageCallback* aRequest)
 {
   // TODO: This would need to be implemented as part of Bug 748391
   return NS_OK;
 }
 
 NS_IMETHODIMP
-MobileMessageDatabaseService::CreateThreadCursor(nsIMobileMessageCursorCallback* aCallback,
-                                                 nsICursorContinueCallback** aResult)
+MobileMessageDatabaseService::CreateThreadCursor(nsIMobileMessageCursorCallback* aRequest,
+                                                 nsICursorContinueCallback** aCursor)
 {
-  NS_NOTYETIMPLEMENTED("Implement me!");
-  return NS_ERROR_NOT_IMPLEMENTED;
+  if (!AndroidBridge::Bridge()) {
+    *aCursor = nullptr;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsICursorContinueCallback> cursor =
+    AndroidBridge::Bridge()->CreateThreadCursor(aRequest);
+  cursor.forget(aCursor);
+  return NS_OK;
 }
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
--- a/dom/mobilemessage/android/MobileMessageDatabaseService.h
+++ b/dom/mobilemessage/android/MobileMessageDatabaseService.h
@@ -15,16 +15,18 @@ namespace dom {
 namespace mobilemessage {
 
 class MobileMessageDatabaseService final : public nsIMobileMessageDatabaseService
 {
 private:
   ~MobileMessageDatabaseService() {}
 
 public:
+  MobileMessageDatabaseService();
+
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMOBILEMESSAGEDATABASESERVICE
 };
 
 } // namespace mobilemessage
 } // namespace dom
 } // namespace mozilla
 
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/android/SmsManager.cpp
@@ -0,0 +1,397 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SmsManager.h"
+
+#include "mozilla/dom/mobilemessage/Constants.h"
+#include "mozilla/dom/mobilemessage/PSms.h"
+#include "mozilla/dom/mobilemessage/SmsParent.h"
+#include "mozilla/dom/mobilemessage/SmsTypes.h"
+#include "mozilla/dom/mobilemessage/Types.h"
+#include "mozilla/dom/MobileMessageThread.h"
+#include "mozilla/dom/SmsMessage.h"
+#include "mozilla/Services.h"
+#include "nsIMobileMessageDatabaseService.h"
+#include "nsIObserverService.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::dom::mobilemessage;
+
+namespace mozilla {
+
+/*static*/
+void
+SmsManager::NotifySmsReceived(jni::String::Param aSender,
+                              jni::String::Param aBody,
+                              int32_t aMessageClass,
+                              int64_t aTimestamp)
+{
+    // TODO Need to correct the message `threadId` parameter value. Bug 859098
+    SmsMessageData message;
+    message.id() = 0;
+    message.threadId() = 0;
+    message.iccId() = EmptyString();
+    message.delivery() = eDeliveryState_Received;
+    message.deliveryStatus() = eDeliveryStatus_Success;
+    message.sender() = aSender ? nsString(aSender) : EmptyString();
+    message.receiver() = EmptyString();
+    message.body() = aBody ? nsString(aBody) : EmptyString();
+    message.messageClass() = static_cast<MessageClass>(aMessageClass);
+    message.timestamp() = aTimestamp;
+    message.sentTimestamp() = aTimestamp;
+    message.deliveryTimestamp() = aTimestamp;
+    message.read() = false;
+
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=] () {
+        nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+        if (!obs) {
+            return;
+        }
+
+        nsCOMPtr<nsIDOMMozSmsMessage> domMessage = new SmsMessage(message);
+        obs->NotifyObservers(domMessage, kSmsReceivedObserverTopic, nullptr);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifySmsSent(int32_t aId,
+                          jni::String::Param aReceiver,
+                          jni::String::Param aBody,
+                          int64_t aTimestamp,
+                          int32_t aRequestId)
+{
+    // TODO Need to add the message `messageClass` parameter value. Bug 804476
+    // TODO Need to correct the message `threadId` parameter value. Bug 859098
+    SmsMessageData message;
+    message.id() = aId;
+    message.threadId() = 0;
+    message.iccId() = EmptyString();
+    message.delivery() = eDeliveryState_Sent;
+    message.deliveryStatus() = eDeliveryStatus_Pending;
+    message.sender() = EmptyString();
+    message.receiver() = aReceiver ? nsString(aReceiver) : EmptyString();
+    message.body() = aBody ? nsString(aBody) : EmptyString();
+    message.messageClass() = eMessageClass_Normal;
+    message.timestamp() = aTimestamp;
+    message.sentTimestamp() = aTimestamp;
+    message.deliveryTimestamp() = aTimestamp;
+    message.read() = true;
+
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        /*
+         * First, we are going to notify all SmsManager that a message has
+         * been sent. Then, we will notify the SmsRequest object about it.
+         */
+        nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+        if (!obs) {
+            return;
+        }
+
+        nsCOMPtr<nsIDOMMozSmsMessage> domMessage = new SmsMessage(message);
+        obs->NotifyObservers(domMessage, kSmsSentObserverTopic, nullptr);
+
+        nsCOMPtr<nsIMobileMessageCallback> request =
+            AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId);
+        if (!request) {
+            return;
+        }
+
+        request->NotifyMessageSent(domMessage);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifySmsDelivery(int32_t aId,
+                              int32_t aDeliveryStatus,
+                              jni::String::Param aReceiver,
+                              jni::String::Param aBody,
+                              int64_t aTimestamp)
+{
+    // TODO Need to add the message `messageClass` parameter value. Bug 804476
+    // TODO Need to correct the message `threadId` parameter value. Bug 859098
+    SmsMessageData message;
+    message.id() = aId;
+    message.threadId() = 0;
+    message.iccId() = EmptyString();
+    message.delivery() = eDeliveryState_Sent;
+    message.deliveryStatus() = static_cast<DeliveryStatus>(aDeliveryStatus);
+    message.sender() = EmptyString();
+    message.receiver() = aReceiver ? nsString(aReceiver) : EmptyString();
+    message.body() = aBody ? nsString(aBody) : EmptyString();
+    message.messageClass() = eMessageClass_Normal;
+    message.timestamp() = aTimestamp;
+    message.sentTimestamp() = aTimestamp;
+    message.deliveryTimestamp() = aTimestamp;
+    message.read() = true;
+
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+        if (!obs) {
+            return;
+        }
+
+        nsCOMPtr<nsIDOMMozSmsMessage> domMessage = new SmsMessage(message);
+        const char* topic = (message.deliveryStatus() == eDeliveryStatus_Success)
+                            ? kSmsDeliverySuccessObserverTopic
+                            : kSmsDeliveryErrorObserverTopic;
+        obs->NotifyObservers(domMessage, topic, nullptr);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifySmsSendFailed(int32_t aError, int32_t aRequestId)
+{
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIMobileMessageCallback> request =
+            AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId);
+        if(!request) {
+            return;
+        }
+
+        request->NotifySendMessageFailed(aError, nullptr);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifyGetSms(int32_t aId,
+                         int32_t aDeliveryStatus,
+                         jni::String::Param aReceiver,
+                         jni::String::Param aSender,
+                         jni::String::Param aBody,
+                         int64_t aTimestamp,
+                         bool aRead,
+                         int32_t aRequestId)
+{
+    nsString receiver(aReceiver);
+    DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received
+                                             : eDeliveryState_Sent;
+
+    // TODO Need to add the message `messageClass` parameter value. Bug 804476
+    // TODO Need to correct the message `threadId` parameter value. Bug 859098
+    SmsMessageData message;
+    message.id() = aId;
+    message.threadId() = 0;
+    message.iccId() = EmptyString();
+    message.delivery() = state;
+    message.deliveryStatus() = static_cast<DeliveryStatus>(aDeliveryStatus);
+    message.sender() = aSender ? nsString(aSender) : EmptyString();
+    message.receiver() = receiver;
+    message.body() = aBody ? nsString(aBody) : EmptyString();
+    message.messageClass() = eMessageClass_Normal;
+    message.timestamp() = aTimestamp;
+    message.sentTimestamp() = aTimestamp;
+    message.deliveryTimestamp() = aTimestamp;
+    message.read() = aRead;
+
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIMobileMessageCallback> request =
+            AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId);
+        if (!request) {
+            return;
+        }
+
+        nsCOMPtr<nsIDOMMozSmsMessage> domMessage = new SmsMessage(message);
+        request->NotifyMessageGot(domMessage);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifyGetSmsFailed(int32_t aError, int32_t aRequestId)
+{
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIMobileMessageCallback> request =
+            AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId);
+        if (!request) {
+            return;
+        }
+
+        request->NotifyGetMessageFailed(aError);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifySmsDeleted(bool aDeleted, int32_t aRequestId)
+{
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIMobileMessageCallback> request =
+            AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId);
+        if (!request) {
+            return;
+        }
+
+        // For android, we support only single SMS deletion.
+        bool deleted = aDeleted;
+        request->NotifyMessageDeleted(&deleted, 1);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifySmsDeleteFailed(int32_t aError, int32_t aRequestId)
+{
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIMobileMessageCallback> request =
+            AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId);
+        if (!request) {
+            return;
+        }
+
+        request->NotifyDeleteMessageFailed(aError);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifyCursorError(int32_t aError, int32_t aRequestId)
+{
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIMobileMessageCursorCallback> request =
+            AndroidBridge::Bridge()->DequeueSmsCursorRequest(aRequestId);
+        if (!request) {
+            return;
+        }
+
+        request->NotifyCursorError(aError);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifyThreadCursorResult(int64_t aId,
+                                     jni::String::Param aLastMessageSubject,
+                                     jni::String::Param aBody,
+                                     int64_t aUnreadCount,
+                                     jni::ObjectArray::Param aParticipants,
+                                     int64_t aTimestamp,
+                                     jni::String::Param aLastMessageType,
+                                     int32_t aRequestId)
+{
+    ThreadData thread;
+    thread.id() = aId;
+    thread.lastMessageSubject() = aLastMessageSubject ?
+                                    nsString(aLastMessageSubject) :
+                                    EmptyString();
+    thread.body() = aBody ? nsString(aBody) : EmptyString();
+    thread.unreadCount() = aUnreadCount;
+    thread.timestamp() = aTimestamp;
+    thread.lastMessageType() = eMessageType_SMS;
+
+    JNIEnv* const env = jni::GetEnvForThread();
+
+    jobjectArray participants = aParticipants.Get();
+    jsize length = env->GetArrayLength(participants);
+    for (jsize i = 0; i < length; ++i) {
+        jstring participant =
+            static_cast<jstring>(env->GetObjectArrayElement(participants, i));
+        if (participant) {
+            thread.participants().AppendElement(nsJNIString(participant, env));
+        }
+    }
+
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIMobileMessageCursorCallback> request =
+            AndroidBridge::Bridge()->GetSmsCursorRequest(aRequestId);
+        if (!request) {
+            return;
+        }
+
+        nsCOMArray<nsIDOMMozMobileMessageThread> arr;
+        arr.AppendElement(new MobileMessageThread(thread));
+
+        nsIDOMMozMobileMessageThread** elements;
+        int32_t size;
+        size = arr.Forget(&elements);
+
+        request->NotifyCursorResult(reinterpret_cast<nsISupports**>(elements),
+                                    size);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifyMessageCursorResult(int32_t aMessageId,
+                                      int32_t aDeliveryStatus,
+                                      jni::String::Param aReceiver,
+                                      jni::String::Param aSender,
+                                      jni::String::Param aBody,
+                                      int64_t aTimestamp,
+                                      int64_t aThreadId,
+                                      bool aRead,
+                                      int32_t aRequestId)
+{
+    nsString receiver = nsString(aReceiver);
+    DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received
+                                             : eDeliveryState_Sent;
+
+    // TODO Need to add the message `messageClass` parameter value. Bug 804476
+    SmsMessageData message;
+    message.id() = aMessageId;
+    message.threadId() = aThreadId;
+    message.iccId() = EmptyString();
+    message.delivery() = state;
+    message.deliveryStatus() = static_cast<DeliveryStatus>(aDeliveryStatus);
+    message.sender() = aSender ? nsString(aSender) : EmptyString();
+    message.receiver() = receiver;
+    message.body() = aBody ? nsString(aBody) : EmptyString();
+    message.messageClass() = eMessageClass_Normal;
+    message.timestamp() = aTimestamp;
+    message.sentTimestamp() = aTimestamp;
+    message.deliveryTimestamp() = aTimestamp;
+    message.read() = aRead;
+
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIMobileMessageCursorCallback> request =
+            AndroidBridge::Bridge()->GetSmsCursorRequest(aRequestId);
+        if (!request) {
+            return;
+        }
+
+        nsCOMArray<nsIDOMMozSmsMessage> arr;
+        arr.AppendElement(new SmsMessage(message));
+
+        nsIDOMMozSmsMessage** elements;
+        int32_t size;
+        size = arr.Forget(&elements);
+
+        request->NotifyCursorResult(reinterpret_cast<nsISupports**>(elements),
+                                    size);
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+/*static*/
+void
+SmsManager::NotifyCursorDone(int32_t aRequestId)
+{
+    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([=]() {
+        nsCOMPtr<nsIMobileMessageCursorCallback> request =
+            AndroidBridge::Bridge()->DequeueSmsCursorRequest(aRequestId);
+        if (!request) {
+            return;
+        }
+
+        request->NotifyCursorDone();
+    });
+    NS_DispatchToMainThread(runnable);
+}
+
+} // namespace
+
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/android/SmsManager.h
@@ -0,0 +1,73 @@
+/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SmsManager_h__
+#define SmsManager_h__
+
+#include "GeneratedJNINatives.h"
+
+namespace mozilla {
+
+class SmsManager : public widget::GeckoSmsManager::Natives<SmsManager>
+{
+private:
+    SmsManager();
+
+public:
+    static void NotifySmsReceived(jni::String::Param aSender,
+                                  jni::String::Param aBody,
+                                  int32_t aMessageClass,
+                                  int64_t aTimestamp);
+    static void NotifySmsSent(int32_t aId,
+                              jni::String::Param aReceiver,
+                              jni::String::Param aBody,
+                              int64_t aTimestamp,
+                              int32_t aRequestId);
+    static void NotifySmsDelivery(int32_t aId,
+                                  int32_t aDeliveryStatus,
+                                  jni::String::Param aReceiver,
+                                  jni::String::Param aBody,
+                                  int64_t aTimestamp);
+    static void NotifySmsSendFailed(int32_t aError,
+                                    int32_t aRequestId);
+    static void NotifyGetSms(int32_t aId,
+                             int32_t aDeliveryStatus,
+                             jni::String::Param aReceiver,
+                             jni::String::Param aSender,
+                             jni::String::Param aBody,
+                             int64_t aTimestamp,
+                             bool aRead,
+                             int32_t aRequestId);
+    static void NotifyGetSmsFailed(int32_t aError,
+                                   int32_t aRequestId);
+    static void NotifySmsDeleted(bool aDeleted,
+                                 int32_t aRequestId);
+    static void NotifySmsDeleteFailed(int32_t aError,
+                                      int32_t aRequestId);
+    static void NotifyCursorError(int32_t aError,
+                                  int32_t aRequestId);
+    static void NotifyThreadCursorResult(int64_t aId,
+                                         jni::String::Param aLastMessageSubject,
+                                         jni::String::Param aBody,
+                                         int64_t aUnreadCount,
+                                         jni::ObjectArray::Param aParticipants,
+                                         int64_t aTimestamp,
+                                         jni::String::Param aLastMessageType,
+                                         int32_t aRequestId);
+    static void NotifyMessageCursorResult(int32_t aMessageId,
+                                          int32_t aDeliveryStatus,
+                                          jni::String::Param aReceiver,
+                                          jni::String::Param aSender,
+                                          jni::String::Param aBody,
+                                          int64_t aTimestamp,
+                                          int64_t aThreadId,
+                                          bool aRead,
+                                          int32_t aRequestId);
+    static void NotifyCursorDone(int32_t aRequestId);
+};
+
+} // namespace
+
+#endif // SmsManager_h__
--- a/dom/mobilemessage/moz.build
+++ b/dom/mobilemessage/moz.build
@@ -14,16 +14,17 @@ EXPORTS.mozilla.dom.mobilemessage += [
     'ipc/SmsChild.h',
     'ipc/SmsParent.h',
     'Types.h',                # Required by IPDL SmsTypes.h
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     SOURCES += [
         'android/MobileMessageDatabaseService.cpp',
+        'android/SmsManager.cpp',
         'android/SmsService.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
     EXTRA_JS_MODULES += [
         'gonk/mms_consts.js',
         'gonk/MmsPduHelper.jsm',
         'gonk/MobileMessageDB.jsm',
         'gonk/SmsSegmentHelper.jsm',
@@ -41,16 +42,17 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'go
             'gonk/SmsService.js',
             'gonk/SmsService.manifest',
         ]
 
 EXPORTS.mozilla.dom += [
     'DOMMobileMessageError.h',
     'MmsMessage.h',
     'MobileMessageManager.h',
+    'MobileMessageThread.h',
     'SmsMessage.h',
 ]
 
 UNIFIED_SOURCES += [
     'Assertions.cpp',
     'Constants.cpp',
     'DeletedMessageInfo.cpp',
     'DOMMobileMessageError.cpp',
@@ -70,13 +72,14 @@ IPDL_SOURCES += [
     'ipc/PMobileMessageCursor.ipdl',
     'ipc/PSms.ipdl',
     'ipc/PSmsRequest.ipdl',
     'ipc/SmsTypes.ipdlh',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base',
+    '/widget/android',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/mobile/android/b2gdroid/app/src/main/AndroidManifest.xml
+++ b/mobile/android/b2gdroid/app/src/main/AndroidManifest.xml
@@ -23,16 +23,24 @@
     <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
     <uses-feature android:name="android.hardware.touchscreen"/>
 
     <!-- Contacts API -->
     <uses-permission android:name="android.permission.READ_CONTACTS"/>
     <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
     <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
 
+    <!-- WebSMS -->
+    <uses-permission android:name="android.permission.SEND_SMS"/>
+    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
+    <uses-permission android:name="android.permission.WRITE_SMS"/>
+    <uses-permission android:name="android.permission.READ_SMS"/>
+
+    <uses-feature android:name="android.hardware.telephony"/>
+
     <!-- Tab Queue -->
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
 
     <!-- Android Beam support -->
     <uses-permission android:name="android.permission.NFC"/>
     <uses-feature android:name="android.hardware.nfc" android:required="false"/>
 
     <uses-permission android:name="android.permission.RECORD_AUDIO"/>
--- a/mobile/android/b2gdroid/confvars.sh
+++ b/mobile/android/b2gdroid/confvars.sh
@@ -117,8 +117,10 @@ if test "$MOZ_OFFICIAL_BRANDING"; then
   if test "$MOZ_UPDATE_CHANNEL" = "beta" -o \
           "$MOZ_UPDATE_CHANNEL" = "release"; then
     MOZ_REQUIRE_SIGNING=1
   fi
 fi
 
 MOZ_JSDOWNLOADS=1
 MOZ_TIME_MANAGER=1
+MOZ_WEBSMS_BACKEND=1
+
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -42,16 +42,18 @@
     <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
 #endif
 #ifdef MOZ_WEBSMS_BACKEND
     <!-- WebSMS -->
     <uses-permission android:name="android.permission.SEND_SMS"/>
     <uses-permission android:name="android.permission.RECEIVE_SMS"/>
     <uses-permission android:name="android.permission.WRITE_SMS"/>
     <uses-permission android:name="android.permission.READ_SMS"/>
+
+    <uses-feature android:name="android.hardware.telephony"/>
 #endif
 
     <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"/>
 
 #ifdef NIGHTLY_BUILD
     <!-- Contacts API -->
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -2376,41 +2376,52 @@ public class GeckoAppShell
     public static void deleteMessage(int aMessageId, int aRequestId) {
         if (!SmsManager.isEnabled()) {
             return;
         }
 
         SmsManager.getInstance().deleteMessage(aMessageId, aRequestId);
     }
 
-    @WrapForJNI(stubName = "CreateMessageListWrapper")
-    public static void createMessageList(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, String aDelivery, boolean aHasRead, boolean aRead, long aThreadId, boolean aReverse, int aRequestId) {
+    @WrapForJNI(stubName = "CreateMessageCursorWrapper")
+    public static void createMessageCursor(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, String aDelivery, boolean aHasRead, boolean aRead, boolean aHasThreadId, long aThreadId, boolean aReverse, int aRequestId) {
+        if (!SmsManager.isEnabled()) {
+            return;
+        }
+
+        SmsManager.getInstance().createMessageCursor(aStartDate, aEndDate, aNumbers, aNumbersCount, aDelivery, aHasRead, aRead, aHasThreadId, aThreadId, aReverse, aRequestId);
+    }
+
+    @WrapForJNI(stubName = "GetNextMessageWrapper")
+    public static void getNextMessage(int aRequestId) {
         if (!SmsManager.isEnabled()) {
             return;
         }
 
-        SmsManager.getInstance().createMessageList(aStartDate, aEndDate, aNumbers, aNumbersCount, aDelivery, aHasRead, aRead, aThreadId, aReverse, aRequestId);
+        SmsManager.getInstance().getNextMessage(aRequestId);
     }
 
-    @WrapForJNI(stubName = "GetNextMessageInListWrapper")
-    public static void getNextMessageInList(int aListId, int aRequestId) {
+    @WrapForJNI(stubName = "CreateThreadCursorWrapper")
+    public static void createThreadCursor(int aRequestId) {
+        Log.i("GeckoAppShell", "CreateThreadCursorWrapper!");
+
         if (!SmsManager.isEnabled()) {
             return;
         }
 
-        SmsManager.getInstance().getNextMessageInList(aListId, aRequestId);
+        SmsManager.getInstance().createThreadCursor(aRequestId);
     }
 
-    @WrapForJNI
-    public static void clearMessageList(int aListId) {
+    @WrapForJNI(stubName = "GetNextThreadWrapper")
+    public static void getNextThread(int aRequestId) {
         if (!SmsManager.isEnabled()) {
             return;
         }
 
-        SmsManager.getInstance().clearMessageList(aListId);
+        SmsManager.getInstance().getNextThread(aRequestId);
     }
 
     /* Called by JNI from AndroidBridge, and by reflection from tests/BaseTest.java.in */
     @WrapForJNI
     @RobocopTarget
     public static boolean isTablet() {
         return HardwareUtils.isTablet();
     }
--- a/mobile/android/base/GeckoSmsManager.java
+++ b/mobile/android/base/GeckoSmsManager.java
@@ -1,37 +1,45 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
+import org.mozilla.gecko.annotation.WrapForJNI;
+
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Looper;
+import android.os.HandlerThread;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
 import android.util.Log;
 
 import static android.telephony.SmsMessage.MessageClass;
 import static org.mozilla.gecko.SmsManager.ISmsManager;
 
 import java.util.ArrayList;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * The envelope class contains all information that are needed to keep track of
  * a sent SMS.
  */
 class Envelope
 {
@@ -178,98 +186,82 @@ class Postman
     }
 
     if (mEnvelopes.set(aId, null) == null) {
       Log.e("GeckoSmsManager", "Trying to destroy an empty Envelope!");
     }
   }
 }
 
-class SmsIOThread extends Thread {
+class SmsIOThread extends HandlerThread {
   private final static SmsIOThread sInstance = new SmsIOThread();
 
   private Handler mHandler;
 
   public static SmsIOThread getInstance() {
     return sInstance;
   }
 
-  public boolean execute(Runnable r) {
-    return mHandler.post(r);
+  SmsIOThread() {
+    super("SmsIOThread");
   }
 
   @Override
-  public void run() {
-    Looper.prepare();
+  public void start() {
+    super.start();
+    mHandler = new Handler(getLooper());
+  }
 
-    mHandler = new Handler();
-
-    Looper.loop();
+  public boolean execute(Runnable r) {
+    return mHandler.post(r);
   }
 }
 
 class MessagesListManager
 {
   private static final MessagesListManager sInstance = new MessagesListManager();
 
   public static MessagesListManager getInstance() {
     return sInstance;
   }
 
-  private final ArrayList<Cursor> mCursors = new ArrayList<>();
-
-  public int add(Cursor aCursor) {
-    int size = mCursors.size();
+  private final HashMap<Integer, Cursor> mCursors = new HashMap<>();
 
-    for (int i=0; i<size; ++i) {
-      if (mCursors.get(i) == null) {
-        mCursors.set(i, aCursor);
-        return i;
-      }
+  public void add(int id, Cursor aCursor) {
+    if (mCursors.containsKey(id)) {
+      Log.e("GeckoSmsManager", "Trying to overwrite cursor!");
+      return;
     }
 
-    mCursors.add(aCursor);
-    return size;
+    mCursors.put(id, aCursor);
   }
 
   public Cursor get(int aId) {
-    if (aId < 0 || mCursors.size() <= aId) {
-      Log.e("GeckoSmsManager", "Trying to get an unknown list!");
+    if (!mCursors.containsKey(aId)) {
+      Log.e("GeckoSmsManager", "Cursor doesn't exist!");
       return null;
     }
 
-    Cursor cursor = mCursors.get(aId);
-    if (cursor == null) {
-      Log.e("GeckoSmsManager", "Trying to get an empty list!");
-    }
-
-    return cursor;
+    return mCursors.get(aId);
   }
 
   public void remove(int aId) {
-    if (aId < 0 || mCursors.size() <= aId) {
-      Log.e("GeckoSmsManager", "Trying to destroy an unknown list!");
+    if (!mCursors.containsKey(aId)) {
+      Log.e("GeckoSmsManager", "Cursor doesn't exist!");
       return;
     }
 
-    Cursor cursor = mCursors.set(aId, null);
-    if (cursor == null) {
-      Log.e("GeckoSmsManager", "Trying to destroy an empty list!");
-      return;
-    }
-
-    cursor.close();
+    mCursors.remove(aId);
   }
 
   public void clear() {
-    for (int i=0; i<mCursors.size(); ++i) {
-      Cursor c = mCursors.get(i);
-      if (c != null) {
-        c.close();
-      }
+    Set<Map.Entry<Integer, Cursor>> entries = mCursors.entrySet();
+    Iterator<Map.Entry<Integer, Cursor>> it = entries.iterator();
+    while (it.hasNext()) {
+      it.next().getValue().close();
     }
 
     mCursors.clear();
   }
 }
 
 public class GeckoSmsManager
   extends BroadcastReceiver
@@ -277,17 +269,17 @@ public class GeckoSmsManager
 {
   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";
 
   /*
    * Make sure that the following error codes are in sync with |ErrorType| in:
    * dom/mobilemessage/interfaces/nsIMobileMessageCallback.idl
-   * The error code are owned by the DOM.
+   * The error codes are owned by the DOM.
    */
   public final static int kNoError               = 0;
   public final static int kNoSignalError         = 1;
   public final static int kNotFoundError         = 2;
   public final static int kUnknownError          = 3;
   public final static int kInternalError         = 4;
   public final static int kNoSimCardError        = 5;
   public final static int kRadioDisabledError    = 6;
@@ -300,18 +292,19 @@ public class GeckoSmsManager
   public final static int kGeneralProblemsError = 13;
   public final static int kServiceNotAvailableError      = 14;
   public final static int kMessageTooLongForNetworkError = 15;
   public final static int kServiceNotSupportedError      = 16;
   public final static int kRetryRequiredError   = 17;
 
   private final static int kMaxMessageSize    = 160;
 
-  private final static Uri kSmsContentUri     = Uri.parse("content://sms");
-  private final static Uri kSmsSentContentUri = Uri.parse("content://sms/sent");
+  private final static Uri kSmsContentUri        = Uri.parse("content://sms");
+  private final static Uri kSmsSentContentUri    = Uri.parse("content://sms/sent");
+  private final static Uri kSmsThreadsContentUri = Uri.parse("content://sms/conversations");
 
   private final static int kSmsTypeInbox      = 1;
   private final static int kSmsTypeSentbox    = 2;
 
   /*
    * Keep the following state codes in syng with |DeliveryState| in:
    * dom/mobilemessage/Types.h
    */
@@ -346,17 +339,19 @@ public class GeckoSmsManager
    * dom/mobilemessage/Types.h
    */
   private final static int kMessageClassNormal  = 0;
   private final static int kMessageClassClass0  = 1;
   private final static int kMessageClassClass1  = 2;
   private final static int kMessageClassClass2  = 3;
   private final static int kMessageClassClass3  = 4;
 
-  private final static String[] kRequiredMessageRows = { "_id", "address", "body", "date", "type", "status" };
+  private final static String[] kRequiredMessageRows = { "_id", "address", "body", "date", "type", "status", "read", "thread_id" };
+  private final static String[] kRequiredMessageRowsForThread = { "_id", "address", "body", "read", "subject", "date" };
+  private final static String[] kThreadProjection = { "thread_id" };
 
   // Used to generate monotonically increasing GUIDs.
   private static final AtomicInteger pendingIntentGuid = new AtomicInteger(Integer.MIN_VALUE);
 
   // The maximum value of a 32 bit signed integer. Used to enforce a limit on ids.
   private static final long UNSIGNED_INTEGER_MAX_VALUE = Integer.MAX_VALUE * 2L + 1L;
 
   public GeckoSmsManager() {
@@ -655,21 +650,24 @@ public class GeckoSmsManager
             sender = cursor.getString(cursor.getColumnIndex("address"));
           } else if (type == kSmsTypeSentbox) {
             deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status")));
             receiver = cursor.getString(cursor.getColumnIndex("address"));
           } else {
             throw new InvalidTypeException();
           }
 
+          boolean read = cursor.getInt(cursor.getColumnIndex("read")) != 0;
+
           notifyGetSms(cursor.getInt(cursor.getColumnIndex("_id")),
                        deliveryStatus,
                        receiver, sender,
                        cursor.getString(cursor.getColumnIndex("body")),
                        cursor.getLong(cursor.getColumnIndex("date")),
+                       read,
                        mRequestId);
         } catch (NotFoundException e) {
           Log.i("GeckoSmsManager", "Message id " + mMessageId + " not found");
           notifyGetSmsFailed(kNotFoundError, mRequestId);
         } catch (UnmatchingIdException e) {
           Log.e("GeckoSmsManager", "Requested message id (" + mMessageId +
                                    ") is different from the one we got.");
           notifyGetSmsFailed(kUnknownError, mRequestId);
@@ -731,206 +729,305 @@ public class GeckoSmsManager
     }
 
     if (!SmsIOThread.getInstance().execute(new DeleteMessageRunnable(aMessageId, aRequestId))) {
       Log.e("GeckoSmsManager", "Failed to add GetMessageRunnable to the SmsIOThread");
       notifySmsDeleteFailed(kUnknownError, aRequestId);
     }
   }
 
+  private void getMessageFromCursorAndNotify(Cursor aCursor, int aRequestId) throws Exception {
+    int type = aCursor.getInt(aCursor.getColumnIndex("type"));
+    int deliveryStatus = kDeliveryStateUnknown;
+    String sender = "";
+    String receiver = "";
+
+    if (type == kSmsTypeInbox) {
+      deliveryStatus = kDeliveryStatusSuccess;
+      sender = aCursor.getString(aCursor.getColumnIndex("address"));
+    } else if (type == kSmsTypeSentbox) {
+      deliveryStatus = getGeckoDeliveryStatus(aCursor.getInt(aCursor.getColumnIndex("status")));
+      receiver = aCursor.getString(aCursor.getColumnIndex("address"));
+    } else {
+      throw new Exception("Shouldn't ever get a message here that's not from inbox or sentbox");
+    }
+
+    boolean read = aCursor.getInt(aCursor.getColumnIndex("read")) != 0;
+
+    notifyMessageCursorResult(aCursor.getInt(aCursor.getColumnIndex("_id")),
+                              deliveryStatus,
+                              receiver, sender,
+                              aCursor.getString(aCursor.getColumnIndex("body")),
+                              aCursor.getLong(aCursor.getColumnIndex("date")),
+                              aCursor.getLong(aCursor.getColumnIndex("thread_id")),
+                              read,
+                              aRequestId);
+  }
+
   @Override
-  public void createMessageList(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, String aDelivery, boolean aHasRead, boolean aRead, long aThreadId, boolean aReverse, int aRequestId) {
-    class CreateMessageListRunnable implements Runnable {
+  public void createMessageCursor(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, String aDelivery, boolean aHasRead, boolean aRead, boolean aHasThreadId, long aThreadId, boolean aReverse, int aRequestId) {
+    class CreateMessageCursorRunnable implements Runnable {
       private final long     mStartDate;
       private final long     mEndDate;
       private final String[] mNumbers;
       private final int      mNumbersCount;
       private final String   mDelivery;
+      private final boolean  mHasThreadId;
+      private final long     mThreadId;
       private final boolean  mReverse;
       private final int      mRequestId;
 
-      CreateMessageListRunnable(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, String aDelivery, boolean aHasRead, boolean aRead, long aThreadId, boolean aReverse, int aRequestId) {
+      CreateMessageCursorRunnable(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, String aDelivery, boolean aHasRead, boolean aRead, boolean aHasThreadId, long aThreadId, boolean aReverse, int aRequestId) {
         mStartDate = aStartDate;
         mEndDate = aEndDate;
         mNumbers = aNumbers;
         mNumbersCount = aNumbersCount;
         mDelivery = aDelivery;
+        mHasThreadId = aHasThreadId;
+        mThreadId = aThreadId;
         mReverse = aReverse;
         mRequestId = aRequestId;
       }
 
       @Override
       public void run() {
         Cursor cursor = null;
         boolean closeCursor = true;
 
         try {
-          // TODO: should use the |selectionArgs| argument in |ContentResolver.query()|.
-          ArrayList<String> restrictions = new ArrayList<String>();
+          StringBuilder restrictions = new StringBuilder();
+          Formatter formatter = new Formatter(restrictions);
 
           if (mStartDate >= 0) {
-            restrictions.add("date >= " + mStartDate);
+            formatter.format("date >= '%d' AND ", mStartDate);
           }
 
           if (mEndDate >= 0) {
-            restrictions.add("date <= " + mEndDate);
+            formatter.format("date <= '%d' AND ", mEndDate);
           }
 
           if (mNumbersCount > 0) {
-            final StringBuilder numberRestriction = new StringBuilder("address IN ('");
-            numberRestriction.append(mNumbers[0]).append("'");
+            formatter.format("address IN ('%s'", mNumbers[0]);
 
-            for (int i=1; i<mNumbersCount; ++i) {
-              numberRestriction.append(", '").append(mNumbers[i]).append("'");
+            for (int i = 1; i < mNumbersCount; ++i) {
+              formatter.format(", '%s'", mNumbers[i]);
             }
-            numberRestriction.append(')');
-
-            restrictions.add(numberRestriction.toString());
+            
+            formatter.format(") AND ");
           }
 
-          if (mDelivery == null) {
-            restrictions.add("type IN ('" + kSmsTypeSentbox + "', '" + kSmsTypeInbox + "')");
+          if (mDelivery == null || mDelivery.isEmpty()) {
+            formatter.format("type IN ('%d', '%d') AND ", kSmsTypeSentbox, kSmsTypeInbox);
           } else if (mDelivery.equals("sent")) {
-            restrictions.add("type = " + kSmsTypeSentbox);
+            formatter.format("type = '%d' AND ", kSmsTypeSentbox);
           } else if (mDelivery.equals("received")) {
-            restrictions.add("type = " + kSmsTypeInbox);
+            formatter.format("type = '%d' AND ", kSmsTypeInbox);
           } else {
-            throw new UnexpectedDeliveryStateException();
+            throw new Exception("Unexpected delivery state: " + mDelivery);
           }
 
-          final StringBuilder restrictionText = new StringBuilder();
-          if (!restrictions.isEmpty()) {
-            restrictionText.append(restrictions.get(0));
+          if (mHasThreadId) {
+            formatter.format("thread_id = '%d' AND ", mThreadId);
           }
 
-          for (int i=1; i<restrictions.size(); ++i) {
-            restrictionText.append(" AND ").append(restrictions.get(i));
-          }
+          // Final 'AND 1' is a no-op so we don't have to special case the last
+          // condition.
+          formatter.format("1");
 
           ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
-          cursor = cr.query(kSmsContentUri, kRequiredMessageRows, restrictionText.toString(), null,
+          cursor = cr.query(kSmsContentUri,
+                            kRequiredMessageRows,
+                            restrictions.toString(),
+                            null,
                             mReverse ? "date DESC" : "date ASC");
 
           if (cursor.getCount() == 0) {
-            notifyNoMessageInList(mRequestId);
+            notifyCursorDone(mRequestId);
             return;
           }
 
-          cursor.moveToFirst();
+          MessagesListManager.getInstance().add(mRequestId, cursor);
 
-          int type = cursor.getInt(cursor.getColumnIndex("type"));
-          int deliveryStatus;
-          String sender = "";
-          String receiver = "";
+          cursor.moveToFirst();
+          getMessageFromCursorAndNotify(cursor, mRequestId);
+          closeCursor = false;
+        } catch (Exception e) {
+          Log.e("GeckoSmsManager", "Error while trying to create a message list cursor", e);
+          notifyCursorError(kUnknownError, mRequestId);
+        } finally {
+          if (closeCursor && cursor != null) {
+            cursor.close();
+          }
+        }
+      }
+    }
 
-          if (type == kSmsTypeInbox) {
-            deliveryStatus = kDeliveryStatusSuccess;
-            sender = cursor.getString(cursor.getColumnIndex("address"));
-          } else if (type == kSmsTypeSentbox) {
-            deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status")));
-            receiver = cursor.getString(cursor.getColumnIndex("address"));
-          } else {
-            throw new UnexpectedDeliveryStateException();
+    if (!SmsIOThread.getInstance().execute(new CreateMessageCursorRunnable(aStartDate, aEndDate, aNumbers, aNumbersCount, aDelivery, aHasRead, aRead, aHasThreadId, aThreadId, aReverse, aRequestId))) {
+      Log.e("GeckoSmsManager", "Failed to add CreateMessageCursorRunnable to the SmsIOThread");
+      notifyCursorError(kUnknownError, aRequestId);
+    }
+  }
+
+  @Override
+  public void getNextMessage(int aRequestId) {
+    class GetNextMessageRunnable implements Runnable {
+      private final int mRequestId;
+
+      GetNextMessageRunnable(int aRequestId) {
+        mRequestId = aRequestId;
+      }
+
+      @Override
+      public void run() {
+        Cursor cursor = null;
+        boolean closeCursor = true;
+        try {
+          cursor = MessagesListManager.getInstance().get(mRequestId);
+
+          if (!cursor.moveToNext()) {
+            MessagesListManager.getInstance().remove(mRequestId);
+            notifyCursorDone(mRequestId);
+            return;
           }
 
-          int listId = MessagesListManager.getInstance().add(cursor);
+          getMessageFromCursorAndNotify(cursor, mRequestId);
           closeCursor = false;
-          notifyListCreated(listId,
-                            cursor.getInt(cursor.getColumnIndex("_id")),
-                            deliveryStatus,
-                            receiver, sender,
-                            cursor.getString(cursor.getColumnIndex("body")),
-                            cursor.getLong(cursor.getColumnIndex("date")),
-                            mRequestId);
-        } catch (UnexpectedDeliveryStateException e) {
-          Log.e("GeckoSmsManager", "Unexcepted delivery state type", e);
-          notifyReadingMessageListFailed(kUnknownError, mRequestId);
         } catch (Exception e) {
-          Log.e("GeckoSmsManager", "Error while trying to create a message list cursor", e);
-          notifyReadingMessageListFailed(kUnknownError, mRequestId);
+          Log.e("GeckoSmsManager", "Error while trying to get the next message of a list", e);
+          notifyCursorError(kUnknownError, mRequestId);
         } finally {
-          // 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) {
+          if (closeCursor) {
             cursor.close();
           }
         }
       }
     }
 
-    if (!SmsIOThread.getInstance().execute(new CreateMessageListRunnable(aStartDate, aEndDate, aNumbers, aNumbersCount, aDelivery, aHasRead, aRead, aThreadId, aReverse, aRequestId))) {
-      Log.e("GeckoSmsManager", "Failed to add CreateMessageListRunnable to the SmsIOThread");
-      notifyReadingMessageListFailed(kUnknownError, aRequestId);
+    if (!SmsIOThread.getInstance().execute(new GetNextMessageRunnable(aRequestId))) {
+      Log.e("GeckoSmsManager", "Failed to add GetNextMessageRunnable to the SmsIOThread");
+      notifyCursorError(kUnknownError, aRequestId);
+    }
+  }
+
+  private void getThreadFromCursorAndNotify(Cursor aCursor, int aRequestId) throws Exception {
+    ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
+
+    long id = aCursor.getLong(aCursor.getColumnIndex("thread_id"));
+    Cursor msgCursor = cr.query(kSmsContentUri,
+                                kRequiredMessageRowsForThread,
+                                "thread_id = " + id,
+                                null,
+                                "date DESC");
+
+    if (msgCursor == null || msgCursor.getCount() == 0) {
+      throw new Exception("Empty thread " + id);
     }
+
+    msgCursor.moveToFirst();
+
+    String lastMessageSubject = msgCursor.getString(msgCursor.getColumnIndex("subject"));
+    String body = msgCursor.getString(msgCursor.getColumnIndex("body"));
+    long timestamp = msgCursor.getLong(msgCursor.getColumnIndex("date"));
+
+    HashSet<String> participants = new HashSet<>();
+    do {
+      String p = msgCursor.getString(msgCursor.getColumnIndex("address"));
+      participants.add(p);
+    } while (msgCursor.moveToNext());
+
+    //TODO: handle MMS
+    String lastMessageType = "sms";
+
+    msgCursor = cr.query(kSmsContentUri,
+                         kRequiredMessageRowsForThread,
+                         "thread_id = " + id + " AND read = 0",
+                         null,
+                         null);
+
+    if (msgCursor == null) {
+      Log.e("GeckoSmsManager", "We should never get here, should have errored before");
+      throw new Exception("Empty thread " + id);
+    }
+
+    long unreadCount = msgCursor.getCount();
+
+    notifyThreadCursorResult(id, lastMessageSubject, body, unreadCount, participants.toArray(), timestamp, lastMessageType, aRequestId);
   }
 
   @Override
-  public void getNextMessageInList(int aListId, int aRequestId) {
-    class GetNextMessageInListRunnable implements Runnable {
-      private final int mListId;
+  public void createThreadCursor(int aRequestId) {
+    class CreateThreadCursorRunnable implements Runnable {
       private final int mRequestId;
 
-      GetNextMessageInListRunnable(int aListId, int aRequestId) {
-        mListId = aListId;
+      CreateThreadCursorRunnable(int aRequestId) {
         mRequestId = aRequestId;
       }
 
       @Override
       public void run() {
         try {
-          Cursor cursor = MessagesListManager.getInstance().get(mListId);
-
-          if (!cursor.moveToNext()) {
-            MessagesListManager.getInstance().remove(mListId);
-            notifyNoMessageInList(mRequestId);
+          ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
+          Cursor cursor = cr.query(kSmsThreadsContentUri,
+                                   kThreadProjection,
+                                   null,
+                                   null,
+                                   "date DESC");
+          if (cursor == null || !cursor.moveToFirst()) {
+            notifyCursorDone(mRequestId);
             return;
           }
 
-          int type = cursor.getInt(cursor.getColumnIndex("type"));
-          int deliveryStatus;
-          String sender = "";
-          String receiver = "";
+          MessagesListManager.getInstance().add(mRequestId, cursor);
 
-          if (type == kSmsTypeInbox) {
-            deliveryStatus = kDeliveryStatusSuccess;
-            sender = cursor.getString(cursor.getColumnIndex("address"));
-          } else if (type == kSmsTypeSentbox) {
-            deliveryStatus = getGeckoDeliveryStatus(cursor.getInt(cursor.getColumnIndex("status")));
-            receiver = cursor.getString(cursor.getColumnIndex("address"));
-          } else {
-            throw new UnexpectedDeliveryStateException();
-          }
-
-          int listId = MessagesListManager.getInstance().add(cursor);
-          notifyGotNextMessage(cursor.getInt(cursor.getColumnIndex("_id")),
-                               deliveryStatus,
-                               receiver, sender,
-                               cursor.getString(cursor.getColumnIndex("body")),
-                               cursor.getLong(cursor.getColumnIndex("date")),
-                               mRequestId);
-        } catch (UnexpectedDeliveryStateException e) {
-          Log.e("GeckoSmsManager", "Unexcepted delivery state type", e);
-          notifyReadingMessageListFailed(kUnknownError, mRequestId);
+          getThreadFromCursorAndNotify(cursor, mRequestId);
         } catch (Exception e) {
-          Log.e("GeckoSmsManager", "Error while trying to get the next message of a list", e);
-          notifyReadingMessageListFailed(kUnknownError, mRequestId);
+          Log.e("GeckoSmsManager", "Error while trying to create thread cursor: " + e);
+          notifyCursorError(kUnknownError, mRequestId);
         }
       }
     }
 
-    if (!SmsIOThread.getInstance().execute(new GetNextMessageInListRunnable(aListId, aRequestId))) {
-      Log.e("GeckoSmsManager", "Failed to add GetNextMessageInListRunnable to the SmsIOThread");
-      notifyReadingMessageListFailed(kUnknownError, aRequestId);
+    if (!SmsIOThread.getInstance().execute(new CreateThreadCursorRunnable(aRequestId))) {
+      Log.e("GeckoSmsManager", "Failed to add CreateThreadCursorRunnable to the SmsIOThread");
+      notifyCursorError(kUnknownError, aRequestId);
     }
   }
 
   @Override
-  public void clearMessageList(int aListId) {
-    MessagesListManager.getInstance().remove(aListId);
+  public void getNextThread(int aRequestId) {
+    class GetNextThreadRunnable implements Runnable {
+      private final int mRequestId;
+
+      GetNextThreadRunnable(int aRequestId) {
+        mRequestId = aRequestId;
+      }
+
+      @Override
+      public void run() {
+        try {
+          Cursor cursor = MessagesListManager.getInstance().get(mRequestId);
+
+          if (!cursor.moveToNext()) {
+            MessagesListManager.getInstance().remove(mRequestId);
+            notifyCursorDone(mRequestId);
+            return;
+          }
+
+          getThreadFromCursorAndNotify(cursor, mRequestId);
+        } catch (Exception e) {
+          Log.e("GeckoSmsManager", "Error while trying to create thread cursor: " + e);
+          notifyCursorError(kUnknownError, mRequestId);
+        }
+      }
+    }
+
+    if (!SmsIOThread.getInstance().execute(new GetNextThreadRunnable(aRequestId))) {
+      Log.e("GeckoSmsManager", "Failed to add GetNextThreadRunnable to the SmsIOThread");
+      notifyCursorError(kUnknownError, aRequestId);
+    }
   }
 
   @Override
   public void stop() {
     GeckoAppShell.getContext().unregisterReceiver(this);
   }
 
   @Override
@@ -978,29 +1075,37 @@ public class GeckoSmsManager
   static class NotFoundException extends Exception {
     private static final long serialVersionUID = 1940676816633984L;
   }
 
   static class TooManyResultsException extends Exception {
     private static final long serialVersionUID = 51883196784325305L;
   }
 
-  static class UnexpectedDeliveryStateException extends Exception {
-    private static final long serialVersionUID = 494122763684005716L;
-  }
-
   static class UnmatchingIdException extends Exception {
     private static final long serialVersionUID = 158467542575633280L;
   }
 
+  @WrapForJNI
   private static native void notifySmsReceived(String aSender, String aBody, int aMessageClass, long aTimestamp);
+  @WrapForJNI
   private static native void notifySmsSent(int aId, String aReceiver, String aBody, long aTimestamp, int aRequestId);
+  @WrapForJNI
   private static native void notifySmsDelivery(int aId, int aDeliveryStatus, String aReceiver, String aBody, long aTimestamp);
+  @WrapForJNI
   private static native void notifySmsSendFailed(int aError, int aRequestId);
-  private static native void notifyGetSms(int aId, int aDeliveryStatus, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId);
+  @WrapForJNI
+  private static native void notifyGetSms(int aId, int aDeliveryStatus, String aReceiver, String aSender, String aBody, long aTimestamp, boolean aRead, int aRequestId);
+  @WrapForJNI
   private static native void notifyGetSmsFailed(int aError, int aRequestId);
+  @WrapForJNI
   private static native void notifySmsDeleted(boolean aDeleted, int aRequestId);
+  @WrapForJNI
   private static native void notifySmsDeleteFailed(int aError, int aRequestId);
-  private static native void notifyNoMessageInList(int aRequestId);
-  private static native void notifyListCreated(int aListId, int aMessageId, int aDeliveryStatus, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId);
-  private static native void notifyGotNextMessage(int aMessageId, int aDeliveryStatus, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId);
-  private static native void notifyReadingMessageListFailed(int aError, int aRequestId);
+  @WrapForJNI
+  private static native void notifyCursorError(int aError, int aRequestId);
+  @WrapForJNI
+  private static native void notifyThreadCursorResult(long aId, String aLastMessageSubject, String aBody, long aUnreadCount, Object[] aParticipants, long aTimestamp, String aLastMessageType, int aRequestId);
+  @WrapForJNI
+  private static native void notifyMessageCursorResult(int aMessageId, int aDeliveryStatus, String aReceiver, String aSender, String aBody, long aTimestamp, long aThreadId, boolean aRead, int aRequestId);
+  @WrapForJNI
+  private static native void notifyCursorDone(int aRequestId);
 }
--- a/mobile/android/base/SmsManager.java
+++ b/mobile/android/base/SmsManager.java
@@ -26,14 +26,15 @@ public class SmsManager {
     public interface ISmsManager {
         void start();
         void stop();
         void shutdown();
 
         void send(String aNumber, String aMessage, int aRequestId);
         void getMessage(int aMessageId, int aRequestId);
         void deleteMessage(int aMessageId, int aRequestId);
-        void createMessageList(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, String aDelivery, boolean aHasRead, boolean aRead, long aThreadId, boolean aReverse, int aRequestId);
-        void getNextMessageInList(int aListId, int aRequestId);
-        void clearMessageList(int aListId);
+        void createMessageCursor(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, String aDelivery, boolean aHasRead, boolean aRead, boolean aHasThreadId, long aThreadId, boolean aReverse, int aRequestId);
+        void createThreadCursor(int aRequestId);
+        void getNextThread(int aRequestId);
+        void getNextMessage(int aRequestId);
     }
 }
 
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1096,62 +1096,101 @@ AndroidBridge::DeleteMessage(int32_t aMe
 
     uint32_t requestId;
     if (!QueueSmsRequest(aRequest, &requestId))
         return;
 
     GeckoAppShell::DeleteMessageWrapper(aMessageId, requestId);
 }
 
-void
-AndroidBridge::CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilter, bool aReverse,
-                                 nsIMobileMessageCallback* aRequest)
+NS_IMPL_ISUPPORTS0(MessageCursorContinueCallback)
+
+NS_IMETHODIMP
+MessageCursorContinueCallback::HandleContinue()
 {
-    ALOG_BRIDGE("AndroidBridge::CreateMessageList");
+    GeckoAppShell::GetNextMessageWrapper(mRequestId);
+    return NS_OK;
+}
+
+already_AddRefed<nsICursorContinueCallback>
+AndroidBridge::CreateMessageCursor(bool aHasStartDate,
+                                   uint64_t aStartDate,
+                                   bool aHasEndDate,
+                                   uint64_t aEndDate,
+                                   const char16_t** aNumbers,
+                                   uint32_t aNumbersCount,
+                                   const nsAString& aDelivery,
+                                   bool aHasRead,
+                                   bool aRead,
+                                   bool aHasThreadId,
+                                   uint64_t aThreadId,
+                                   bool aReverse,
+                                   nsIMobileMessageCursorCallback* aRequest)
+{
+    ALOG_BRIDGE("AndroidBridge::CreateMessageCursor");
 
     JNIEnv* const env = jni::GetGeckoThreadEnv();
 
     uint32_t requestId;
-    if (!QueueSmsRequest(aRequest, &requestId))
-        return;
+    if (!QueueSmsCursorRequest(aRequest, &requestId))
+        return nullptr;
 
     AutoLocalJNIFrame jniFrame(env, 2);
 
     jobjectArray numbers =
-        (jobjectArray)env->NewObjectArray(aFilter.numbers().Length(),
+        (jobjectArray)env->NewObjectArray(aNumbersCount,
                                           jStringClass,
                                           NewJavaString(&jniFrame, EmptyString()));
 
-    for (uint32_t i = 0; i < aFilter.numbers().Length(); ++i) {
-        jstring elem = NewJavaString(&jniFrame, aFilter.numbers()[i]);
+    for (uint32_t i = 0; i < aNumbersCount; ++i) {
+        jstring elem = NewJavaString(&jniFrame, nsDependentString(aNumbers[i]));
         env->SetObjectArrayElement(numbers, i, elem);
         env->DeleteLocalRef(elem);
     }
 
-    int64_t startDate = aFilter.hasStartDate() ? aFilter.startDate() : -1;
-    int64_t endDate = aFilter.hasEndDate() ? aFilter.endDate() : -1;
-    GeckoAppShell::CreateMessageListWrapper(startDate, endDate,
-                                            ObjectArray::Ref::From(numbers),
-                                            aFilter.numbers().Length(),
-                                            aFilter.delivery(),
-                                            aFilter.hasRead(), aFilter.read(),
-                                            aFilter.threadId(),
-                                            aReverse, requestId);
+    int64_t startDate = aHasStartDate ? aStartDate : -1;
+    int64_t endDate = aHasEndDate ? aEndDate : -1;
+    GeckoAppShell::CreateMessageCursorWrapper(startDate, endDate,
+                                              ObjectArray::Ref::From(numbers),
+                                              aNumbersCount,
+                                              aDelivery,
+                                              aHasRead, aRead,
+                                              aHasThreadId, aThreadId,
+                                              aReverse,
+                                              requestId);
+
+    nsCOMPtr<nsICursorContinueCallback> callback = 
+       new MessageCursorContinueCallback(requestId);
+    return callback.forget();
 }
 
-void
-AndroidBridge::GetNextMessageInList(int32_t aListId, nsIMobileMessageCallback* aRequest)
+NS_IMPL_ISUPPORTS0(ThreadCursorContinueCallback)
+
+NS_IMETHODIMP
+ThreadCursorContinueCallback::HandleContinue()
 {
-    ALOG_BRIDGE("AndroidBridge::GetNextMessageInList");
+    GeckoAppShell::GetNextThreadWrapper(mRequestId);
+    return NS_OK;
+}
+
+already_AddRefed<nsICursorContinueCallback>
+AndroidBridge::CreateThreadCursor(nsIMobileMessageCursorCallback* aRequest)
+{
+    ALOG_BRIDGE("AndroidBridge::CreateThreadCursor");
 
     uint32_t requestId;
-    if (!QueueSmsRequest(aRequest, &requestId))
-        return;
+    if (!QueueSmsCursorRequest(aRequest, &requestId)) {
+        return nullptr;
+    }
 
-    GeckoAppShell::GetNextMessageInListWrapper(aListId, requestId);
+    GeckoAppShell::CreateThreadCursorWrapper(requestId);
+
+    nsCOMPtr<nsICursorContinueCallback> callback =
+        new ThreadCursorContinueCallback(requestId);
+    return callback.forget();
 }
 
 bool
 AndroidBridge::QueueSmsRequest(nsIMobileMessageCallback* aRequest, uint32_t* aRequestIdOut)
 {
     MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
     MOZ_ASSERT(aRequest && aRequestIdOut);
 
@@ -1179,16 +1218,67 @@ AndroidBridge::DequeueSmsRequest(uint32_
     MOZ_ASSERT(aRequestId < mSmsRequests.Length());
     if (aRequestId >= mSmsRequests.Length()) {
         return nullptr;
     }
 
     return mSmsRequests[aRequestId].forget();
 }
 
+bool
+AndroidBridge::QueueSmsCursorRequest(nsIMobileMessageCursorCallback* aRequest,
+                                     uint32_t* aRequestIdOut)
+{
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+    MOZ_ASSERT(aRequest && aRequestIdOut);
+
+    const uint32_t length = mSmsCursorRequests.Length();
+    for (uint32_t i = 0; i < length; i++) {
+        if (!(mSmsCursorRequests)[i]) {
+            (mSmsCursorRequests)[i] = aRequest;
+            *aRequestIdOut = i;
+            return true;
+        }
+    }
+
+    mSmsCursorRequests.AppendElement(aRequest);
+
+    // After AppendElement(), previous `length` points to the new tail element.
+    *aRequestIdOut = length;
+    return true;
+}
+
+nsCOMPtr<nsIMobileMessageCursorCallback>
+AndroidBridge::GetSmsCursorRequest(uint32_t aRequestId)
+{
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    MOZ_ASSERT(aRequestId < mSmsCursorRequests.Length());
+    if (aRequestId >= mSmsCursorRequests.Length()) {
+        return nullptr;
+    }
+
+    // TODO: remove on final dequeue
+    return mSmsCursorRequests[aRequestId];
+}
+
+already_AddRefed<nsIMobileMessageCursorCallback>
+AndroidBridge::DequeueSmsCursorRequest(uint32_t aRequestId)
+{
+    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+    MOZ_ASSERT(aRequestId < mSmsCursorRequests.Length());
+    if (aRequestId >= mSmsCursorRequests.Length()) {
+        return nullptr;
+    }
+
+    // TODO: remove on final dequeue
+    return mSmsCursorRequests[aRequestId].forget();
+}
+
 void
 AndroidBridge::GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo)
 {
     ALOG_BRIDGE("AndroidBridge::GetCurrentNetworkInformation");
 
     // To prevent calling too many methods through JNI, the Java method returns
     // an array of double even if we actually want an integer, a boolean, and an integer.
 
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -20,16 +20,18 @@
 #include "nsIMutableArray.h"
 #include "nsIMIMEInfo.h"
 #include "nsColor.h"
 #include "gfxRect.h"
 #include "mozilla/gfx/Point.h"
 
 #include "nsIAndroidBridge.h"
 #include "nsIMobileMessageCallback.h"
+#include "nsIMobileMessageCursorCallback.h"
+#include "nsIDOMDOMCursor.h"
 
 #include "mozilla/Likely.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Types.h"
 #include "mozilla/jni/Utils.h"
 
 // Some debug #defines
@@ -69,25 +71,16 @@ typedef struct AndroidSystemColors {
     nscolor textColorTertiaryInverse;
     nscolor textColorHighlight;
     nscolor colorForeground;
     nscolor colorBackground;
     nscolor panelColorForeground;
     nscolor panelColorBackground;
 } AndroidSystemColors;
 
-class nsFilePickerCallback : nsISupports {
-public:
-    NS_DECL_THREADSAFE_ISUPPORTS
-    virtual void handleResult(nsAString& filePath) = 0;
-    nsFilePickerCallback() {}
-protected:
-    virtual ~nsFilePickerCallback() {}
-};
-
 class DelayedTask {
 public:
     DelayedTask(Task* aTask, int aDelayMs) {
         mTask = aTask;
         mRunTime = mozilla::TimeStamp::Now() + mozilla::TimeDuration::FromMilliseconds(aDelayMs);
     }
 
     bool IsEarlierThan(DelayedTask *aOther) {
@@ -103,16 +96,52 @@ public:
         return mTask;
     }
 
 private:
     Task* mTask;
     mozilla::TimeStamp mRunTime;
 };
 
+class ThreadCursorContinueCallback : public nsICursorContinueCallback
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICURSORCONTINUECALLBACK
+
+    ThreadCursorContinueCallback(int aRequestId)
+        : mRequestId(aRequestId)
+    {
+    }
+private:
+    virtual ~ThreadCursorContinueCallback()
+    {
+    }
+
+    int mRequestId;
+};
+
+class MessageCursorContinueCallback : public nsICursorContinueCallback
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICURSORCONTINUECALLBACK
+
+    MessageCursorContinueCallback(int aRequestId)
+        : mRequestId(aRequestId)
+    {
+    }
+private:
+    virtual ~MessageCursorContinueCallback()
+    {
+    }
+
+    int mRequestId;
+};
+
 class AndroidBridge final
 {
 public:
     enum {
         // Values for NotifyIME, in addition to values from the Gecko
         // IMEMessage enum; use negative values here to prevent conflict
         NOTIFY_IME_OPEN_VKB = -2,
         NOTIFY_IME_REPLY_EVENT = -1,
@@ -236,20 +265,35 @@ public:
     void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
 
     nsresult GetSegmentInfoForText(const nsAString& aText,
                                    nsIMobileMessageCallback* aRequest);
     void SendMessage(const nsAString& aNumber, const nsAString& aText,
                      nsIMobileMessageCallback* aRequest);
     void GetMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest);
     void DeleteMessage(int32_t aMessageId, nsIMobileMessageCallback* aRequest);
-    void CreateMessageList(const dom::mobilemessage::SmsFilterData& aFilter,
-                           bool aReverse, nsIMobileMessageCallback* aRequest);
-    void GetNextMessageInList(int32_t aListId, nsIMobileMessageCallback* aRequest);
+    already_AddRefed<nsICursorContinueCallback>
+    CreateMessageCursor(bool aHasStartDate,
+                        uint64_t aStartDate,
+                        bool aHasEndDate,
+                        uint64_t aEndDate,
+                        const char16_t** aNumbers,
+                        uint32_t aNumbersCount,
+                        const nsAString& aDelivery,
+                        bool aHasRead,
+                        bool aRead,
+                        bool aHasThreadId,
+                        uint64_t aThreadId,
+                        bool aReverse,
+                        nsIMobileMessageCursorCallback* aRequest);
+    already_AddRefed<nsICursorContinueCallback>
+    CreateThreadCursor(nsIMobileMessageCursorCallback* aRequest);
     already_AddRefed<nsIMobileMessageCallback> DequeueSmsRequest(uint32_t aRequestId);
+    nsCOMPtr<nsIMobileMessageCursorCallback> GetSmsCursorRequest(uint32_t aRequestId);
+    already_AddRefed<nsIMobileMessageCursorCallback> DequeueSmsCursorRequest(uint32_t aRequestId);
 
     void GetCurrentNetworkInformation(hal::NetworkInformation* aNetworkInfo);
 
     void SetFirstPaintViewport(const LayerIntPoint& aOffset, const CSSToLayerScale& aZoom, const CSSRect& aCssPageRect);
     void SetPageRect(const CSSRect& aCssPageRect);
     void SyncViewportInfo(const LayerIntRect& aDisplayPort, const CSSToLayerScale& aDisplayResolution,
                           bool aLayersUpdated, int32_t aPaintSyncId, ParentLayerRect& aScrollRect, CSSToParentLayerScale& aScale,
                           ScreenMargin& aFixedLayerMargins);
@@ -308,17 +352,18 @@ public:
 
     static nsresult GetExternalPublicDirectory(const nsAString& aType, nsAString& aPath);
 
 protected:
     static nsDataHashtable<nsStringHashKey, nsString> sStoragePaths;
 
     static pthread_t sJavaUiThread;
     static AndroidBridge* sBridge;
-    nsTArray<nsCOMPtr<nsIMobileMessageCallback> > mSmsRequests;
+    nsTArray<nsCOMPtr<nsIMobileMessageCallback>> mSmsRequests;
+    nsTArray<nsCOMPtr<nsIMobileMessageCursorCallback>> mSmsCursorRequests;
 
     widget::GeckoLayerClient::GlobalRef mLayerClient;
 
     // the android.telephony.SmsMessage class
     jclass mAndroidSmsMessageClass;
 
     AndroidBridge();
     ~AndroidBridge();
@@ -329,16 +374,17 @@ protected:
 
     bool mHasNativeBitmapAccess;
     bool mHasNativeWindowAccess;
     bool mHasNativeWindowFallback;
 
     int mAPIVersion;
 
     bool QueueSmsRequest(nsIMobileMessageCallback* aRequest, uint32_t* aRequestIdOut);
+    bool QueueSmsCursorRequest(nsIMobileMessageCursorCallback* aRequest, uint32_t* aRequestIdOut);
 
     // intput stream
     jclass jReadableByteChannel;
     jclass jChannels;
     jmethodID jChannelCreate;
     jmethodID jByteBufferRead;
 
     jclass jInputStream;
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -28,30 +28,23 @@
 #include "nsICrashReporter.h"
 #include "nsExceptionHandler.h"
 #endif
 
 #include "mozilla/unused.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/UniquePtr.h"
 
-#include "mozilla/dom/SmsMessage.h"
-#include "mozilla/dom/mobilemessage/Constants.h"
-#include "mozilla/dom/mobilemessage/Types.h"
-#include "mozilla/dom/mobilemessage/PSms.h"
-#include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "mozilla/layers/APZCTreeManager.h"
-#include "nsIMobileMessageDatabaseService.h"
 #include "nsPluginInstanceOwner.h"
 #include "AndroidSurfaceTexture.h"
 #include "nsMemoryPressure.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
-using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla::widget::android;
 
 /* Forward declare all the JNI methods as extern "C" */
 
 extern "C" {
 /*
@@ -159,509 +152,16 @@ Java_org_mozilla_gecko_GeckoAppShell_not
       bool   mCharging;
       double mRemainingTime;
     };
 
     nsCOMPtr<nsIRunnable> runnable = new NotifyBatteryChangeRunnable(aLevel, aCharging, aRemainingTime);
     NS_DispatchToMainThread(runnable);
 }
 
-#ifdef MOZ_WEBSMS_BACKEND
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(JNIEnv* jenv, jclass,
-                                                         jstring aSender,
-                                                         jstring aBody,
-                                                         jint aMessageClass,
-                                                         jlong aTimestamp)
-{
-    class NotifySmsReceivedRunnable : public nsRunnable {
-    public:
-      NotifySmsReceivedRunnable(const SmsMessageData& aMessageData)
-        : mMessageData(aMessageData)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-        if (!obs) {
-          return NS_OK;
-        }
-
-        nsCOMPtr<nsIDOMMozSmsMessage> message = new SmsMessage(mMessageData);
-        obs->NotifyObservers(message, kSmsReceivedObserverTopic, nullptr);
-        return NS_OK;
-      }
-
-    private:
-      SmsMessageData mMessageData;
-    };
-
-    // TODO Need to correct the message `threadId` parameter value. Bug 859098
-    SmsMessageData message(0, 0, eDeliveryState_Received, eDeliveryStatus_Success,
-                           nsJNIString(aSender, jenv), EmptyString(),
-                           nsJNIString(aBody, jenv),
-                           static_cast<MessageClass>(aMessageClass),
-                           aTimestamp, false);
-
-    nsCOMPtr<nsIRunnable> runnable = new NotifySmsReceivedRunnable(message);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifySmsSent(JNIEnv* jenv, jclass,
-                                                     jint aId,
-                                                     jstring aReceiver,
-                                                     jstring aBody,
-                                                     jlong aTimestamp,
-                                                     jint aRequestId)
-{
-    class NotifySmsSentRunnable : public nsRunnable {
-    public:
-      NotifySmsSentRunnable(const SmsMessageData& aMessageData,
-                            int32_t aRequestId)
-        : mMessageData(aMessageData)
-        , mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        /*
-         * First, we are going to notify all SmsManager that a message has
-         * been sent. Then, we will notify the SmsRequest object about it.
-         */
-        nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-        if (!obs) {
-          return NS_OK;
-        }
-
-        nsCOMPtr<nsIDOMMozSmsMessage> message = new SmsMessage(mMessageData);
-        obs->NotifyObservers(message, kSmsSentObserverTopic, nullptr);
-
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        request->NotifyMessageSent(message);
-        return NS_OK;
-      }
-
-    private:
-      SmsMessageData mMessageData;
-      int32_t        mRequestId;
-    };
-
-    // TODO Need to add the message `messageClass` parameter value. Bug 804476
-    // TODO Need to correct the message `threadId` parameter value. Bug 859098
-    SmsMessageData message(aId, 0, eDeliveryState_Sent, eDeliveryStatus_Pending,
-                           EmptyString(), nsJNIString(aReceiver, jenv),
-                           nsJNIString(aBody, jenv), eMessageClass_Normal,
-                           aTimestamp, true);
-
-    nsCOMPtr<nsIRunnable> runnable = new NotifySmsSentRunnable(message, aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifySmsDelivery(JNIEnv* jenv, jclass,
-                                                         jint aId,
-                                                         jint aDeliveryStatus,
-                                                         jstring aReceiver,
-                                                         jstring aBody,
-                                                         jlong aTimestamp)
-{
-    class NotifySmsDeliveredRunnable : public nsRunnable {
-    public:
-      NotifySmsDeliveredRunnable(const SmsMessageData& aMessageData)
-        : mMessageData(aMessageData)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-        if (!obs) {
-          return NS_OK;
-        }
-
-        nsCOMPtr<nsIDOMMozSmsMessage> message = new SmsMessage(mMessageData);
-        const char* topic = (mMessageData.deliveryStatus() == eDeliveryStatus_Success)
-                            ? kSmsDeliverySuccessObserverTopic
-                            : kSmsDeliveryErrorObserverTopic;
-        obs->NotifyObservers(message, topic, nullptr);
-
-        return NS_OK;
-      }
-
-    private:
-      SmsMessageData mMessageData;
-    };
-
-    // TODO Need to add the message `messageClass` parameter value. Bug 804476
-    // TODO Need to correct the message `threadId` parameter value. Bug 859098
-    SmsMessageData message(aId, 0, eDeliveryState_Sent,
-                           static_cast<DeliveryStatus>(aDeliveryStatus),
-                           EmptyString(), nsJNIString(aReceiver, jenv),
-                           nsJNIString(aBody, jenv), eMessageClass_Normal,
-                           aTimestamp, true);
-
-    nsCOMPtr<nsIRunnable> runnable = new NotifySmsDeliveredRunnable(message);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifySmsSendFailed(JNIEnv* jenv, jclass,
-                                                           jint aError,
-                                                           jint aRequestId)
-{
-    class NotifySmsSendFailedRunnable : public nsRunnable {
-    public:
-      NotifySmsSendFailedRunnable(int32_t aError,
-                                  int32_t aRequestId)
-        : mError(aError)
-        , mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        request->NotifySendMessageFailed(mError);
-        return NS_OK;
-      }
-
-    private:
-      int32_t  mError;
-      int32_t  mRequestId;
-    };
-
-
-    nsCOMPtr<nsIRunnable> runnable =
-      new NotifySmsSendFailedRunnable(aError, aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifyGetSms(JNIEnv* jenv, jclass,
-                                                    jint aId,
-                                                    jint aDeliveryStatus,
-                                                    jstring aReceiver,
-                                                    jstring aSender,
-                                                    jstring aBody,
-                                                    jlong aTimestamp,
-                                                    jint aRequestId)
-{
-    class NotifyGetSmsRunnable : public nsRunnable {
-    public:
-      NotifyGetSmsRunnable(const SmsMessageData& aMessageData,
-                           int32_t aRequestId)
-        : mMessageData(aMessageData)
-        , mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        nsCOMPtr<nsIDOMMozSmsMessage> message = new SmsMessage(mMessageData);
-        request->NotifyMessageGot(message);
-        return NS_OK;
-      }
-
-    private:
-      SmsMessageData mMessageData;
-      int32_t        mRequestId;
-    };
-
-    nsJNIString receiver = nsJNIString(aReceiver, jenv);
-    DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received
-                                             : eDeliveryState_Sent;
-
-    // TODO Need to add the message `read` parameter value. Bug 748391
-    // TODO Need to add the message `messageClass` parameter value. Bug 804476
-    // TODO Need to correct the message `threadId` parameter value. Bug 859098
-    SmsMessageData message(aId, 0, state,
-                           static_cast<DeliveryStatus>(aDeliveryStatus),
-                           nsJNIString(aSender, jenv), receiver,
-                           nsJNIString(aBody, jenv), eMessageClass_Normal,
-                           aTimestamp, true);
-
-    nsCOMPtr<nsIRunnable> runnable = new NotifyGetSmsRunnable(message, aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifyGetSmsFailed(JNIEnv* jenv, jclass,
-                                                          jint aError,
-                                                          jint aRequestId)
-{
-    class NotifyGetSmsFailedRunnable : public nsRunnable {
-    public:
-      NotifyGetSmsFailedRunnable(int32_t aError,
-                                 int32_t aRequestId)
-        : mError(aError)
-        , mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        request->NotifyGetMessageFailed(mError);
-        return NS_OK;
-      }
-
-    private:
-      int32_t  mError;
-      int32_t  mRequestId;
-    };
-
-
-    nsCOMPtr<nsIRunnable> runnable =
-      new NotifyGetSmsFailedRunnable(aError, aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifySmsDeleted(JNIEnv* jenv, jclass,
-                                                        jboolean aDeleted,
-                                                        jint aRequestId)
-{
-    class NotifySmsDeletedRunnable : public nsRunnable {
-    public:
-      NotifySmsDeletedRunnable(bool aDeleted, int32_t aRequestId)
-        : mDeleted(aDeleted)
-        , mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        // For android, we support only single SMS deletion.
-        request->NotifyMessageDeleted(&mDeleted, 1);
-        return NS_OK;
-      }
-
-    private:
-      bool      mDeleted;
-      int32_t   mRequestId;
-    };
-
-
-    nsCOMPtr<nsIRunnable> runnable =
-      new NotifySmsDeletedRunnable(aDeleted, aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifySmsDeleteFailed(JNIEnv* jenv, jclass,
-                                                             jint aError,
-                                                             jint aRequestId)
-{
-    class NotifySmsDeleteFailedRunnable : public nsRunnable {
-    public:
-      NotifySmsDeleteFailedRunnable(int32_t aError,
-                                    int32_t aRequestId)
-        : mError(aError)
-        , mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        request->NotifyDeleteMessageFailed(mError);
-        return NS_OK;
-      }
-
-    private:
-      int32_t  mError;
-      int32_t  mRequestId;
-    };
-
-
-    nsCOMPtr<nsIRunnable> runnable =
-      new NotifySmsDeleteFailedRunnable(aError, aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifyNoMessageInList(JNIEnv* jenv, jclass,
-                                                             jint aRequestId)
-{
-    class NotifyNoMessageInListRunnable : public nsRunnable {
-    public:
-      NotifyNoMessageInListRunnable(int32_t aRequestId)
-        : mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        request->NotifyNoMessageInList();
-        return NS_OK;
-      }
-
-    private:
-      int32_t               mRequestId;
-    };
-
-
-    nsCOMPtr<nsIRunnable> runnable =
-      new NotifyNoMessageInListRunnable(aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifyListCreated(JNIEnv* jenv, jclass,
-                                                         jint aListId,
-                                                         jint aMessageId,
-                                                         jint aDeliveryStatus,
-                                                         jstring aReceiver,
-                                                         jstring aSender,
-                                                         jstring aBody,
-                                                         jlong aTimestamp,
-                                                         jint aRequestId)
-{
-    class NotifyCreateMessageListRunnable : public nsRunnable {
-    public:
-      NotifyCreateMessageListRunnable(int32_t aListId,
-                                      const SmsMessageData& aMessageData,
-                                      int32_t aRequestId)
-        : mListId(aListId)
-        , mMessageData(aMessageData)
-        , mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        nsCOMPtr<nsIDOMMozSmsMessage> message = new SmsMessage(mMessageData);
-        request->NotifyMessageListCreated(mListId, message);
-        return NS_OK;
-      }
-
-    private:
-      int32_t        mListId;
-      SmsMessageData mMessageData;
-      int32_t        mRequestId;
-    };
-
-
-    nsJNIString receiver = nsJNIString(aReceiver, jenv);
-    DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received
-                                             : eDeliveryState_Sent;
-
-    // TODO Need to add the message `read` parameter value. Bug 748391
-    // TODO Need to add the message `messageClass` parameter value. Bug 804476
-    // TODO Need to correct the message `threadId` parameter value. Bug 859098
-    SmsMessageData message(aMessageId, 0, state,
-                           static_cast<DeliveryStatus>(aDeliveryStatus),
-                           nsJNIString(aSender, jenv), receiver,
-                           nsJNIString(aBody, jenv), eMessageClass_Normal,
-                           aTimestamp, true);
-
-    nsCOMPtr<nsIRunnable> runnable =
-      new NotifyCreateMessageListRunnable(aListId, message, aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifyGotNextMessage(JNIEnv* jenv, jclass,
-                                                            jint aMessageId,
-                                                            jint aDeliveryStatus,
-                                                            jstring aReceiver,
-                                                            jstring aSender,
-                                                            jstring aBody,
-                                                            jlong aTimestamp,
-                                                            jint aRequestId)
-{
-    class NotifyGotNextMessageRunnable : public nsRunnable {
-    public:
-      NotifyGotNextMessageRunnable(const SmsMessageData& aMessageData,
-                                   int32_t aRequestId)
-        : mMessageData(aMessageData)
-        , mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        nsCOMPtr<nsIDOMMozSmsMessage> message = new SmsMessage(mMessageData);
-        request->NotifyNextMessageInListGot(message);
-        return NS_OK;
-      }
-
-    private:
-      SmsMessageData mMessageData;
-      int32_t        mRequestId;
-    };
-
-
-    nsJNIString receiver = nsJNIString(aReceiver, jenv);
-    DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received
-                                             : eDeliveryState_Sent;
-
-    // TODO Need to add the message `read` parameter value. Bug 748391
-    // TODO Need to add the message `messageClass` parameter value. Bug 804476
-    // TODO Need to correct the message `threadId` parameter value. Bug 859098
-    SmsMessageData message(aMessageId, 0, state,
-                           static_cast<DeliveryStatus>(aDeliveryStatus),
-                           nsJNIString(aSender, jenv), receiver,
-                           nsJNIString(aBody, jenv), eMessageClass_Normal,
-                           aTimestamp, true);
-
-    nsCOMPtr<nsIRunnable> runnable =
-      new NotifyGotNextMessageRunnable(message, aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoSmsManager_notifyReadingMessageListFailed(JNIEnv* jenv, jclass,
-                                                                      jint aError,
-                                                                      jint aRequestId)
-{
-    class NotifyReadListFailedRunnable : public nsRunnable {
-    public:
-      NotifyReadListFailedRunnable(int32_t aError,
-                                   int32_t aRequestId)
-        : mError(aError)
-        , mRequestId(aRequestId)
-      {}
-
-      NS_IMETHODIMP Run() {
-        nsCOMPtr<nsIMobileMessageCallback> request =
-          AndroidBridge::Bridge()->DequeueSmsRequest(mRequestId);
-        NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
-
-        request->NotifyReadMessageListFailed(mError);
-        return NS_OK;
-      }
-
-    private:
-      int32_t  mError;
-      int32_t  mRequestId;
-    };
-
-
-    nsCOMPtr<nsIRunnable> runnable =
-      new NotifyReadListFailedRunnable(aError, aRequestId);
-    NS_DispatchToMainThread(runnable);
-}
-
-#endif  // MOZ_WEBSMS_BACKEND
-
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite(JNIEnv*, jclass)
 {
     nsWindow::InvalidateAndScheduleComposite();
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition(JNIEnv*, jclass, jint width, jint height)
@@ -670,41 +170,16 @@ Java_org_mozilla_gecko_GeckoAppShell_sch
 }
 
 NS_EXPORT float JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_computeRenderIntegrity(JNIEnv*, jclass)
 {
     return nsWindow::ComputeRenderIntegrity();
 }
 
-NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_notifyFilePickerResult(JNIEnv* jenv, jclass, jstring filePath, jlong callback)
-{
-    class NotifyFilePickerResultRunnable : public nsRunnable {
-    public:
-        NotifyFilePickerResultRunnable(nsString& fileDir, long callback) :
-            mFileDir(fileDir), mCallback(callback) {}
-
-        NS_IMETHODIMP Run() {
-            nsFilePickerCallback* handler = (nsFilePickerCallback*)mCallback;
-            handler->handleResult(mFileDir);
-            handler->Release();
-            return NS_OK;
-        }
-    private:
-        nsString mFileDir;
-        long mCallback;
-    };
-    nsString path = nsJNIString(filePath, jenv);
-
-    nsCOMPtr<nsIRunnable> runnable =
-        new NotifyFilePickerResultRunnable(path, (long)callback);
-    NS_DispatchToMainThread(runnable);
-}
-
 #define MAX_LOCK_ATTEMPTS 10
 
 static bool LockWindowWithRetry(void* window, unsigned char** bits, int* width, int* height, int* format, int* stride)
 {
   int count = 0;
 
   while (count < MAX_LOCK_ATTEMPTS) {
       if (AndroidBridge::Bridge()->LockWindow(window, bits, width, height, format, stride))
@@ -868,17 +343,17 @@ Java_org_mozilla_gecko_gfx_NativePanZoom
     if (!AndroidBridge::Bridge()) {
         return;
     }
 
     const auto& newRef = NativePanZoomController::Ref::From(instance);
     NativePanZoomController::LocalRef oldRef =
             AndroidContentController::SetNativePanZoomController(newRef);
 
-    MOZ_ASSERT(!oldRef, "Registering a new NPZC when we already have one");
+    // MOZ_ASSERT(!oldRef, "Registering a new NPZC when we already have one");
 }
 
 NS_EXPORT jboolean JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_handleTouchEvent(JNIEnv* env, jobject instance, jobject event)
 {
     APZCTreeManager *controller = nsWindow::GetAPZCTreeManager();
     if (!controller) {
         return false;
--- a/widget/android/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -62,16 +62,75 @@ public:
                 ::template Wrap<&Impl::GetProfilerTime>)
     };
 };
 
 template<class Impl>
 constexpr JNINativeMethod GeckoJavaSampler::Natives<Impl>::methods[];
 
 template<class Impl>
+class GeckoSmsManager::Natives : public mozilla::jni::NativeImpl<GeckoSmsManager, Impl>
+{
+public:
+    static constexpr JNINativeMethod methods[] = {
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifyCursorDone_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifyCursorDone_t, Impl>
+                ::template Wrap<&Impl::NotifyCursorDone>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifyCursorError_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifyCursorError_t, Impl>
+                ::template Wrap<&Impl::NotifyCursorError>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifyGetSms_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifyGetSms_t, Impl>
+                ::template Wrap<&Impl::NotifyGetSms>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifyGetSmsFailed_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifyGetSmsFailed_t, Impl>
+                ::template Wrap<&Impl::NotifyGetSmsFailed>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifyMessageCursorResult_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifyMessageCursorResult_t, Impl>
+                ::template Wrap<&Impl::NotifyMessageCursorResult>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifySmsDeleteFailed_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifySmsDeleteFailed_t, Impl>
+                ::template Wrap<&Impl::NotifySmsDeleteFailed>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifySmsDeleted_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifySmsDeleted_t, Impl>
+                ::template Wrap<&Impl::NotifySmsDeleted>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifySmsDelivery_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifySmsDelivery_t, Impl>
+                ::template Wrap<&Impl::NotifySmsDelivery>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifySmsReceived_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifySmsReceived_t, Impl>
+                ::template Wrap<&Impl::NotifySmsReceived>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifySmsSendFailed_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifySmsSendFailed_t, Impl>
+                ::template Wrap<&Impl::NotifySmsSendFailed>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifySmsSent_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifySmsSent_t, Impl>
+                ::template Wrap<&Impl::NotifySmsSent>),
+
+        mozilla::jni::MakeNativeMethod<GeckoSmsManager::NotifyThreadCursorResult_t>(
+                mozilla::jni::NativeStub<GeckoSmsManager::NotifyThreadCursorResult_t, Impl>
+                ::template Wrap<&Impl::NotifyThreadCursorResult>)
+    };
+};
+
+template<class Impl>
+constexpr JNINativeMethod GeckoSmsManager::Natives<Impl>::methods[];
+
+template<class Impl>
 class GeckoThread::Natives : public mozilla::jni::NativeImpl<GeckoThread, Impl>
 {
 public:
     static constexpr JNINativeMethod methods[] = {
 
         mozilla::jni::MakeNativeMethod<GeckoThread::SpeculativeConnect_t>(
                 mozilla::jni::NativeStub<GeckoThread::SpeculativeConnect_t, Impl>
                 ::template Wrap<&Impl::SpeculativeConnect>)
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -73,24 +73,16 @@ auto GeckoAppShell::CancelVibrate() -> v
 constexpr char GeckoAppShell::CheckURIVisited_t::name[];
 constexpr char GeckoAppShell::CheckURIVisited_t::signature[];
 
 auto GeckoAppShell::CheckURIVisited(mozilla::jni::String::Param a0) -> void
 {
     return mozilla::jni::Method<CheckURIVisited_t>::Call(nullptr, nullptr, a0);
 }
 
-constexpr char GeckoAppShell::ClearMessageList_t::name[];
-constexpr char GeckoAppShell::ClearMessageList_t::signature[];
-
-auto GeckoAppShell::ClearMessageList(int32_t a0) -> void
-{
-    return mozilla::jni::Method<ClearMessageList_t>::Call(nullptr, nullptr, a0);
-}
-
 constexpr char GeckoAppShell::CloseCamera_t::name[];
 constexpr char GeckoAppShell::CloseCamera_t::signature[];
 
 auto GeckoAppShell::CloseCamera() -> void
 {
     return mozilla::jni::Method<CloseCamera_t>::Call(nullptr, nullptr);
 }
 
@@ -113,32 +105,40 @@ auto GeckoAppShell::ConnectionGetMimeTyp
 constexpr char GeckoAppShell::CreateInputStream_t::name[];
 constexpr char GeckoAppShell::CreateInputStream_t::signature[];
 
 auto GeckoAppShell::CreateInputStream(mozilla::jni::Object::Param a0) -> mozilla::jni::Object::LocalRef
 {
     return mozilla::jni::Method<CreateInputStream_t>::Call(nullptr, nullptr, a0);
 }
 
-constexpr char GeckoAppShell::CreateMessageListWrapper_t::name[];
-constexpr char GeckoAppShell::CreateMessageListWrapper_t::signature[];
+constexpr char GeckoAppShell::CreateMessageCursorWrapper_t::name[];
+constexpr char GeckoAppShell::CreateMessageCursorWrapper_t::signature[];
 
-auto GeckoAppShell::CreateMessageListWrapper(int64_t a0, int64_t a1, mozilla::jni::ObjectArray::Param a2, int32_t a3, mozilla::jni::String::Param a4, bool a5, bool a6, int64_t a7, bool a8, int32_t a9) -> void
+auto GeckoAppShell::CreateMessageCursorWrapper(int64_t a0, int64_t a1, mozilla::jni::ObjectArray::Param a2, int32_t a3, mozilla::jni::String::Param a4, bool a5, bool a6, bool a7, int64_t a8, bool a9, int32_t a10) -> void
 {
-    return mozilla::jni::Method<CreateMessageListWrapper_t>::Call(nullptr, nullptr, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+    return mozilla::jni::Method<CreateMessageCursorWrapper_t>::Call(nullptr, nullptr, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
 }
 
 constexpr char GeckoAppShell::CreateShortcut_t::name[];
 constexpr char GeckoAppShell::CreateShortcut_t::signature[];
 
 auto GeckoAppShell::CreateShortcut(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1, mozilla::jni::String::Param a2) -> void
 {
     return mozilla::jni::Method<CreateShortcut_t>::Call(nullptr, nullptr, a0, a1, a2);
 }
 
+constexpr char GeckoAppShell::CreateThreadCursorWrapper_t::name[];
+constexpr char GeckoAppShell::CreateThreadCursorWrapper_t::signature[];
+
+auto GeckoAppShell::CreateThreadCursorWrapper(int32_t a0) -> void
+{
+    return mozilla::jni::Method<CreateThreadCursorWrapper_t>::Call(nullptr, nullptr, a0);
+}
+
 constexpr char GeckoAppShell::DeleteMessageWrapper_t::name[];
 constexpr char GeckoAppShell::DeleteMessageWrapper_t::signature[];
 
 auto GeckoAppShell::DeleteMessageWrapper(int32_t a0, int32_t a1) -> void
 {
     return mozilla::jni::Method<DeleteMessageWrapper_t>::Call(nullptr, nullptr, a0, a1);
 }
 
@@ -361,22 +361,30 @@ auto GeckoAppShell::GetMessageWrapper(in
 constexpr char GeckoAppShell::GetMimeTypeFromExtensionsWrapper_t::name[];
 constexpr char GeckoAppShell::GetMimeTypeFromExtensionsWrapper_t::signature[];
 
 auto GeckoAppShell::GetMimeTypeFromExtensionsWrapper(mozilla::jni::String::Param a0) -> mozilla::jni::String::LocalRef
 {
     return mozilla::jni::Method<GetMimeTypeFromExtensionsWrapper_t>::Call(nullptr, nullptr, a0);
 }
 
-constexpr char GeckoAppShell::GetNextMessageInListWrapper_t::name[];
-constexpr char GeckoAppShell::GetNextMessageInListWrapper_t::signature[];
+constexpr char GeckoAppShell::GetNextMessageWrapper_t::name[];
+constexpr char GeckoAppShell::GetNextMessageWrapper_t::signature[];
 
-auto GeckoAppShell::GetNextMessageInListWrapper(int32_t a0, int32_t a1) -> void
+auto GeckoAppShell::GetNextMessageWrapper(int32_t a0) -> void
 {
-    return mozilla::jni::Method<GetNextMessageInListWrapper_t>::Call(nullptr, nullptr, a0, a1);
+    return mozilla::jni::Method<GetNextMessageWrapper_t>::Call(nullptr, nullptr, a0);
+}
+
+constexpr char GeckoAppShell::GetNextThreadWrapper_t::name[];
+constexpr char GeckoAppShell::GetNextThreadWrapper_t::signature[];
+
+auto GeckoAppShell::GetNextThreadWrapper(int32_t a0) -> void
+{
+    return mozilla::jni::Method<GetNextThreadWrapper_t>::Call(nullptr, nullptr, a0);
 }
 
 constexpr char GeckoAppShell::GetProxyForURIWrapper_t::name[];
 constexpr char GeckoAppShell::GetProxyForURIWrapper_t::signature[];
 
 auto GeckoAppShell::GetProxyForURIWrapper(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1, mozilla::jni::String::Param a2, int32_t a3) -> mozilla::jni::String::LocalRef
 {
     return mozilla::jni::Method<GetProxyForURIWrapper_t>::Call(nullptr, nullptr, a0, a1, a2, a3);
@@ -790,16 +798,54 @@ auto GeckoJavaSampler::StopJavaProfiling
 constexpr char GeckoJavaSampler::UnpauseJavaProfiling_t::name[];
 constexpr char GeckoJavaSampler::UnpauseJavaProfiling_t::signature[];
 
 auto GeckoJavaSampler::UnpauseJavaProfiling() -> void
 {
     return mozilla::jni::Method<UnpauseJavaProfiling_t>::Call(nullptr, nullptr);
 }
 
+constexpr char GeckoSmsManager::name[];
+
+constexpr char GeckoSmsManager::NotifyCursorDone_t::name[];
+constexpr char GeckoSmsManager::NotifyCursorDone_t::signature[];
+
+constexpr char GeckoSmsManager::NotifyCursorError_t::name[];
+constexpr char GeckoSmsManager::NotifyCursorError_t::signature[];
+
+constexpr char GeckoSmsManager::NotifyGetSms_t::name[];
+constexpr char GeckoSmsManager::NotifyGetSms_t::signature[];
+
+constexpr char GeckoSmsManager::NotifyGetSmsFailed_t::name[];
+constexpr char GeckoSmsManager::NotifyGetSmsFailed_t::signature[];
+
+constexpr char GeckoSmsManager::NotifyMessageCursorResult_t::name[];
+constexpr char GeckoSmsManager::NotifyMessageCursorResult_t::signature[];
+
+constexpr char GeckoSmsManager::NotifySmsDeleteFailed_t::name[];
+constexpr char GeckoSmsManager::NotifySmsDeleteFailed_t::signature[];
+
+constexpr char GeckoSmsManager::NotifySmsDeleted_t::name[];
+constexpr char GeckoSmsManager::NotifySmsDeleted_t::signature[];
+
+constexpr char GeckoSmsManager::NotifySmsDelivery_t::name[];
+constexpr char GeckoSmsManager::NotifySmsDelivery_t::signature[];
+
+constexpr char GeckoSmsManager::NotifySmsReceived_t::name[];
+constexpr char GeckoSmsManager::NotifySmsReceived_t::signature[];
+
+constexpr char GeckoSmsManager::NotifySmsSendFailed_t::name[];
+constexpr char GeckoSmsManager::NotifySmsSendFailed_t::signature[];
+
+constexpr char GeckoSmsManager::NotifySmsSent_t::name[];
+constexpr char GeckoSmsManager::NotifySmsSent_t::signature[];
+
+constexpr char GeckoSmsManager::NotifyThreadCursorResult_t::name[];
+constexpr char GeckoSmsManager::NotifyThreadCursorResult_t::signature[];
+
 constexpr char GeckoThread::name[];
 
 constexpr char GeckoThread::PumpMessageLoop_t::name[];
 constexpr char GeckoThread::PumpMessageLoop_t::signature[];
 
 auto GeckoThread::PumpMessageLoop(mozilla::jni::Object::Param a0) -> bool
 {
     return mozilla::jni::Method<PumpMessageLoop_t>::Call(nullptr, nullptr, a0);
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -250,34 +250,16 @@ public:
         static const bool isMultithreaded = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     static auto CheckURIVisited(mozilla::jni::String::Param) -> void;
 
 public:
-    struct ClearMessageList_t {
-        typedef GeckoAppShell Owner;
-        typedef void ReturnType;
-        typedef void SetterType;
-        typedef mozilla::jni::Args<
-                int32_t> Args;
-        static constexpr char name[] = "clearMessageList";
-        static constexpr char signature[] =
-                "(I)V";
-        static const bool isStatic = true;
-        static const bool isMultithreaded = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-    };
-
-    static auto ClearMessageList(int32_t) -> void;
-
-public:
     struct CloseCamera_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "closeCamera";
         static constexpr char signature[] =
                 "()V";
@@ -339,41 +321,42 @@ public:
         static const bool isMultithreaded = true;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     static auto CreateInputStream(mozilla::jni::Object::Param) -> mozilla::jni::Object::LocalRef;
 
 public:
-    struct CreateMessageListWrapper_t {
+    struct CreateMessageCursorWrapper_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 int64_t,
                 int64_t,
                 mozilla::jni::ObjectArray::Param,
                 int32_t,
                 mozilla::jni::String::Param,
                 bool,
                 bool,
+                bool,
                 int64_t,
                 bool,
                 int32_t> Args;
-        static constexpr char name[] = "createMessageList";
-        static constexpr char signature[] =
-                "(JJ[Ljava/lang/String;ILjava/lang/String;ZZJZI)V";
+        static constexpr char name[] = "createMessageCursor";
+        static constexpr char signature[] =
+                "(JJ[Ljava/lang/String;ILjava/lang/String;ZZZJZI)V";
         static const bool isStatic = true;
         static const bool isMultithreaded = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
-    static auto CreateMessageListWrapper(int64_t, int64_t, mozilla::jni::ObjectArray::Param, int32_t, mozilla::jni::String::Param, bool, bool, int64_t, bool, int32_t) -> void;
+    static auto CreateMessageCursorWrapper(int64_t, int64_t, mozilla::jni::ObjectArray::Param, int32_t, mozilla::jni::String::Param, bool, bool, bool, int64_t, bool, int32_t) -> void;
 
 public:
     struct CreateShortcut_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 mozilla::jni::String::Param,
@@ -386,16 +369,34 @@ public:
         static const bool isMultithreaded = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     static auto CreateShortcut(mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param) -> void;
 
 public:
+    struct CreateThreadCursorWrapper_t {
+        typedef GeckoAppShell Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t> Args;
+        static constexpr char name[] = "createThreadCursor";
+        static constexpr char signature[] =
+                "(I)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+    static auto CreateThreadCursorWrapper(int32_t) -> void;
+
+public:
     struct DeleteMessageWrapper_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 int32_t,
                 int32_t> Args;
         static constexpr char name[] = "deleteMessage";
@@ -899,33 +900,50 @@ public:
         static const bool isMultithreaded = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
     static auto GetMimeTypeFromExtensionsWrapper(mozilla::jni::String::Param) -> mozilla::jni::String::LocalRef;
 
 public:
-    struct GetNextMessageInListWrapper_t {
+    struct GetNextMessageWrapper_t {
         typedef GeckoAppShell Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
-                int32_t,
                 int32_t> Args;
-        static constexpr char name[] = "getNextMessageInList";
-        static constexpr char signature[] =
-                "(II)V";
+        static constexpr char name[] = "getNextMessage";
+        static constexpr char signature[] =
+                "(I)V";
         static const bool isStatic = true;
         static const bool isMultithreaded = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
-    static auto GetNextMessageInListWrapper(int32_t, int32_t) -> void;
+    static auto GetNextMessageWrapper(int32_t) -> void;
+
+public:
+    struct GetNextThreadWrapper_t {
+        typedef GeckoAppShell Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t> Args;
+        static constexpr char name[] = "getNextThread";
+        static constexpr char signature[] =
+                "(I)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+    static auto GetNextThreadWrapper(int32_t) -> void;
 
 public:
     struct GetProxyForURIWrapper_t {
         typedef GeckoAppShell Owner;
         typedef mozilla::jni::String::LocalRef ReturnType;
         typedef mozilla::jni::String::Param SetterType;
         typedef mozilla::jni::Args<
                 mozilla::jni::String::Param,
@@ -1901,16 +1919,264 @@ public:
     };
 
     static auto UnpauseJavaProfiling() -> void;
 
 public:
     template<class Impl> class Natives;
 };
 
+class GeckoSmsManager : public mozilla::jni::Class<GeckoSmsManager>
+{
+public:
+    typedef mozilla::jni::Ref<GeckoSmsManager> Ref;
+    typedef mozilla::jni::LocalRef<GeckoSmsManager> LocalRef;
+    typedef mozilla::jni::GlobalRef<GeckoSmsManager> GlobalRef;
+    typedef const mozilla::jni::Param<GeckoSmsManager>& Param;
+
+    static constexpr char name[] =
+            "org/mozilla/gecko/GeckoSmsManager";
+
+protected:
+    GeckoSmsManager(jobject instance) : Class(instance) {}
+
+public:
+    struct NotifyCursorDone_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t> Args;
+        static constexpr char name[] = "notifyCursorDone";
+        static constexpr char signature[] =
+                "(I)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifyCursorError_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t> Args;
+        static constexpr char name[] = "notifyCursorError";
+        static constexpr char signature[] =
+                "(II)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifyGetSms_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t,
+                mozilla::jni::String::Param,
+                mozilla::jni::String::Param,
+                mozilla::jni::String::Param,
+                int64_t,
+                bool,
+                int32_t> Args;
+        static constexpr char name[] = "notifyGetSms";
+        static constexpr char signature[] =
+                "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;JZI)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifyGetSmsFailed_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t> Args;
+        static constexpr char name[] = "notifyGetSmsFailed";
+        static constexpr char signature[] =
+                "(II)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifyMessageCursorResult_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t,
+                mozilla::jni::String::Param,
+                mozilla::jni::String::Param,
+                mozilla::jni::String::Param,
+                int64_t,
+                int64_t,
+                bool,
+                int32_t> Args;
+        static constexpr char name[] = "notifyMessageCursorResult";
+        static constexpr char signature[] =
+                "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;JJZI)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifySmsDeleteFailed_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t> Args;
+        static constexpr char name[] = "notifySmsDeleteFailed";
+        static constexpr char signature[] =
+                "(II)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifySmsDeleted_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                bool,
+                int32_t> Args;
+        static constexpr char name[] = "notifySmsDeleted";
+        static constexpr char signature[] =
+                "(ZI)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifySmsDelivery_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t,
+                mozilla::jni::String::Param,
+                mozilla::jni::String::Param,
+                int64_t> Args;
+        static constexpr char name[] = "notifySmsDelivery";
+        static constexpr char signature[] =
+                "(IILjava/lang/String;Ljava/lang/String;J)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifySmsReceived_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                mozilla::jni::String::Param,
+                mozilla::jni::String::Param,
+                int32_t,
+                int64_t> Args;
+        static constexpr char name[] = "notifySmsReceived";
+        static constexpr char signature[] =
+                "(Ljava/lang/String;Ljava/lang/String;IJ)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifySmsSendFailed_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                int32_t> Args;
+        static constexpr char name[] = "notifySmsSendFailed";
+        static constexpr char signature[] =
+                "(II)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifySmsSent_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int32_t,
+                mozilla::jni::String::Param,
+                mozilla::jni::String::Param,
+                int64_t,
+                int32_t> Args;
+        static constexpr char name[] = "notifySmsSent";
+        static constexpr char signature[] =
+                "(ILjava/lang/String;Ljava/lang/String;JI)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    struct NotifyThreadCursorResult_t {
+        typedef GeckoSmsManager Owner;
+        typedef void ReturnType;
+        typedef void SetterType;
+        typedef mozilla::jni::Args<
+                int64_t,
+                mozilla::jni::String::Param,
+                mozilla::jni::String::Param,
+                int64_t,
+                mozilla::jni::ObjectArray::Param,
+                int64_t,
+                mozilla::jni::String::Param,
+                int32_t> Args;
+        static constexpr char name[] = "notifyThreadCursorResult";
+        static constexpr char signature[] =
+                "(JLjava/lang/String;Ljava/lang/String;J[Ljava/lang/Object;JLjava/lang/String;I)V";
+        static const bool isStatic = true;
+        static const bool isMultithreaded = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+public:
+    template<class Impl> class Natives;
+};
+
 class GeckoThread : public mozilla::jni::Class<GeckoThread>
 {
 public:
     typedef mozilla::jni::Ref<GeckoThread> Ref;
     typedef mozilla::jni::LocalRef<GeckoThread> LocalRef;
     typedef mozilla::jni::GlobalRef<GeckoThread> GlobalRef;
     typedef const mozilla::jni::Param<GeckoThread>& Param;