This commit is contained in:
Ryan VanderMeulen 2013-06-26 12:33:24 -04:00
Родитель f4df7e7e1b 6ab81c651a
Коммит e19b88cdad
6 изменённых файлов: 524 добавлений и 271 удалений

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

@ -75,13 +75,11 @@ FilePicker.prototype = {
}, },
appendFilters: function(filterMask) { appendFilters: function(filterMask) {
this.mFilterTypes = null;
// Ci.nsIFilePicker.filterHTML is not supported // Ci.nsIFilePicker.filterHTML is not supported
// Ci.nsIFilePicker.filterText is not supported // Ci.nsIFilePicker.filterText is not supported
if (filterMask & Ci.nsIFilePicker.filterImages) { if (filterMask & Ci.nsIFilePicker.filterImages) {
this.mFilterTypes = IMAGE_FILTERS; this.mFilterTypes = this.mFilterTypes.concat(IMAGE_FILTERS);
} }
// Ci.nsIFilePicker.filterXML is not supported // Ci.nsIFilePicker.filterXML is not supported
@ -90,11 +88,11 @@ FilePicker.prototype = {
// Ci.nsIFilePicker.filterAllowURLs is not supported // Ci.nsIFilePicker.filterAllowURLs is not supported
if (filterMask & Ci.nsIFilePicker.filterVideo) { if (filterMask & Ci.nsIFilePicker.filterVideo) {
this.mFilterTypes = VIDEO_FILTERS; this.mFilterTypes = this.mFilterTypes.concat(VIDEO_FILTERS);
} }
if (filterMask & Ci.nsIFilePicker.filterAudio) { if (filterMask & Ci.nsIFilePicker.filterAudio) {
this.mFilterTypes = AUDIO_FILTERS; this.mFilterTypes = this.mFilterTypes.concat(AUDIO_FILTERS);
} }
// Ci.nsIFilePicker.filterAll is by default // Ci.nsIFilePicker.filterAll is by default

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

@ -1,4 +1,4 @@
{ {
"revision": "500287afb1817acb98bee67684c6c4c0f7240eb9", "revision": "6175336047b511e982827ae4ed559ee1f644a79e",
"repo_path": "/integration/gaia-central" "repo_path": "/integration/gaia-central"
} }

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

@ -8,6 +8,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include <cutils/properties.h> #include <cutils/properties.h>
#include <stagefright/foundation/ADebug.h>
#include <stagefright/foundation/AMessage.h> #include <stagefright/foundation/AMessage.h>
#include <stagefright/MediaExtractor.h> #include <stagefright/MediaExtractor.h>
#include <stagefright/MetaData.h> #include <stagefright/MetaData.h>
@ -174,8 +175,10 @@ OmxDecoder::~OmxDecoder()
void OmxDecoder::statusChanged() void OmxDecoder::statusChanged()
{ {
mozilla::ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); sp<AMessage> notify =
mDecoder->GetReentrantMonitor().NotifyAll(); new AMessage(kNotifyStatusChanged, mReflector->id());
// post AMessage to OmxDecoder via ALooper.
notify->post();
} }
static sp<IOMX> sOMX = nullptr; static sp<IOMX> sOMX = nullptr;
@ -765,12 +768,28 @@ void OmxDecoder::Pause()
// Called on ALooper thread. // Called on ALooper thread.
void OmxDecoder::onMessageReceived(const sp<AMessage> &msg) void OmxDecoder::onMessageReceived(const sp<AMessage> &msg)
{ {
Mutex::Autolock autoLock(mSeekLock); switch (msg->what()) {
case kNotifyPostReleaseVideoBuffer:
{
Mutex::Autolock autoLock(mSeekLock);
// Free pending video buffers when OmxDecoder is not seeking video.
// If OmxDecoder is seeking video, the buffers are freed on seek exit.
if (!mIsVideoSeeking) {
ReleaseAllPendingVideoBuffersLocked();
}
break;
}
// Free pending video buffers when OmxDecoder is not seeking video. case kNotifyStatusChanged:
// If OmxDecoder is in seeking video, the buffers are freed on seek exit. {
if (mIsVideoSeeking != true) { mozilla::ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
ReleaseAllPendingVideoBuffersLocked(); mDecoder->GetReentrantMonitor().NotifyAll();
break;
}
default:
TRESPASS();
break;
} }
} }

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

@ -86,7 +86,8 @@ class OmxDecoder : public OMXCodecProxy::EventListener {
}; };
enum { enum {
kNotifyPostReleaseVideoBuffer = 'noti' kNotifyPostReleaseVideoBuffer = 'noti',
kNotifyStatusChanged = 'stat'
}; };
AbstractMediaDecoder *mDecoder; AbstractMediaDecoder *mDecoder;

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

