mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
Bug 810091 - B2G MMS: Don't download message twice on receiving duplicated notification. r=vyang, r=gene.lian
This commit is contained in:
parent
56bd55c05b
commit
8309216d97
@ -946,6 +946,7 @@ MmsService.prototype = {
|
||||
intermediate.deliveryStatus = [DELIVERY_STATUS_PENDING];
|
||||
intermediate.timestamp = Date.now();
|
||||
intermediate.sender = null;
|
||||
intermediate.transactionId = intermediate.headers["x-mms-transaction-id"];
|
||||
if (intermediate.headers.from) {
|
||||
intermediate.sender = intermediate.headers.from.address;
|
||||
} else {
|
||||
@ -1047,6 +1048,147 @@ MmsService.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* A helper function to broadcast the mms sent system message and notify observers.
|
||||
*
|
||||
* @params aDomMessage
|
||||
* The nsIDOMMozMmsMessage object.
|
||||
*/
|
||||
broadcastSentMessageEvent: function broadcastSentMessageEvent(aDomMessage) {
|
||||
// Broadcasting a 'sms-sent' system message to open apps.
|
||||
this.broadcastMmsSystemMessage("sms-sent", aDomMessage);
|
||||
|
||||
// Notifying observers an MMS message is sent.
|
||||
Services.obs.notifyObservers(aDomMessage, kSmsSentObserverTopic, null);
|
||||
},
|
||||
|
||||
/**
|
||||
* A helper function to broadcast the mms received system message and notify observers.
|
||||
*
|
||||
* @params aDomMessage
|
||||
* The nsIDOMMozMmsMessage object.
|
||||
*/
|
||||
broadcastReceivedMessageEvent :function broadcastReceivedMessageEvent(aDomMessage) {
|
||||
// Broadcasting a 'sms-received' system message to open apps.
|
||||
this.broadcastMmsSystemMessage("sms-received", aDomMessage);
|
||||
|
||||
// Notifying observers an MMS message is comming.
|
||||
Services.obs.notifyObservers(aDomMessage, kSmsReceivedObserverTopic, null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for retrieveMessage.
|
||||
*/
|
||||
retrieveMessageCallback: function retrieveMessageCallback(wish,
|
||||
savableMessage,
|
||||
mmsStatus,
|
||||
retrievedMessage) {
|
||||
debug("retrievedMessage = " + JSON.stringify(retrievedMessage));
|
||||
|
||||
// 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);
|
||||
let transactionId = retrievedMessage.headers["x-mms-transaction-id"];
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
this.broadcastReceivedMessageEvent(domMessage);
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for saveReceivedMessage.
|
||||
*/
|
||||
saveReceivedMessageCallback: function saveReceivedMessageCallback(savableMessage,
|
||||
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;
|
||||
}
|
||||
|
||||
// For X-Mms-Report-Allowed and X-Mms-Transaction-Id
|
||||
let wish = savableMessage.headers["x-mms-delivery-report"];
|
||||
let transactionId = savableMessage.headers["x-mms-transaction-id"];
|
||||
|
||||
this.broadcastReceivedMessageEvent(domMessage);
|
||||
|
||||
let retrievalMode = RETRIEVAL_MODE_MANUAL;
|
||||
try {
|
||||
retrievalMode = Services.prefs.getCharPref(PREF_RETRIEVAL_MODE);
|
||||
} catch (e) {}
|
||||
|
||||
let isRoaming = gMmsConnection.isDataConnRoaming();
|
||||
if ((retrievalMode === RETRIEVAL_MODE_AUTOMATIC_HOME && isRoaming) ||
|
||||
RETRIEVAL_MODE_MANUAL === retrievalMode ||
|
||||
RETRIEVAL_MODE_NEVER === retrievalMode) {
|
||||
let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode
|
||||
? MMS.MMS_PDU_STATUS_REJECTED
|
||||
: MMS.MMS_PDU_STATUS_DEFERRED;
|
||||
|
||||
// For X-Mms-Report-Allowed
|
||||
let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport,
|
||||
wish);
|
||||
|
||||
let transaction = new NotifyResponseTransaction(transactionId,
|
||||
mmsStatus,
|
||||
reportAllowed);
|
||||
transaction.run();
|
||||
return;
|
||||
}
|
||||
let url = savableMessage.headers["x-mms-content-location"].uri;
|
||||
|
||||
// For RETRIEVAL_MODE_AUTOMATIC or RETRIEVAL_MODE_AUTOMATIC_HOME but not
|
||||
// roaming, proceed to retrieve MMS.
|
||||
this.retrieveMessage(url, this.retrieveMessageCallback.bind(this, wish, savableMessage));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle incoming M-Notification.ind PDU.
|
||||
*
|
||||
@ -1054,118 +1196,21 @@ MmsService.prototype = {
|
||||
* The parsed MMS message object.
|
||||
*/
|
||||
handleNotificationIndication: function handleNotificationIndication(notification) {
|
||||
let url = notification.headers["x-mms-content-location"].uri;
|
||||
// TODO: bug 810091 - don't download message twice when receiving duplicated
|
||||
// notification.
|
||||
|
||||
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.
|
||||
gMobileMessageDatabaseService.getMessageRecordByTransactionId(transactionId,
|
||||
(function (aRv, aMessageRecord) {
|
||||
if (Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR === aRv
|
||||
&& aMessageRecord) {
|
||||
debug("We already got the NotificationIndication with transactionId = "
|
||||
+ transactionId + " before.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Broadcasting an 'sms-received' system message to open apps.
|
||||
this.broadcastMmsSystemMessage("sms-received", domMessage);
|
||||
let savableMessage = this.convertIntermediateToSavable(notification);
|
||||
|
||||
// Notifying observers a new notification indication is coming.
|
||||
Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null);
|
||||
|
||||
let retrievalMode = RETRIEVAL_MODE_MANUAL;
|
||||
try {
|
||||
retrievalMode = Services.prefs.getCharPref(PREF_RETRIEVAL_MODE);
|
||||
} catch (e) {}
|
||||
|
||||
let isRoaming = gMmsConnection.isDataConnRoaming();
|
||||
if ((retrievalMode === RETRIEVAL_MODE_AUTOMATIC_HOME && isRoaming) ||
|
||||
RETRIEVAL_MODE_MANUAL === retrievalMode ||
|
||||
RETRIEVAL_MODE_NEVER === retrievalMode) {
|
||||
let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode
|
||||
? MMS.MMS_PDU_STATUS_REJECTED
|
||||
: MMS.MMS_PDU_STATUS_DEFERRED;
|
||||
|
||||
// For X-Mms-Report-Allowed
|
||||
let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport,
|
||||
wish);
|
||||
|
||||
let transaction = new NotifyResponseTransaction(transactionId,
|
||||
mmsStatus,
|
||||
reportAllowed);
|
||||
transaction.run();
|
||||
return;
|
||||
}
|
||||
|
||||
// For RETRIEVAL_MODE_AUTOMATIC or RETRIEVAL_MODE_AUTOMATIC_HOME but not
|
||||
// roaming, proceed to retrieve MMS.
|
||||
this.retrieveMessage(url, (function responseNotify(mmsStatus,
|
||||
retrievedMessage) {
|
||||
debug("retrievedMessage = " + JSON.stringify(retrievedMessage));
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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, kSmsReceivedObserverTopic, null);
|
||||
}).bind(this));
|
||||
}).bind(this));
|
||||
gMobileMessageDatabaseService
|
||||
.saveReceivedMessage(savableMessage,
|
||||
this.saveReceivedMessageCallback.bind(this, savableMessage));
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
@ -1307,9 +1352,8 @@ MmsService.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
self.broadcastMmsSystemMessage("sms-sent", aDomMessage);
|
||||
self.broadcastSentMessageEvent(domMessage);
|
||||
aRequest.notifyMessageSent(aDomMessage);
|
||||
Services.obs.notifyObservers(aDomMessage, kSmsSentObserverTopic, null);
|
||||
});
|
||||
};
|
||||
|
||||
@ -1411,9 +1455,7 @@ MmsService.prototype = {
|
||||
}
|
||||
// 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, kSmsReceivedObserverTopic, null);
|
||||
this.broadcastReceivedMessageEvent(domMessage);
|
||||
let transaction = new AcknowledgeTransaction(transactionId, reportAllowed);
|
||||
transaction.run();
|
||||
}).bind(this));
|
||||
|
@ -24,7 +24,7 @@ interface nsIRilMobileMessageDatabaseRecordCallback : nsISupports
|
||||
void notify(in nsresult aRv, in jsval aMessageRecord);
|
||||
};
|
||||
|
||||
[scriptable, uuid(8f49216f-bc0c-420e-b77e-7f1cbdcd245f)]
|
||||
[scriptable, uuid(0ead3154-542d-4e2c-a624-9e3cec504758)]
|
||||
interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
|
||||
{
|
||||
/**
|
||||
@ -40,6 +40,7 @@ interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
|
||||
* - |delivery| DOMString: the delivery state of received message
|
||||
* - |deliveryStatus| DOMString Array: the delivery status of received message
|
||||
* - |receivers| DOMString Array: the phone numbers of receivers
|
||||
* - |transactionId| DOMString: the transaction ID from MMS pdu header.
|
||||
*
|
||||
* Note: |deliveryStatus| should only contain single string to specify
|
||||
* the delivery status of MMS message for the phone owner self.
|
||||
@ -82,4 +83,12 @@ interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
|
||||
*/
|
||||
void getMessageRecordById(in long aMessageId,
|
||||
in nsIRilMobileMessageDatabaseRecordCallback aCallback);
|
||||
|
||||
/**
|
||||
* |aTransactionId| DOMString: the transaction ID of MMS pdu.
|
||||
* |aCallback| nsIRilMobileMessageDatabaseCallback: a callback which takes
|
||||
* result flag and message record as parameters.
|
||||
*/
|
||||
void getMessageRecordByTransactionId(in DOMString aTransactionId,
|
||||
in nsIRilMobileMessageDatabaseRecordCallback aCallback);
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ const RIL_GETTHREADSCURSOR_CID =
|
||||
|
||||
const DEBUG = false;
|
||||
const DB_NAME = "sms";
|
||||
const DB_VERSION = 8;
|
||||
const DB_VERSION = 9;
|
||||
const MESSAGE_STORE_NAME = "sms";
|
||||
const THREAD_STORE_NAME = "thread";
|
||||
const PARTICIPANT_STORE_NAME = "participant";
|
||||
@ -30,6 +30,7 @@ const MOST_RECENT_STORE_NAME = "most-recent";
|
||||
const DELIVERY_SENDING = "sending";
|
||||
const DELIVERY_SENT = "sent";
|
||||
const DELIVERY_RECEIVED = "received";
|
||||
const DELIVERY_NOT_DOWNLOADED = "not-downloaded";
|
||||
|
||||
const DELIVERY_STATUS_NOT_APPLICABLE = "not-applicable";
|
||||
const DELIVERY_STATUS_SUCCESS = "success";
|
||||
@ -200,6 +201,10 @@ MobileMessageDatabaseService.prototype = {
|
||||
if (DEBUG) debug("Upgrade to version 8. Add participant/thread stores.");
|
||||
self.upgradeSchema7(db, event.target.transaction);
|
||||
break;
|
||||
case 8:
|
||||
if (DEBUG) debug("Upgrade to version 9. Add transactionId index for incoming MMS.");
|
||||
self.upgradeSchema8(event.target.transaction);
|
||||
break;
|
||||
default:
|
||||
event.target.transaction.abort();
|
||||
callback("Old database version: " + event.oldVersion, null);
|
||||
@ -559,6 +564,39 @@ MobileMessageDatabaseService.prototype = {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Add transactionId index for MMS.
|
||||
*/
|
||||
upgradeSchema8: function upgradeSchema8(transaction) {
|
||||
let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
|
||||
|
||||
// Delete "transactionId" index.
|
||||
if (messageStore.indexNames.contains("transactionId")) {
|
||||
messageStore.deleteIndex("transactionId");
|
||||
}
|
||||
|
||||
// Create new "transactionId" indexes.
|
||||
messageStore.createIndex("transactionId", "transactionIdIndex", { unique: true });
|
||||
|
||||
// Populate new "transactionIdIndex" attributes.
|
||||
messageStore.openCursor().onsuccess = function(event) {
|
||||
let cursor = event.target.result;
|
||||
if (!cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
let messageRecord = cursor.value;
|
||||
if ("mms" == messageRecord.type &&
|
||||
(DELIVERY_NOT_DOWNLOADED == messageRecord.delivery ||
|
||||
DELIVERY_RECEIVED == messageRecord.delivery)) {
|
||||
messageRecord.transactionIdIndex =
|
||||
messageRecord.headers["x-mms-transaction-id"];
|
||||
cursor.update(messageRecord);
|
||||
}
|
||||
cursor.continue();
|
||||
};
|
||||
},
|
||||
|
||||
createDomMessageFromRecord: function createDomMessageFromRecord(aMessageRecord) {
|
||||
if (DEBUG) {
|
||||
debug("createDomMessageFromRecord: " + JSON.stringify(aMessageRecord));
|
||||
@ -908,6 +946,7 @@ MobileMessageDatabaseService.prototype = {
|
||||
if ((aMessage.type != "sms" && aMessage.type != "mms") ||
|
||||
(aMessage.type == "sms" && aMessage.messageClass == undefined) ||
|
||||
(aMessage.type == "mms" && (aMessage.delivery == undefined ||
|
||||
aMessage.transactionId == undefined ||
|
||||
!Array.isArray(aMessage.deliveryStatus) ||
|
||||
!Array.isArray(aMessage.receivers))) ||
|
||||
aMessage.sender == undefined ||
|
||||
@ -950,6 +989,10 @@ MobileMessageDatabaseService.prototype = {
|
||||
aMessage.readIndex = [FILTER_READ_UNREAD, timestamp];
|
||||
aMessage.read = FILTER_READ_UNREAD;
|
||||
|
||||
if (aMessage.type == "mms") {
|
||||
aMessage.transactionIdIndex = aMessage.transactionId;
|
||||
}
|
||||
|
||||
if (aMessage.type == "sms") {
|
||||
aMessage.delivery = DELIVERY_RECEIVED;
|
||||
aMessage.deliveryStatus = DELIVERY_STATUS_SUCCESS;
|
||||
@ -1120,6 +1163,37 @@ MobileMessageDatabaseService.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
getMessageRecordByTransactionId: function getMessageRecordByTransactionId(aTransactionId, aCallback) {
|
||||
if (DEBUG) debug("Retrieving message with transaction ID " + aTransactionId);
|
||||
this.newTxn(READ_ONLY, function (error, txn, messageStore) {
|
||||
if (error) {
|
||||
if (DEBUG) debug(error);
|
||||
aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null);
|
||||
return;
|
||||
}
|
||||
let request = messageStore.index("transactionId").get(aTransactionId);
|
||||
|
||||
txn.oncomplete = function oncomplete(event) {
|
||||
if (DEBUG) debug("Transaction " + txn + " completed.");
|
||||
let messageRecord = request.result;
|
||||
if (!messageRecord) {
|
||||
if (DEBUG) debug("Transaction ID " + aTransactionId + " not found");
|
||||
aCallback.notify(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR, null);
|
||||
return;
|
||||
}
|
||||
aCallback.notify(Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR, messageRecord);
|
||||
};
|
||||
|
||||
txn.onerror = function onerror(event) {
|
||||
if (DEBUG) {
|
||||
if (event.target)
|
||||
debug("Caught error on transaction", event.target.errorCode);
|
||||
}
|
||||
aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
getMessageRecordById: function getMessageRecordById(aMessageId, aCallback) {
|
||||
if (DEBUG) debug("Retrieving message with ID " + aMessageId);
|
||||
this.newTxn(READ_ONLY, function (error, txn, messageStore) {
|
||||
|
Loading…
Reference in New Issue
Block a user