Bug 880563 - B2G MMS: deleting an MMS during the sending or retrieving process doesn't work properly. r=vicamo,ctai a=leo+

This commit is contained in:
Gene Lian 2013-06-20 19:23:43 +08:00
Родитель d19eab7644
Коммит 1e2d59faea
2 изменённых файлов: 469 добавлений и 246 удалений

Просмотреть файл

@ -33,11 +33,18 @@ const kSmsRetrievingObserverTopic = "sms-retrieving";
const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
const kXpcomShutdownObserverTopic = "xpcom-shutdown";
const kPrefenceChangedObserverTopic = "nsPref:changed";
const kMobileMessageDeletedObserverTopic = "mobile-message-deleted";
// HTTP status codes:
// @see http://tools.ietf.org/html/rfc2616#page-39
const HTTP_STATUS_OK = 200;
// Non-standard HTTP status for internal use.
const _HTTP_STATUS_USER_CANCELLED = -1;
// Non-standard MMS status for internal use.
const _MMS_ERROR_MESSAGE_DELETED = -1;
const CONFIG_SEND_REPORT_NEVER = 0;
const CONFIG_SEND_REPORT_DEFAULT_NO = 1;
const CONFIG_SEND_REPORT_DEFAULT_YES = 2;
@ -397,7 +404,7 @@ MmsProxyFilter.prototype = {
};
XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
return {
let helper = {
/**
* Send MMS request to MMSC.
*
@ -413,14 +420,55 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
*/
sendRequest: function sendRequest(method, url, istream, callback) {
// TODO: bug 810226 - Support GPRS bearer for MMS transmission and reception.
let cancellable = {
callback: callback,
gMmsConnection.acquire((function (method, url, istream, callback,
connected) {
if (!connected) {
// Connection timeout or failed. Report error.
isDone: false,
isCancelled: false,
cancel: function cancel() {
if (this.isDone) {
// It's too late to cancel.
return;
}
this.isCancelled = true;
if (this.isAcquiringConn) {
// We cannot cancel data connection setup here, so we invoke done()
// here and handle |cancellable.isDone| in callback function of
// |gMmsConnection.acquire|.
this.done(_HTTP_STATUS_USER_CANCELLED, null);
} else if (this.xhr) {
// Client has already sent the HTTP request. Try to abort it.
this.xhr.abort();
}
},
done: function done(httpStatus, data) {
this.isDone = true;
if (!this.callback) {
return;
}
if (this.isCancelled) {
this.callback(_HTTP_STATUS_USER_CANCELLED, null);
} else {
this.callback(httpStatus, data);
}
}
};
cancellable.isAcquiringConn =
!gMmsConnection.acquire((function (connected) {
cancellable.isAcquiringConn = false;
if (!connected || cancellable.isCancelled) {
gMmsConnection.release();
if (callback) {
callback(0, null);
if (!cancellable.isDone) {
cancellable.done(cancellable.isCancelled ?
_HTTP_STATUS_USER_CANCELLED : 0, null);
}
return;
}
@ -429,84 +477,90 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
let proxyFilter = new MmsProxyFilter(url);
gpps.registerFilter(proxyFilter, 0);
let releaseMmsConnectionAndCallback = (function (httpStatus, data) {
gpps.unregisterFilter(proxyFilter);
// Always release the MMS network connection before callback.
gMmsConnection.release();
if (callback) {
callback(httpStatus, data);
}
}).bind(this);
cancellable.xhr = this.sendHttpRequest(method, url, istream, proxyFilter,
cancellable.done.bind(cancellable));
}).bind(this));
try {
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
return cancellable;
},
// Basic setups
xhr.open(method, url, true);
xhr.responseType = "arraybuffer";
if (istream) {
xhr.setRequestHeader("Content-Type",
"application/vnd.wap.mms-message");
xhr.setRequestHeader("Content-Length", istream.available());
}
sendHttpRequest: function sendHttpRequest(method, url, istream, proxyFilter,
callback) {
let releaseMmsConnectionAndCallback = function (httpStatus, data) {
gpps.unregisterFilter(proxyFilter);
// Always release the MMS network connection before callback.
gMmsConnection.release();
callback(httpStatus, data);
};
// UAProf headers.
let uaProfUrl, uaProfTagname = "x-wap-profile";
try {
uaProfUrl = Services.prefs.getCharPref('wap.UAProf.url');
uaProfTagname = Services.prefs.getCharPref('wap.UAProf.tagname');
} catch (e) {}
try {
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
if (uaProfUrl) {
xhr.setRequestHeader(uaProfTagname, uaProfUrl);
}
// Setup event listeners
xhr.onerror = function () {
if (DEBUG) debug("xhr error, response headers: " +
xhr.getAllResponseHeaders());
releaseMmsConnectionAndCallback(xhr.status, null);
};
xhr.onreadystatechange = function () {
if (xhr.readyState != Ci.nsIXMLHttpRequest.DONE) {
return;
}
let data = null;
switch (xhr.status) {
case HTTP_STATUS_OK: {
if (DEBUG) debug("xhr success, response headers: "
+ xhr.getAllResponseHeaders());
let array = new Uint8Array(xhr.response);
if (false) {
for (let begin = 0; begin < array.length; begin += 20) {
let partial = array.subarray(begin, begin + 20);
if (DEBUG) debug("res: " + JSON.stringify(partial));
}
}
data = {array: array, offset: 0};
break;
}
default: {
if (DEBUG) debug("xhr done, but status = " + xhr.status +
", statusText = " + xhr.statusText);
break;
}
}
releaseMmsConnectionAndCallback(xhr.status, data);
}
// Send request
xhr.send(istream);
} catch (e) {
if (DEBUG) debug("xhr error, can't send: " + e.message);
releaseMmsConnectionAndCallback(0, null);
// Basic setups
xhr.open(method, url, true);
xhr.responseType = "arraybuffer";
if (istream) {
xhr.setRequestHeader("Content-Type",
"application/vnd.wap.mms-message");
xhr.setRequestHeader("Content-Length", istream.available());
}
}).bind(this, method, url, istream, callback));
// UAProf headers.
let uaProfUrl, uaProfTagname = "x-wap-profile";
try {
uaProfUrl = Services.prefs.getCharPref('wap.UAProf.url');
uaProfTagname = Services.prefs.getCharPref('wap.UAProf.tagname');
} catch (e) {}
if (uaProfUrl) {
xhr.setRequestHeader(uaProfTagname, uaProfUrl);
}
// Setup event listeners
xhr.onerror = function () {
if (DEBUG) debug("xhr error, response headers: " +
xhr.getAllResponseHeaders());
releaseMmsConnectionAndCallback(xhr.status, null);
};
xhr.onreadystatechange = function () {
if (xhr.readyState != Ci.nsIXMLHttpRequest.DONE) {
return;
}
let data = null;
switch (xhr.status) {
case HTTP_STATUS_OK: {
if (DEBUG) debug("xhr success, response headers: "
+ xhr.getAllResponseHeaders());
let array = new Uint8Array(xhr.response);
if (false) {
for (let begin = 0; begin < array.length; begin += 20) {
let partial = array.subarray(begin, begin + 20);
if (DEBUG) debug("res: " + JSON.stringify(partial));
}
}
data = {array: array, offset: 0};
break;
}
default: {
if (DEBUG) debug("xhr done, but status = " + xhr.status +
", statusText = " + xhr.statusText);
break;
}
}
releaseMmsConnectionAndCallback(xhr.status, data);
};
// Send request
xhr.send(istream);
return xhr;
} catch (e) {
if (DEBUG) debug("xhr error, can't send: " + e.message);
releaseMmsConnectionAndCallback(0, null);
return null;
}
},
/**
@ -586,6 +640,8 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
return true;
}
};
return helper;
});
/**
@ -634,47 +690,144 @@ NotifyResponseTransaction.prototype = {
};
/**
* Retrieve message back from MMSC.
* CancellableTransaction - base class inherited by [Send|Retrieve]Transaction.
* @param cancellableId
* An ID used to keep track of if an message is deleted from DB.
*/
function CancellableTransaction(cancellableId) {
this.cancellableId = cancellableId;
this.isCancelled = false;
}
CancellableTransaction.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
// The timer for retrying sending or retrieving process.
timer: null,
// Keep a reference to the callback when calling
// |[Send|Retrieve]Transaction.run(callback)|.
runCallback: null,
isObserversAdded: false,
registerRunCallback: function registerRunCallback(callback) {
if (!this.isObserversAdded) {
Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
Services.obs.addObserver(this, kMobileMessageDeletedObserverTopic, false);
this.isObserversAdded = true;
}
this.runCallback = callback;
this.isCancelled = false;
},
removeObservers: function removeObservers() {
if (this.isObserversAdded) {
Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
Services.obs.removeObserver(this, kMobileMessageDeletedObserverTopic);
this.isObserversAdded = false;
}
},
runCallbackIfValid: function runCallbackIfValid(mmsStatus, msg) {
this.removeObservers();
if (this.runCallback) {
this.runCallback(mmsStatus, msg);
this.runCallback = null;
}
},
// Keep a reference to the cancellable when calling
// |gMmsTransactionHelper.sendRequest(...)|.
cancellable: null,
cancelRunning: function cancelRunning() {
this.isCancelled = true;
if (this.timer) {
// The sending or retrieving process is waiting for the next retry.
// What we only need to do is to cancel the timer.
this.timer.cancel();
this.timer = null;
this.runCallbackIfValid(_MMS_ERROR_MESSAGE_DELETED, null);
return;
}
if (this.cancellable) {
// The sending or retrieving process is still running. We attempt to
// abort the HTTP request.
this.cancellable.cancel();
this.cancellable = null;
}
},
// nsIObserver
observe: function observe(subject, topic, data) {
switch (topic) {
case kXpcomShutdownObserverTopic: {
this.cancelRunning();
break;
}
case kMobileMessageDeletedObserverTopic: {
data = JSON.parse(data);
if (data.id != this.cancellableId) {
return;
}
this.cancelRunning();
break;
}
}
}
};
/**
* Class for retrieving message from MMSC, which inherits CancellableTransaction.
*
* @param contentLocation
* X-Mms-Content-Location of the message.
*/
function RetrieveTransaction(contentLocation) {
function RetrieveTransaction(cancellableId, contentLocation) {
// Call |CancellableTransaction| constructor.
CancellableTransaction.call(this, cancellableId);
this.contentLocation = contentLocation;
}
RetrieveTransaction.prototype = {
/**
* We need to keep a reference to the timer to assure the timer is fired.
*/
timer: null,
RetrieveTransaction.prototype = Object.create(CancellableTransaction.prototype, {
/**
* @param callback [optional]
* A callback function that takes two arguments: one for X-Mms-Status,
* the other for the parsed M-Retrieve.conf message.
*/
run: function run(callback) {
this.retryCount = 0;
let that = this;
this.retrieve((function retryCallback(mmsStatus, msg) {
if (MMS.MMS_PDU_STATUS_DEFERRED == mmsStatus &&
that.retryCount < PREF_RETRIEVAL_RETRY_COUNT) {
if (that.timer == null) {
that.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
}
run: {
value: function run(callback) {
this.registerRunCallback(callback);
that.timer.initWithCallback((function (){
this.retrieve(retryCallback);
}).bind(that),
PREF_RETRIEVAL_RETRY_INTERVALS[that.retryCount],
Ci.nsITimer.TYPE_ONE_SHOT);
that.retryCount++;
return;
}
if (callback) {
callback(mmsStatus, msg);
}
}).bind(this));
this.retryCount = 0;
let that = this;
this.retrieve((function retryCallback(mmsStatus, msg) {
if (MMS.MMS_PDU_STATUS_DEFERRED == mmsStatus &&
that.retryCount < PREF_RETRIEVAL_RETRY_COUNT) {
if (that.timer == null) {
that.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
}
that.timer.initWithCallback((function (){
this.retrieve(retryCallback);
}).bind(that),
PREF_RETRIEVAL_RETRY_INTERVALS[that.retryCount],
Ci.nsITimer.TYPE_ONE_SHOT);
that.retryCount++;
return;
}
this.runCallbackIfValid(mmsStatus, msg);
}).bind(this));
},
enumerable: true,
configurable: true,
writable: true
},
/**
@ -682,44 +835,59 @@ RetrieveTransaction.prototype = {
* A callback function that takes two arguments: one for X-Mms-Status,
* the other for the parsed M-Retrieve.conf message.
*/
retrieve: function retrieve(callback) {
gMmsTransactionHelper.sendRequest("GET", this.contentLocation, null,
(function (httpStatus, data) {
if ((httpStatus != HTTP_STATUS_OK) || !data) {
callback(MMS.MMS_PDU_STATUS_DEFERRED, null);
return;
}
retrieve: {
value: function retrieve(callback) {
this.timer = null;
let retrieved = MMS.PduHelper.parse(data, null);
if (!retrieved || (retrieved.type != MMS.MMS_PDU_TYPE_RETRIEVE_CONF)) {
callback(MMS.MMS_PDU_STATUS_UNRECOGNISED, null);
return;
}
this.cancellable =
gMmsTransactionHelper.sendRequest("GET", this.contentLocation, null,
(function (httpStatus, data) {
if (httpStatus == _HTTP_STATUS_USER_CANCELLED) {
callback(_MMS_ERROR_MESSAGE_DELETED, null);
return;
}
// Fix default header field values.
if (retrieved.headers["x-mms-delivery-report"] == null) {
retrieved.headers["x-mms-delivery-report"] = false;
}
if ((httpStatus != HTTP_STATUS_OK) || !data) {
callback(MMS.MMS_PDU_STATUS_DEFERRED, null);
return;
}
let retrieveStatus = retrieved.headers["x-mms-retrieve-status"];
if ((retrieveStatus != null) &&
(retrieveStatus != MMS.MMS_PDU_ERROR_OK)) {
callback(MMS.translatePduErrorToStatus(retrieveStatus),
retrieved);
return;
}
let retrieved = MMS.PduHelper.parse(data, null);
if (!retrieved || (retrieved.type != MMS.MMS_PDU_TYPE_RETRIEVE_CONF)) {
callback(MMS.MMS_PDU_STATUS_UNRECOGNISED, null);
return;
}
callback(MMS.MMS_PDU_STATUS_RETRIEVED, retrieved);
}).bind(this));
// Fix default header field values.
if (retrieved.headers["x-mms-delivery-report"] == null) {
retrieved.headers["x-mms-delivery-report"] = false;
}
let retrieveStatus = retrieved.headers["x-mms-retrieve-status"];
if ((retrieveStatus != null) &&
(retrieveStatus != MMS.MMS_PDU_ERROR_OK)) {
callback(MMS.translatePduErrorToStatus(retrieveStatus), retrieved);
return;
}
callback(MMS.MMS_PDU_STATUS_RETRIEVED, retrieved);
}).bind(this));
},
enumerable: true,
configurable: true,
writable: true
}
};
});
/**
* SendTransaction.
* Class for sending M-Send.req to MMSC
* Class for sending M-Send.req to MMSC, which inherits CancellableTransaction.
* @throws Error("Check max values parameters fail.")
*/
function SendTransaction(msg) {
function SendTransaction(cancellableId, msg) {
// Call |CancellableTransaction| constructor.
CancellableTransaction.call(this, cancellableId);
msg.headers["x-mms-message-type"] = MMS.MMS_PDU_TYPE_SEND_REQ;
if (!msg.headers["x-mms-transaction-id"]) {
// Create an unique transaction id
@ -775,13 +943,13 @@ function SendTransaction(msg) {
this.msg = msg;
}
SendTransaction.prototype = {
/**
* We need to keep a reference to the timer to assure the timer is fired.
*/
timer: null,
istreamComposed: false,
SendTransaction.prototype = Object.create(CancellableTransaction.prototype, {
istreamComposed: {
value: false,
enumerable: true,
configurable: true,
writable: true
},
/**
* @param parts
@ -789,42 +957,47 @@ SendTransaction.prototype = {
* @param callback [optional]
* A callback function that takes zero argument.
*/
loadBlobs: function loadBlobs(parts, callback) {
let callbackIfValid = function callbackIfValid() {
if (DEBUG) debug("All parts loaded: " + JSON.stringify(parts));
if (callback) {
callback();
}
}
if (!parts || !parts.length) {
callbackIfValid();
return;
}
let numPartsToLoad = parts.length;
for each (let part in parts) {
if (!(part.content instanceof Ci.nsIDOMBlob)) {
numPartsToLoad--;
if (!numPartsToLoad) {
callbackIfValid();
return;
loadBlobs: {
value: function loadBlobs(parts, callback) {
let callbackIfValid = function callbackIfValid() {
if (DEBUG) debug("All parts loaded: " + JSON.stringify(parts));
if (callback) {
callback();
}
continue;
}
let fileReader = Cc["@mozilla.org/files/filereader;1"]
.createInstance(Ci.nsIDOMFileReader);
fileReader.addEventListener("loadend",
(function onloadend(part, event) {
let arrayBuffer = event.target.result;
part.content = new Uint8Array(arrayBuffer);
numPartsToLoad--;
if (!numPartsToLoad) {
callbackIfValid();
if (!parts || !parts.length) {
callbackIfValid();
return;
}
let numPartsToLoad = parts.length;
for each (let part in parts) {
if (!(part.content instanceof Ci.nsIDOMBlob)) {
numPartsToLoad--;
if (!numPartsToLoad) {
callbackIfValid();
return;
}
continue;
}
}).bind(null, part));
fileReader.readAsArrayBuffer(part.content);
};
let fileReader = Cc["@mozilla.org/files/filereader;1"]
.createInstance(Ci.nsIDOMFileReader);
fileReader.addEventListener("loadend",
(function onloadend(part, event) {
let arrayBuffer = event.target.result;
part.content = new Uint8Array(arrayBuffer);
numPartsToLoad--;
if (!numPartsToLoad) {
callbackIfValid();
}
}).bind(null, part));
fileReader.readAsArrayBuffer(part.content);
};
},
enumerable: true,
configurable: true,
writable: true
},
/**
@ -832,47 +1005,54 @@ SendTransaction.prototype = {
* A callback function that takes two arguments: one for
* X-Mms-Response-Status, the other for the parsed M-Send.conf message.
*/
run: function run(callback) {
if (!this.istreamComposed) {
this.loadBlobs(this.msg.parts, (function () {
this.istream = MMS.PduHelper.compose(null, this.msg);
this.istreamComposed = true;
this.run(callback);
}).bind(this));
return;
}
run: {
value: function run(callback) {
this.registerRunCallback(callback);
let callbackIfValid = function callbackIfValid(mmsStatus, msg) {
if (callback) {
callback(mmsStatus, msg);
}
}
if (!this.istream) {
callbackIfValid(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null);
return;
}
this.retryCount = 0;
let retryCallback = (function (mmsStatus, msg) {
if ((MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE == mmsStatus ||
MMS.MMS_PDU_ERROR_PERMANENT_FAILURE == mmsStatus) &&
this.retryCount < PREF_SEND_RETRY_COUNT) {
if (this.timer == null) {
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
}
this.retryCount++;
this.timer.initWithCallback(this.send.bind(this, retryCallback),
PREF_SEND_RETRY_INTERVAL,
Ci.nsITimer.TYPE_ONE_SHOT);
if (!this.istreamComposed) {
this.loadBlobs(this.msg.parts, (function () {
this.istream = MMS.PduHelper.compose(null, this.msg);
this.istreamComposed = true;
if (this.isCancelled) {
this.runCallbackIfValid(_MMS_ERROR_MESSAGE_DELETED, null);
} else {
this.run(callback);
}
}).bind(this));
return;
}
callbackIfValid(mmsStatus, msg);
}).bind(this);
this.send(retryCallback);
if (!this.istream) {
this.runCallbackIfValid(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null);
return;
}
this.retryCount = 0;
let retryCallback = (function (mmsStatus, msg) {
if ((MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE == mmsStatus ||
MMS.MMS_PDU_ERROR_PERMANENT_FAILURE == mmsStatus) &&
this.retryCount < PREF_SEND_RETRY_COUNT) {
if (this.timer == null) {
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
}
this.retryCount++;
this.timer.initWithCallback(this.send.bind(this, retryCallback),
PREF_SEND_RETRY_INTERVAL,
Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
this.runCallbackIfValid(mmsStatus, msg);
}).bind(this);
// This is the entry point to start sending.
this.send(retryCallback);
},
enumerable: true,
configurable: true,
writable: true
},
/**
@ -880,30 +1060,44 @@ SendTransaction.prototype = {
* A callback function that takes two arguments: one for
* X-Mms-Response-Status, the other for the parsed M-Send.conf message.
*/
send: function send(callback) {
gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc, this.istream,
function (httpStatus, data) {
if (httpStatus != HTTP_STATUS_OK) {
callback(MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE, null);
return;
}
send: {
value: function send(callback) {
this.timer = null;
if (!data) {
callback(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null);
return;
}
this.cancellable =
gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc,
this.istream,
function (httpStatus, data) {
if (httpStatus == _HTTP_STATUS_USER_CANCELLED) {
callback(_MMS_ERROR_MESSAGE_DELETED, null);
return;
}
let response = MMS.PduHelper.parse(data, null);
if (!response || (response.type != MMS.MMS_PDU_TYPE_SEND_CONF)) {
callback(MMS.MMS_PDU_RESPONSE_ERROR_UNSUPPORTED_MESSAGE, null);
return;
}
if (httpStatus != HTTP_STATUS_OK) {
callback(MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE, null);
return;
}
let responseStatus = response.headers["x-mms-response-status"];
callback(responseStatus, response);
});
if (!data) {
callback(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null);
return;
}
let response = MMS.PduHelper.parse(data, null);
if (!response || (response.type != MMS.MMS_PDU_TYPE_SEND_CONF)) {
callback(MMS.MMS_PDU_RESPONSE_ERROR_UNSUPPORTED_MESSAGE, null);
return;
}
let responseStatus = response.headers["x-mms-response-status"];
callback(responseStatus, response);
});
},
enumerable: true,
configurable: true,
writable: true
}
};
});
/**
* Send M-acknowledge.ind back to MMSC.
@ -1084,7 +1278,7 @@ MmsService.prototype = {
// Notifying observers an MMS message is retrieving.
Services.obs.notifyObservers(aDomMessage, kSmsRetrievingObserverTopic, null);
let transaction = new RetrieveTransaction(aContentLocation);
let transaction = new RetrieveTransaction(aDomMessage.id, aContentLocation);
transaction.run(aCallback);
},
@ -1449,11 +1643,20 @@ MmsService.prototype = {
let self = this;
let sendTransactionCb = function sendTransactionCb(aRecordId, aErrorCode) {
let sendTransactionCb = function sendTransactionCb(aDomMessage, aErrorCode) {
if (DEBUG) debug("The error code of sending transaction: " + aErrorCode);
// If the messsage has been deleted (because the sending process is
// cancelled), we don't need to reset the its delievery state/status.
if (aErrorCode == Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR) {
aRequest.notifySendMessageFailed(aErrorCode);
Services.obs.notifyObservers(aDomMessage, kSmsFailedObserverTopic, null);
return;
}
let isSentSuccess = (aErrorCode == Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR);
gMobileMessageDatabaseService
.setMessageDelivery(aRecordId,
.setMessageDelivery(aDomMessage.id,
null,
isSentSuccess ? DELIVERY_SENT : DELIVERY_ERROR,
isSentSuccess ? null : DELIVERY_STATUS_ERROR,
@ -1488,14 +1691,16 @@ MmsService.prototype = {
// For radio disabled error.
if (gMmsConnection.radioDisabled) {
if (DEBUG) debug("Error! Radio is disabled when sending MMS.");
sendTransactionCb(aDomMessage.id, Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR);
sendTransactionCb(aDomMessage,
Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR);
return;
}
// For SIM card is not ready.
if (gRIL.rilContext.cardState != "ready") {
if (DEBUG) debug("Error! SIM card is not ready when sending MMS.");
sendTransactionCb(aDomMessage.id, Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR);
sendTransactionCb(aDomMessage,
Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR);
return;
}
@ -1503,18 +1708,25 @@ MmsService.prototype = {
Services.obs.notifyObservers(aDomMessage, kSmsSendingObserverTopic, null);
let sendTransaction;
try {
sendTransaction = new SendTransaction(savableMessage);
sendTransaction = new SendTransaction(aDomMessage.id, savableMessage);
} catch (e) {
if (DEBUG) debug("Exception: fail to create a SendTransaction instance.");
sendTransactionCb(aDomMessage.id, Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
sendTransactionCb(aDomMessage,
Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
return;
}
sendTransaction.run(function callback(aMmsStatus, aMsg) {
let isSentSuccess = (aMmsStatus == MMS.MMS_PDU_ERROR_OK);
if (DEBUG) debug("The sending status of sendTransaction.run(): " + aMmsStatus);
sendTransactionCb(aDomMessage.id, isSentSuccess?
Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR:
Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
let errorCode;
if (aMmsStatus == _MMS_ERROR_MESSAGE_DELETED) {
errorCode = Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR;
} else if (aMmsStatus != MMS.MMS_PDU_ERROR_OK) {
errorCode = Ci.nsIMobileMessageCallback.INTERNAL_ERROR;
} else {
errorCode = Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR;
}
sendTransactionCb(aDomMessage, errorCode);
});
});
},
@ -1571,6 +1783,13 @@ MmsService.prototype = {
// For X-Mms-Report-Allowed
let wish = aMessageRecord.headers["x-mms-delivery-report"];
let responseNotify = function responseNotify(mmsStatus, retrievedMsg) {
// If the messsage has been deleted (because the retrieving process is
// cancelled), we don't need to reset the its delievery state/status.
if (mmsStatus == _MMS_ERROR_MESSAGE_DELETED) {
aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
return;
}
// If the mmsStatus is still MMS_PDU_STATUS_DEFERRED after retry,
// we should not store it into database and update its delivery
// status to 'error'.

Просмотреть файл

@ -1542,7 +1542,11 @@ MobileMessageDatabaseService.prototype = {
messageRecord.threadId,
messageId,
messageRecord.read);
};
Services.obs.notifyObservers(null,
"mobile-message-deleted",
JSON.stringify({ id: messageId }));
};
} else if (DEBUG) {
debug("Message id " + messageId + " does not exist");
}