Bug 843445 - B2G MMS: Provide nsIDOMMobileMessageManager.retrieveMMS() to retrieve MMS for the deferred retrieval mode, part-2:mms_service. r=vyang, r=gene.lian, sicking, a=leo+
authorVicamo Yang <vyang@mozilla.com>
Thu, 18 Apr 2013 09:09:31 +0800
changeset 119089 3847fb528e5598970367958556ee1c1444b53609
parent 119088 51c4987b8206b7ffb22b1fe364b73a1b775f34ba
child 119090 92af4c10ec85f73c8bacfab533c7bc786d0f0d93
push id680
push uservyang@mozilla.com
push dateThu, 18 Apr 2013 01:11:41 +0000
reviewersvyang, gene, leo
bugs843445
milestone18.0
Bug 843445 - B2G MMS: Provide nsIDOMMobileMessageManager.retrieveMMS() to retrieve MMS for the deferred retrieval mode, part-2:mms_service. r=vyang, r=gene.lian, sicking, a=leo+
dom/mms/src/ril/MmsService.js
dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
--- a/dom/mms/src/ril/MmsService.js
+++ b/dom/mms/src/ril/MmsService.js
@@ -1049,117 +1049,114 @@ MmsService.prototype = {
 
     let transactionId = notification.headers["x-mms-transaction-id"];
     // For X-Mms-Report-Allowed
     let wish = notification.headers["x-mms-delivery-report"];
 
     let savableMessage = this.convertIntermediateToSavable(notification);
 
     gMobileMessageDatabaseService.saveReceivedMessage(savableMessage,
-      (function (rv, domMessage) {
-        let success = Components.isSuccessCode(rv);
-        if (!success) {
-          // At this point we could send a message to content to notify the
-          // user that storing an incoming MMS notification indication failed,
-          // ost likely due to a full disk.
-          debug("Could not store MMS " + JSON.stringify(savableMessage) +
-                ", error code " + rv);
-          // Because MMSC will resend the notification indication once we don't
-          // response the notification. Hope the end user will clean some space
-          // for the resent notification indication.
-          return;
-        }
+        (function (rv, domMessage) {
+      let success = Components.isSuccessCode(rv);
+      if (!success) {
+        // At this point we could send a message to content to notify the
+        // user that storing an incoming MMS notification indication failed,
+        // ost likely due to a full disk.
+        debug("Could not store MMS " + JSON.stringify(savableMessage) +
+              ", error code " + rv);
+        // Because MMSC will resend the notification indication once we don't
+        // response the notification. Hope the end user will clean some space
+        // for the resent notification indication.
+        return;
+      }
+
+      // Broadcasting an 'sms-received' system message to open apps.
+      this.broadcastMmsSystemMessage("sms-received", domMessage);
+
+      // Notifying observers a new notification indication is coming.
+      Services.obs.notifyObservers(domMessage, kMmsReceivedObserverTopic, null);
+
+      let retrievalMode = RETRIEVAL_MODE_MANUAL;
+      try {
+        retrievalMode = Services.prefs.getCharPref(PREF_RETRIEVAL_MODE);
+      } catch (e) {}
 
-        // Broadcasting an 'sms-received' system message to open apps.
-        this.broadcastMmsSystemMessage("sms-received", domMessage);
+      if (RETRIEVAL_MODE_AUTOMATIC !== retrievalMode) {
+        let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode
+                      ? MMS.MMS_PDU_STATUS_REJECTED
+                      : MMS.MMS_PDU_STATUS_DEFERRED;
 
-        // Notifying observers a new notification indication is coming.
-        Services.obs.notifyObservers(domMessage, kMmsReceivedObserverTopic, null);
+        // For X-Mms-Report-Allowed
+        let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport,
+                                                  wish);
 
-        let retrievalMode = RETRIEVAL_MODE_MANUAL;
-        try {
-          retrievalMode = Services.prefs.getCharPref(PREF_RETRIEVAL_MODE);
-        } catch (e) {}
+        let transaction = new NotifyResponseTransaction(transactionId,
+                                                        mmsStatus,
+                                                        reportAllowed);
+        transaction.run();
+        return;
+      }
 
-        if (RETRIEVAL_MODE_AUTOMATIC !== retrievalMode) {
-          let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode
-                        ? MMS.MMS_PDU_STATUS_REJECTED
-                        : MMS.MMS_PDU_STATUS_DEFERRED;
+      // For RETRIEVAL_MODE_AUTOMATIC, proceed to retrieve MMS.
+      this.retrieveMessage(url, (function responseNotify(mmsStatus,
+                                                         retrievedMessage) {
+        debug("retrievedMessage = " + JSON.stringify(retrievedMessage));
 
-          // For X-Mms-Report-Allowed
-          let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport,
+        // The absence of the field does not indicate any default
+        // value. So we go check the same field in the retrieved
+        // message instead.
+        if (wish == null && retrievedMessage) {
+          wish = retrievedMessage.headers["x-mms-delivery-report"];
+        }
+        let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport,
                                                     wish);
 
+        // If the mmsStatus isn't MMS_PDU_STATUS_RETRIEVED after retrieving,
+        // something must be wrong with MMSC, so stop updating the DB record.
+        // We could send a message to content to notify the user the MMS
+        // retrieving failed. The end user has to retrieve the MMS again.
+        if (MMS.MMS_PDU_STATUS_RETRIEVED !== mmsStatus) {
           let transaction = new NotifyResponseTransaction(transactionId,
                                                           mmsStatus,
                                                           reportAllowed);
           transaction.run();
           return;
         }
 
-        // For RETRIEVAL_MODE_AUTOMATIC, proceed to retrieve MMS.
-        this.retrieveMessage(url, (function responseNotify(mmsStatus,
-                                                           retrievedMessage) {
-          debug("retrievedMessage = " + JSON.stringify(retrievedMessage));
+        savableMessage = this.mergeRetrievalConfirmation(retrievedMessage,
+                                                         savableMessage);
 
-          // The absence of the field does not indicate any default
-          // value. So we go check the same field in the retrieved
-          // message instead.
-          if (wish == null && retrievedMessage) {
-            wish = retrievedMessage.headers["x-mms-delivery-report"];
-          }
-          let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport,
-                                                    wish);
+        gMobileMessageDatabaseService.saveReceivedMessage(savableMessage,
+            (function (rv, domMessage) {
+          let success = Components.isSuccessCode(rv);
+          let transaction =
+            new NotifyResponseTransaction(transactionId,
+                                          success ? MMS.MMS_PDU_STATUS_RETRIEVED
+                                                  : MMS.MMS_PDU_STATUS_DEFERRED,
+                                          reportAllowed);
+          transaction.run();
 
-          // If the mmsStatus isn't MMS_PDU_STATUS_RETRIEVED after retrieving,
-          // something must be wrong with MMSC, so stop updating the DB record.
-          // We could send a message to content to notify the user the MMS
-          // retrieving failed. The end user has to retrieve the MMS again.
-          if (MMS.MMS_PDU_STATUS_RETRIEVED !== mmsStatus) {
-            let transaction =
-              new NotifyResponseTransaction(transactionId,
-                                            mmsStatus,
-                                            reportAllowed);
-            transaction.run();
+          if (!success) {
+            // At this point we could send a message to content to
+            // notify the user that storing an incoming MMS failed,
+            // most likely due to a full disk. The end user has to
+            // retrieve the MMS again.
+            debug("Could not store MMS " + domMessage.id +
+                  ", error code " + rv);
             return;
           }
 
-          savableMessage = this.mergeRetrievalConfirmation(retrievedMessage,
-                                                           savableMessage);
-
-          gMobileMessageDatabaseService.saveReceivedMessage(savableMessage,
-            (function (rv, domMessage) {
-              let success = Components.isSuccessCode(rv);
-              let transaction =
-                new NotifyResponseTransaction(transactionId,
-                                              success ? MMS.MMS_PDU_STATUS_RETRIEVED
-                                                      : MMS.MMS_PDU_STATUS_DEFERRED,
-                                              reportAllowed);
-              transaction.run();
+          // Broadcasting an 'sms-received' system message to open apps.
+          this.broadcastMmsSystemMessage("sms-received", domMessage);
 
-              if (!success) {
-                // At this point we could send a message to content to
-                // notify the user that storing an incoming MMS failed,
-                // most likely due to a full disk. The end user has to
-                // retrieve the MMS again.
-                debug("Could not store MMS " + domMessage.id +
-                      ", error code " + rv);
-                return;
-              }
-
-              // Broadcasting an 'sms-received' system message to open apps.
-              this.broadcastMmsSystemMessage("sms-received", domMessage);
-
-              // Notifying observers an MMS message is received.
-              Services.obs.notifyObservers(domMessage, kMmsReceivedObserverTopic, null);
-            }).bind(this)
-          );
+          // Notifying observers an MMS message is received.
+          Services.obs.notifyObservers(domMessage, kMmsReceivedObserverTopic, null);
         }).bind(this));
-      }).bind(this)
-    );
+      }).bind(this));
+    }).bind(this));
   },
 
   /**
    * Handle incoming M-Delivery.ind PDU.
    *
    * @param msg
    *        The MMS message object.
    */
@@ -1319,16 +1316,90 @@ MmsService.prototype = {
       sendTransaction.run(function callback(aMmsStatus, aMsg) {
         let isSentSuccess = (aMmsStatus == MMS.MMS_PDU_ERROR_OK);
         debug("The sending status of sendTransaction.run(): " + aMmsStatus);
         sendTransactionCb(aDomMessage.id, isSentSuccess);
       });
     });
   },
 