@ -33,11 +33,18 @@ const kSmsRetrievingObserverTopic = "sms-retrieving";
const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed"; const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
const kXpcomShutdownObserverTopic = "xpcom-shutdown"; const kXpcomShutdownObserverTopic = "xpcom-shutdown";
const kPrefenceChangedObserverTopic = "nsPref:changed"; const kPrefenceChangedObserverTopic = "nsPref:changed";
const kMobileMessageDeletedObserverTopic = "mobile-message-deleted";
// HTTP status codes: // HTTP status codes:
// @see http://tools.ietf.org/html/rfc2616#page-39 // @see http://tools.ietf.org/html/rfc2616#page-39
const HTTP_STATUS_OK = 200; 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_NEVER = 0;
const CONFIG_SEND_REPORT_DEFAULT_NO = 1; const CONFIG_SEND_REPORT_DEFAULT_NO = 1;
const CONFIG_SEND_REPORT_DEFAULT_YES = 2; const CONFIG_SEND_REPORT_DEFAULT_YES = 2;
@ -397,7 +404,7 @@ MmsProxyFilter.prototype = {
}; };
XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () { XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
return { let helper = {
/** /**
* Send MMS request to MMSC. * Send MMS request to MMSC.
* *
@ -413,14 +420,55 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
*/ */
sendRequest: function sendRequest(method, url, istream, callback) { sendRequest: function sendRequest(method, url, istream, callback) {
// TODO: bug 810226 - Support GPRS bearer for MMS transmission and reception. // TODO: bug 810226 - Support GPRS bearer for MMS transmission and reception.
let cancellable = {
callback: callback,
gMmsConnection.acquire((function (method, url, istream, callback, isDone: false,
connected) { isCancelled: false,
if (!connected) {
// Connection timeout or failed. Report error. 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(); gMmsConnection.release();
if (callback) {
callback(0, null); if (!cancellable.isDone) {
cancellable.done(cancellable.isCancelled ?
_HTTP_STATUS_USER_CANCELLED : 0, null);
} }
return; return;
} }
@ -429,84 +477,90 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
let proxyFilter = new MmsProxyFilter(url); let proxyFilter = new MmsProxyFilter(url);
gpps.registerFilter(proxyFilter, 0); gpps.registerFilter(proxyFilter, 0);
let releaseMmsConnectionAndCallback = (function (httpStatus, data) { cancellable.xhr = this.sendHttpRequest(method, url, istream, proxyFilter,
gpps.unregisterFilter(proxyFilter); cancellable.done.bind(cancellable));
// Always release the MMS network connection before callback. }).bind(this));
gMmsConnection.release();
if (callback) {
callback(httpStatus, data);
}
}).bind(this);
try { return cancellable;
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] },
.createInstance(Ci.nsIXMLHttpRequest);
// Basic setups sendHttpRequest: function sendHttpRequest(method, url, istream, proxyFilter,
xhr.open(method, url, true); callback) {
xhr.responseType = "arraybuffer"; let releaseMmsConnectionAndCallback = function (httpStatus, data) {
if (istream) { gpps.unregisterFilter(proxyFilter);
xhr.setRequestHeader("Content-Type", // Always release the MMS network connection before callback.
"application/vnd.wap.mms-message"); gMmsConnection.release();
xhr.setRequestHeader("Content-Length", istream.available()); callback(httpStatus, data);
} };
// UAProf headers. try {
let uaProfUrl, uaProfTagname = "x-wap-profile"; let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
try { .createInstance(Ci.nsIXMLHttpRequest);
uaProfUrl = Services.prefs.getCharPref('wap.UAProf.url');
uaProfTagname = Services.prefs.getCharPref('wap.UAProf.tagname');
} catch (e) {}
if (uaProfUrl) { // Basic setups
xhr.setRequestHeader(uaProfTagname, uaProfUrl); xhr.open(method, url, true);
} xhr.responseType = "arraybuffer";
if (istream) {
// Setup event listeners xhr.setRequestHeader("Content-Type",
xhr.onerror = function () { "application/vnd.wap.mms-message");
if (DEBUG) debug("xhr error, response headers: " + xhr.setRequestHeader("Content-Length", istream.available());
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);
} }
}).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 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 * @param contentLocation
* X-Mms-Content-Location of the message. * X-Mms-Content-Location of the message.
*/ */
function RetrieveTransaction(contentLocation) { function RetrieveTransaction(cancellableId, contentLocation) {
// Call |CancellableTransaction| constructor.
CancellableTransaction.call(this, cancellableId);
this.contentLocation = contentLocation; this.contentLocation = contentLocation;
} }
RetrieveTransaction.prototype = { RetrieveTransaction.prototype = Object.create(CancellableTransaction.prototype, {
/**
* We need to keep a reference to the timer to assure the timer is fired.
*/
timer: null,
/** /**
* @param callback [optional] * @param callback [optional]
* A callback function that takes two arguments: one for X-Mms-Status, * A callback function that takes two arguments: one for X-Mms-Status,
* the other for the parsed M-Retrieve.conf message. * the other for the parsed M-Retrieve.conf message.
*/ */
run: function run(callback) { run: {
this.retryCount = 0; value: function run(callback) {
let that = this; this.registerRunCallback(callback);
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.retryCount = 0;
this.retrieve(retryCallback); let that = this;
}).bind(that), this.retrieve((function retryCallback(mmsStatus, msg) {
PREF_RETRIEVAL_RETRY_INTERVALS[that.retryCount], if (MMS.MMS_PDU_STATUS_DEFERRED == mmsStatus &&
Ci.nsITimer.TYPE_ONE_SHOT); that.retryCount < PREF_RETRIEVAL_RETRY_COUNT) {
that.retryCount++; if (that.timer == null) {
return; that.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
} }
if (callback) {
callback(mmsStatus, msg); that.timer.initWithCallback((function (){
} this.retrieve(retryCallback);
}).bind(this)); }).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, * A callback function that takes two arguments: one for X-Mms-Status,
* the other for the parsed M-Retrieve.conf message. * the other for the parsed M-Retrieve.conf message.
*/ */
retrieve: function retrieve(callback) { retrieve: {
gMmsTransactionHelper.sendRequest("GET", this.contentLocation, null, value: function retrieve(callback) {
(function (httpStatus, data) { this.timer = null;
if ((httpStatus != HTTP_STATUS_OK) || !data) {
callback(MMS.MMS_PDU_STATUS_DEFERRED, null);
return;
}
let retrieved = MMS.PduHelper.parse(data, null); this.cancellable =
if (!retrieved || (retrieved.type != MMS.MMS_PDU_TYPE_RETRIEVE_CONF)) { gMmsTransactionHelper.sendRequest("GET", this.contentLocation, null,
callback(MMS.MMS_PDU_STATUS_UNRECOGNISED, null); (function (httpStatus, data) {
return; if (httpStatus == _HTTP_STATUS_USER_CANCELLED) {
} callback(_MMS_ERROR_MESSAGE_DELETED, null);
return;
}
// Fix default header field values. if ((httpStatus != HTTP_STATUS_OK) || !data) {
if (retrieved.headers["x-mms-delivery-report"] == null) { callback(MMS.MMS_PDU_STATUS_DEFERRED, null);
retrieved.headers["x-mms-delivery-report"] = false; return;
} }
let retrieveStatus = retrieved.headers["x-mms-retrieve-status"]; let retrieved = MMS.PduHelper.parse(data, null);
if ((retrieveStatus != null) && if (!retrieved || (retrieved.type != MMS.MMS_PDU_TYPE_RETRIEVE_CONF)) {
(retrieveStatus != MMS.MMS_PDU_ERROR_OK)) { callback(MMS.MMS_PDU_STATUS_UNRECOGNISED, null);
callback(MMS.translatePduErrorToStatus(retrieveStatus), return;
retrieved); }
return;
}
callback(MMS.MMS_PDU_STATUS_RETRIEVED, retrieved); // Fix default header field values.
}).bind(this)); 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. * 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.") * @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; msg.headers["x-mms-message-type"] = MMS.MMS_PDU_TYPE_SEND_REQ;
if (!msg.headers["x-mms-transaction-id"]) { if (!msg.headers["x-mms-transaction-id"]) {
// Create an unique transaction id // Create an unique transaction id
@ -775,13 +943,13 @@ function SendTransaction(msg) {
this.msg = msg; this.msg = msg;
} }
SendTransaction.prototype = { SendTransaction.prototype = Object.create(CancellableTransaction.prototype, {
/** istreamComposed: {
* We need to keep a reference to the timer to assure the timer is fired. value: false,
*/ enumerable: true,
timer: null, configurable: true,
writable: true
istreamComposed: false, },
/** /**
* @param parts * @param parts
@ -789,42 +957,47 @@ SendTransaction.prototype = {
* @param callback [optional] * @param callback [optional]
* A callback function that takes zero argument. * A callback function that takes zero argument.
*/ */
loadBlobs: function loadBlobs(parts, callback) { loadBlobs: {
let callbackIfValid = function callbackIfValid() { value: function loadBlobs(parts, callback) {
if (DEBUG) debug("All parts loaded: " + JSON.stringify(parts)); let callbackIfValid = function callbackIfValid() {
if (callback) { if (DEBUG) debug("All parts loaded: " + JSON.stringify(parts));
callback(); 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;
} }
continue;
} }
let fileReader = Cc["@mozilla.org/files/filereader;1"]
.createInstance(Ci.nsIDOMFileReader); if (!parts || !parts.length) {
fileReader.addEventListener("loadend", callbackIfValid();
(function onloadend(part, event) { return;
let arrayBuffer = event.target.result; }
part.content = new Uint8Array(arrayBuffer);
numPartsToLoad--; let numPartsToLoad = parts.length;
if (!numPartsToLoad) { for each (let part in parts) {
callbackIfValid(); if (!(part.content instanceof Ci.nsIDOMBlob)) {
numPartsToLoad--;
if (!numPartsToLoad) {
callbackIfValid();
return;
}
continue;
} }
}).bind(null, part)); let fileReader = Cc["@mozilla.org/files/filereader;1"]
fileReader.readAsArrayBuffer(part.content); .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 * A callback function that takes two arguments: one for
* X-Mms-Response-Status, the other for the parsed M-Send.conf message. * X-Mms-Response-Status, the other for the parsed M-Send.conf message.
*/ */
run: function run(callback) { run: {
if (!this.istreamComposed) { value: function run(callback) {
this.loadBlobs(this.msg.parts, (function () { this.registerRunCallback(callback);
this.istream = MMS.PduHelper.compose(null, this.msg);
this.istreamComposed = true;
this.run(callback);
}).bind(this));
return;
}
let callbackIfValid = function callbackIfValid(mmsStatus, msg) { if (!this.istreamComposed) {
if (callback) { this.loadBlobs(this.msg.parts, (function () {
callback(mmsStatus, msg); this.istream = MMS.PduHelper.compose(null, this.msg);
} this.istreamComposed = true;
} if (this.isCancelled) {
this.runCallbackIfValid(_MMS_ERROR_MESSAGE_DELETED, null);
if (!this.istream) { } else {
callbackIfValid(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null); this.run(callback);
return; }
} }).bind(this));
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; return;
} }
callbackIfValid(mmsStatus, msg); if (!this.istream) {
}).bind(this); this.runCallbackIfValid(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null);
this.send(retryCallback); 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 * A callback function that takes two arguments: one for
* X-Mms-Response-Status, the other for the parsed M-Send.conf message. * X-Mms-Response-Status, the other for the parsed M-Send.conf message.
*/ */
send: function send(callback) { send: {
gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc, this.istream, value: function send(callback) {
function (httpStatus, data) { this.timer = null;
if (httpStatus != HTTP_STATUS_OK) {
callback(MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE, null);
return;
}
if (!data) { this.cancellable =
callback(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null); gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc,
return; 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 (httpStatus != HTTP_STATUS_OK) {
if (!response || (response.type != MMS.MMS_PDU_TYPE_SEND_CONF)) { callback(MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE, null);
callback(MMS.MMS_PDU_RESPONSE_ERROR_UNSUPPORTED_MESSAGE, null); return;
return; }
}
let responseStatus = response.headers["x-mms-response-status"]; if (!data) {
callback(responseStatus, response); 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. * Send M-acknowledge.ind back to MMSC.
@ -1084,7 +1278,7 @@ MmsService.prototype = {
// Notifying observers an MMS message is retrieving. // Notifying observers an MMS message is retrieving.
Services.obs.notifyObservers(aDomMessage, kSmsRetrievingObserverTopic, null); Services.obs.notifyObservers(aDomMessage, kSmsRetrievingObserverTopic, null);
let transaction = new RetrieveTransaction(aContentLocation); let transaction = new RetrieveTransaction(aDomMessage.id, aContentLocation);
transaction.run(aCallback); transaction.run(aCallback);
}, },
@ -1449,11 +1643,20 @@ MmsService.prototype = {
let self = this; 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 (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); let isSentSuccess = (aErrorCode == Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR);
gMobileMessageDatabaseService gMobileMessageDatabaseService
.setMessageDelivery(aRecordId, .setMessageDelivery(aDomMessage.id,
null, null,
isSentSuccess ? DELIVERY_SENT : DELIVERY_ERROR, isSentSuccess ? DELIVERY_SENT : DELIVERY_ERROR,
isSentSuccess ? null : DELIVERY_STATUS_ERROR, isSentSuccess ? null : DELIVERY_STATUS_ERROR,
@ -1488,14 +1691,16 @@ MmsService.prototype = {
// For radio disabled error. // For radio disabled error.
if (gMmsConnection.radioDisabled) { if (gMmsConnection.radioDisabled) {
if (DEBUG) debug("Error! Radio is disabled when sending MMS."); 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; return;
} }
// For SIM card is not ready. // For SIM card is not ready.
if (gRIL.rilContext.cardState != "ready") { if (gRIL.rilContext.cardState != "ready") {
if (DEBUG) debug("Error! SIM card is not ready when sending MMS."); 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; return;
} }
@ -1503,18 +1708,25 @@ MmsService.prototype = {
Services.obs.notifyObservers(aDomMessage, kSmsSendingObserverTopic, null); Services.obs.notifyObservers(aDomMessage, kSmsSendingObserverTopic, null);
let sendTransaction; let sendTransaction;
try { try {
sendTransaction = new SendTransaction(savableMessage); sendTransaction = new SendTransaction(aDomMessage.id, savableMessage);
} catch (e) { } catch (e) {
if (DEBUG) debug("Exception: fail to create a SendTransaction instance."); if (DEBUG) debug("Exception: fail to create a SendTransaction instance.");
sendTransactionCb(aDomMessage.id, Ci.nsIMobileMessageCallback.INTERNAL_ERROR); sendTransactionCb(aDomMessage,
Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
return; return;
} }
sendTransaction.run(function callback(aMmsStatus, aMsg) { sendTransaction.run(function callback(aMmsStatus, aMsg) {
let isSentSuccess = (aMmsStatus == MMS.MMS_PDU_ERROR_OK);
if (DEBUG) debug("The sending status of sendTransaction.run(): " + aMmsStatus); if (DEBUG) debug("The sending status of sendTransaction.run(): " + aMmsStatus);
sendTransactionCb(aDomMessage.id, isSentSuccess? let errorCode;
Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR: if (aMmsStatus == _MMS_ERROR_MESSAGE_DELETED) {
Ci.nsIMobileMessageCallback.INTERNAL_ERROR); 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 // For X-Mms-Report-Allowed
let wish = aMessageRecord.headers["x-mms-delivery-report"]; let wish = aMessageRecord.headers["x-mms-delivery-report"];
let responseNotify = function responseNotify(mmsStatus, retrievedMsg) { 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, // If the mmsStatus is still MMS_PDU_STATUS_DEFERRED after retry,
// we should not store it into database and update its delivery // we should not store it into database and update its delivery
// status to 'error'. // status to 'error'.

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

@ -20,6 +20,9 @@ const RIL_GETTHREADSCURSOR_CID =
Components.ID("{95ee7c3e-d6f2-4ec4-ade5-0c453c036d35}"); Components.ID("{95ee7c3e-d6f2-4ec4-ade5-0c453c036d35}");
const DEBUG = false; const DEBUG = false;
const DISABLE_MMS_GROUPING_FOR_RECEIVING = true;
const DB_NAME = "sms"; const DB_NAME = "sms";
const DB_VERSION = 11; const DB_VERSION = 11;
const MESSAGE_STORE_NAME = "sms"; const MESSAGE_STORE_NAME = "sms";
@ -1133,13 +1136,15 @@ MobileMessageDatabaseService.prototype = {
let self = this.getRilIccInfoMsisdn(); let self = this.getRilIccInfoMsisdn();
let threadParticipants = [aMessage.sender]; let threadParticipants = [aMessage.sender];
if (aMessage.type == "sms") { if (aMessage.type == "sms") {
// TODO Bug 853384 - for some SIMs we cannot retrieve the vaild // For some SIMs we cannot retrieve the vaild MSISDN (i.e. the user's own
// phone number, thus setting the SMS' receiver to be null. // phone number), thus setting the SMS' receiver to be null.
aMessage.receiver = self; aMessage.receiver = self;
} else if (aMessage.type == "mms") { } else if (aMessage.type == "mms" && !DISABLE_MMS_GROUPING_FOR_RECEIVING) {
let receivers = aMessage.receivers; let receivers = aMessage.receivers;
// We need to add the receivers (excluding our own) into the participants // If we don't want to disable the MMS grouping for receiving, we need to
// of a thread. Some cases we might encounter here: // add the receivers (excluding the user's own number) to the participants
// for creating the thread. Some cases might be investigated as below:
//
// 1. receivers.length == 0 // 1. receivers.length == 0
// This usually happens when receiving an MMS notification indication // This usually happens when receiving an MMS notification indication
// which doesn't carry any receivers. // which doesn't carry any receivers.
@ -1148,18 +1153,25 @@ MobileMessageDatabaseService.prototype = {
// add it into participants because we know that number is our own. // add it into participants because we know that number is our own.
// 3. receivers.length >= 2 // 3. receivers.length >= 2
// If the receivers contain multiple phone numbers, we need to add all // If the receivers contain multiple phone numbers, we need to add all
// of them but not our own into participants. // of them but not the user's own number into participants.
if (receivers.length >= 2) { if (receivers.length >= 2) {
// TODO Bug 853384 - for some SIM cards, the phone number might not be let isSuccess = false;
// available, so we cannot correcly exclude our own from the receivers,
// thus wrongly building the thread index.
let slicedReceivers = receivers.slice(); let slicedReceivers = receivers.slice();
if (self) { if (self) {
let found = slicedReceivers.indexOf(self); let found = slicedReceivers.indexOf(self);
if (found !== -1) { if (found !== -1) {
isSuccess = true;
slicedReceivers.splice(found, 1); slicedReceivers.splice(found, 1);
} }
} }
if (!isSuccess) {
// For some SIMs we cannot retrieve the vaild MSISDN (i.e. the user's
// own phone number), so we cannot correcly exclude the user's own
// number from the receivers, thus wrongly building the thread index.
if (DEBUG) debug("Error! Cannot strip out user's own phone number!");
}
threadParticipants = threadParticipants.concat(slicedReceivers); threadParticipants = threadParticipants.concat(slicedReceivers);
} }
} }
@ -1220,8 +1232,8 @@ MobileMessageDatabaseService.prototype = {
} }
} }
// TODO Bug 853384 - for some SIMs we cannot retrieve the vaild // For some SIMs we cannot retrieve the vaild MSISDN (i.e. the user's own
// phone number, thus setting the message's sender to be null. // phone number), thus setting the message's sender to be null.
aMessage.sender = this.getRilIccInfoMsisdn(); aMessage.sender = this.getRilIccInfoMsisdn();
let timestamp = aMessage.timestamp; let timestamp = aMessage.timestamp;
@ -1542,7 +1554,11 @@ MobileMessageDatabaseService.prototype = {
messageRecord.threadId, messageRecord.threadId,
messageId, messageId,
messageRecord.read); messageRecord.read);
};
Services.obs.notifyObservers(null,
"mobile-message-deleted",
JSON.stringify({ id: messageId }));
};
} else if (DEBUG) { } else if (DEBUG) {
debug("Message id " + messageId + " does not exist"); debug("Message id " + messageId + " does not exist");
} }