+  retrieve: function retrieve(id, aRequest) {
+    gMobileMessageDatabaseService.getMessageRecordById(id,
+        (function notifyResult(aRv, aMessageRecord) {
+      if (Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR != aRv) {
+        debug("Function getMessageRecordById() return error.");
+        aRequest.notifyGetMessageFailed(aRv);
+        return;
+      }
+      if ("mms" != aMessageRecord.type) {
+        debug("Type of message record is not mms");
+        aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+        return;
+      }
+      if (!aMessageRecord.headers ||
+          !aMessageRecord.headers["x-mms-content-location"]) {
+        debug("Can't find mms content url in database.");
+        aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+        return;
+      }
+
+      let url =  aMessageRecord.headers["x-mms-content-location"].uri;
+      // For X-Mms-Report-Allowed
+      let wish = aMessageRecord.headers["x-mms-delivery-report"];
+      this.retrieveMessage(url, (function responseNotify(mmsStatus, retrievedMsg) {
+        // If the mmsStatus is still MMS_PDU_STATUS_DEFERRED after retry,
+        // we should not store it into database.
+        if (MMS.MMS_PDU_STATUS_RETRIEVED !== mmsStatus) {
+          debug("RetrieveMessage fail after retry.");
+          aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+          return;
+        }
+        // In OMA-TS-MMS_ENC-V1_3, Table 5 in page 25. This header field
+        // (x-mms-transaction-id) SHALL be present when the MMS Proxy relay
+        // seeks an acknowledgement for the MM delivered though M-Retrieve.conf
+        // PDU during deferred retrieval. This transaction ID is used by the MMS
+        // Client and MMS Proxy-Relay to provide linkage between the originated
+        // M-Retrieve.conf and the response M-Acknowledge.ind PDUs.
+        let transactionId = retrievedMsg.headers["x-mms-transaction-id"];
+
+        // The absence of the field does not indicate any default
+        // value. So we go checking the same field in retrieved
+        // message instead.
+        if (wish == null && retrievedMsg) {
+          wish = retrievedMsg.headers["x-mms-delivery-report"];
+        }
+        let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport,
+                                                  wish);
+
+        debug("retrievedMsg = " + JSON.stringify(retrievedMsg));
+        aMessageRecord = this.mergeRetrievalConfirmation(retrievedMsg, aMessageRecord);
+        gMobileMessageDatabaseService.saveReceivedMessage(aMessageRecord,
+                                                          (function (rv, domMessage) {
+          let success = Components.isSuccessCode(rv);
+          if (!success) {
+            // At this point we could send a message to content to
+            // notify the user that storing an incoming MMS failed, most
+            // likely due to a full disk.
+            debug("Could not store MMS " + domMessage.id +
+                  ", error code " + rv);
+            aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+            return;
+          }
+          // Notifying observers a new MMS message is retrieved.
+          aRequest.notifyMessageGot(domMessage);
+          // Broadcasting an 'sms-received' system message to open apps.
+          this.broadcastMmsSystemMessage("sms-received", domMessage);
+          Services.obs.notifyObservers(domMessage, kMmsReceivedObserverTopic, null);
+          let transaction = new AcknowledgeTransaction(transactionId, reportAllowed);
+          transaction.run();
+        }).bind(this));
+      }).bind(this));
+    }).bind(this));
+  },
+
   // nsIWapPushApplication
 
   receiveWapPush: function receiveWapPush(array, length, offset, options) {
     let data = {array: array, offset: offset};
     let msg = MMS.PduHelper.parse(data, null);
     if (!msg) {
       return false;
     }
--- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
@@ -1336,67 +1336,81 @@ MobileMessageDatabaseService.prototype =
         if (DEBUG) {
           debug("The record's delivery and/or deliveryStatus are updated.");
         }
         messageStore.put(messageRecord);
       };
     });
   },
 
-  /**
-   * nsIMobileMessageDatabaseService API
-   */
-
-  getMessage: function getMessage(messageId, aRequest) {
-    if (DEBUG) debug("Retrieving message with ID " + messageId);
-    let self = this;
+  getMessageRecordById: function getMessageRecordById(aMessageId, aCallback) {
+    if (DEBUG) debug("Retrieving message with ID " + aMessageId);
     this.newTxn(READ_ONLY, function (error, txn, messageStore) {
       if (error) {
         if (DEBUG) debug(error);
-        aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null);
         return;
       }
-      let request = messageStore.mozGetAll(messageId);
+      let request = messageStore.mozGetAll(aMessageId);
 
       txn.oncomplete = function oncomplete() {
         if (DEBUG) debug("Transaction " + txn + " completed.");
         if (request.result.length > 1) {
-          if (DEBUG) debug("Got too many results for id " + messageId);
-          aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR);
+          if (DEBUG) debug("Got too many results for id " + aMessageId);
+          aCallback.notify(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR, null);
           return;
         }
         let messageRecord = request.result[0];
         if (!messageRecord) {
-          if (DEBUG) debug("Message ID " + messageId + " not found");
-          aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
+          if (DEBUG) debug("Message ID " + aMessageId + " not found");
+          aCallback.notify(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR, null);
           return;
         }
-        if (messageRecord.id != messageId) {
+        if (messageRecord.id != aMessageId) {
           if (DEBUG) {
-            debug("Requested message ID (" + messageId + ") is " +
+            debug("Requested message ID (" + aMessageId + ") is " +
                   "different from the one we got");
           }
-          aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR);
+          aCallback.notify(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR, null);
           return;
         }
-        let domMessage = self.createDomMessageFromRecord(messageRecord);
-        aRequest.notifyMessageGot(domMessage);
+        aCallback.notify(Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR, messageRecord);
       };
 
       txn.onerror = function onerror(event) {
         if (DEBUG) {
-          if (event.target)
+          if (event.target) {
             debug("Caught error on transaction", event.target.errorCode);
+          }
         }
-        //TODO look at event.target.errorCode, pick appropriate error constant
-        aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
+        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null);
       };
     });
   },
 
+  /**
+   * nsIMobileMessageDatabaseService API
+   */
+
+  getMessage: function getMessage(aMessageId, aRequest) {
+    if (DEBUG) debug("Retrieving message with ID " + aMessageId);
+    let self = this;
+    let notifyCallback = {
+      notify: function notify(aRv, aMessageRecord) {
+        if (Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR == aRv) {
+          let domMessage = self.createDomMessageFromRecord(aMessageRecord);
+          aRequest.notifyMessageGot(domMessage);
+          return;
+        }
+        aRequest.notifyGetMessageFailed(aRv, null);
+      }
+    };
+    this.getMessageRecordById(aMessageId, notifyCallback);
+  },
+
   deleteMessage: function deleteMessage(messageId, aRequest) {
     let deleted = false;
     let self = this;
     this.newTxn(READ_WRITE, function (error, txn, stores) {
       if (error) {
         aRequest.notifyDeleteMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
         return;
       }