Bug 945089 - Part 1.2: Move SMS concatenation logic from ril_worker.js to RadioInterfaceLayer.js. r=vyang

This commit is contained in:
Bevis Tseng 2014-03-13 01:27:00 +09:00
parent 1205781404
commit 1931395fb0
2 changed files with 310 additions and 202 deletions

View File

@ -1905,6 +1905,8 @@ function RadioInterface(aClientId, aWorkerMessenger) {
this.portAddressedSmsApps = {};
this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this);
this._receivedSmsSegmentsMap = {};
this._sntp = new Sntp(this.setClockBySntp.bind(this),
Services.prefs.getIntPref("network.sntp.maxRetryCount"),
Services.prefs.getIntPref("network.sntp.refreshPeriod"),
@ -2207,13 +2209,8 @@ RadioInterface.prototype = {
this.clientId, message);
break;
case "sms-received":
let ackOk = this.handleSmsReceived(message);
// Note: ACK has been done by modem for NEW_SMS_ON_SIM
if (ackOk && message.simStatus === undefined) {
this.workerMessenger.send("ackSMS", { result: RIL.PDU_FCS_OK });
}
return;
case "broadcastsms-received":
this.handleSmsMultipart(message);
break;
case "cellbroadcast-received":
message.timestamp = Date.now();
gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
@ -2719,9 +2716,9 @@ RadioInterface.prototype = {
let options = {
bearer: WAP.WDP_BEARER_GSM_SMS_GSM_MSISDN,
sourceAddress: message.sender,
sourcePort: message.header.originatorPort,
sourcePort: message.originatorPort,
destinationAddress: this.rilContext.iccInfo.msisdn,
destinationPort: message.header.destinationPort,
destinationPort: message.destinationPort,
serviceId: this.clientId
};
WAP.WapPushManager.receiveWdpPDU(message.fullData, message.fullData.length,
@ -2767,23 +2764,7 @@ RadioInterface.prototype = {
_smsHandledWakeLock: null,
_smsHandledWakeLockTimer: null,
_releaseSmsHandledWakeLock: function() {
if (DEBUG) this.debug("Releasing the CPU wake lock for handling SMS.");
if (this._smsHandledWakeLockTimer) {
this._smsHandledWakeLockTimer.cancel();
}
if (this._smsHandledWakeLock) {
this._smsHandledWakeLock.unlock();
this._smsHandledWakeLock = null;
}
},
portAddressedSmsApps: null,
handleSmsReceived: function(message) {
if (DEBUG) this.debug("handleSmsReceived: " + JSON.stringify(message));
// We need to acquire a CPU wake lock to avoid the system falling into
// the sleep mode when the RIL handles the received SMS.
_acquireSmsHandledWakeLock: function() {
if (!this._smsHandledWakeLock) {
if (DEBUG) this.debug("Acquiring a CPU wake lock for handling SMS.");
this._smsHandledWakeLock = gPowerManagerService.newWakeLock("cpu");
@ -2798,17 +2779,251 @@ RadioInterface.prototype = {
.initWithCallback(this._releaseSmsHandledWakeLock.bind(this),
SMS_HANDLED_WAKELOCK_TIMEOUT,
Ci.nsITimer.TYPE_ONE_SHOT);
},
// FIXME: Bug 737202 - Typed arrays become normal arrays when sent to/from workers
if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
message.fullData = new Uint8Array(message.fullData);
_releaseSmsHandledWakeLock: function() {
if (DEBUG) this.debug("Releasing the CPU wake lock for handling SMS.");
if (this._smsHandledWakeLockTimer) {
this._smsHandledWakeLockTimer.cancel();
}
if (this._smsHandledWakeLock) {
this._smsHandledWakeLock.unlock();
this._smsHandledWakeLock = null;
}
},
/**
* Hash map for received multipart sms fragments. Messages are hashed with
* its sender address and concatenation reference number. Three additional
* attributes `segmentMaxSeq`, `receivedSegments`, `segments` are inserted.
*/
_receivedSmsSegmentsMap: null,
/**
* Helper for processing received multipart SMS.
*
* @return null for handled segments, and an object containing full message
* body/data once all segments are received.
*/
_processReceivedSmsSegment: function(aSegment) {
// Directly replace full message body for single SMS.
if (!(aSegment.segmentMaxSeq && (aSegment.segmentMaxSeq > 1))) {
if (aSegment.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
aSegment.fullData = aSegment.data;
} else {
aSegment.fullBody = aSegment.body;
}
return aSegment;
}
// Handle Concatenation for Class 0 SMS
let hash = aSegment.sender + ":" +
aSegment.segmentRef + ":" +
aSegment.segmentMaxSeq;
let seq = aSegment.segmentSeq;
let options = this._receivedSmsSegmentsMap[hash];
if (!options) {
options = aSegment;
this._receivedSmsSegmentsMap[hash] = options;
options.receivedSegments = 0;
options.segments = [];
} else if (options.segments[seq]) {
// Duplicated segment?
if (DEBUG) {
this.debug("Got duplicated segment no." + seq +
" of a multipart SMS: " + JSON.stringify(aSegment));
}
return null;
}
if (options.receivedSegments > 0) {
// Update received timestamp.
options.timestamp = aSegment.timestamp;
}
if (options.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
options.segments[seq] = aSegment.data;
} else {
options.segments[seq] = aSegment.body;
}
options.receivedSegments++;
// The port information is only available in 1st segment for CDMA WAP Push.
// If the segments of a WAP Push are not received in sequence
// (e.g., SMS with seq == 1 is not the 1st segment received by the device),
// we have to retrieve the port information from 1st segment and
// save it into the cached options.
if (aSegment.teleservice === RIL.PDU_CDMA_MSG_TELESERIVCIE_ID_WAP
&& seq === 1) {
if (!options.originatorPort && aSegment.originatorPort) {
options.originatorPort = aSegment.originatorPort;
}
if (!options.destinationPort && aSegment.destinationPort) {
options.destinationPort = aSegment.destinationPort;
}
}
if (options.receivedSegments < options.segmentMaxSeq) {
if (DEBUG) {
this.debug("Got segment no." + seq + " of a multipart SMS: " +
JSON.stringify(options));
}
return null;
}
// Remove from map
delete this._receivedSmsSegmentsMap[hash];
// Rebuild full body
if (options.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
// Uint8Array doesn't have `concat`, so we have to merge all segements
// by hand.
let fullDataLen = 0;
for (let i = 1; i <= options.segmentMaxSeq; i++) {
fullDataLen += options.segments[i].length;
}
options.fullData = new Uint8Array(fullDataLen);
for (let d= 0, i = 1; i <= options.segmentMaxSeq; i++) {
let data = options.segments[i];
for (let j = 0; j < data.length; j++) {
options.fullData[d++] = data[j];
}
}
} else {
options.fullBody = options.segments.join("");
}
// Remove handy fields after completing the concatenation.
delete options.receivedSegments;
delete options.segments;
if (DEBUG) {
this.debug("Got full multipart SMS: " + JSON.stringify(options));
}
return options;
},
/**
* Helper to create Savable SmsSegment.
*/
_createSavableSmsSegment: function(aMessage) {
// We precisely define what data fields to be stored into
// DB here for better data migration.
let segment = {};
segment.messageType = aMessage.messageType;
segment.teleservice = aMessage.teleservice;
segment.SMSC = aMessage.SMSC;
segment.sentTimestamp = aMessage.sentTimestamp;
segment.timestamp = Date.now();
segment.sender = aMessage.sender;
segment.pid = aMessage.pid;
segment.encoding = aMessage.encoding;
segment.messageClass = aMessage.messageClass;
segment.iccId = this.getIccId();
if (aMessage.header) {
segment.segmentRef = aMessage.header.segmentRef;
segment.segmentSeq = aMessage.header.segmentSeq;
segment.segmentMaxSeq = aMessage.header.segmentMaxSeq;
segment.originatorPort = aMessage.header.originatorPort;
segment.destinationPort = aMessage.header.destinationPort;
}
segment.mwiPresent = (aMessage.mwi)? true: false;
segment.mwiDiscard = (segment.mwiPresent)? aMessage.mwi.discard: false;
segment.mwiMsgCount = (segment.mwiPresent)? aMessage.mwi.msgCount: 0;
segment.mwiActive = (segment.mwiPresent)? aMessage.mwi.active: false;
segment.serviceCategory = aMessage.serviceCategory;
segment.language = aMessage.language;
segment.data = aMessage.data;
segment.body = aMessage.body;
return segment;
},
/**
* Helper to purge complete message.
*
* We remove unnessary fields defined in _createSavableSmsSegment() after
* completing the concatenation.
*/
_purgeCompleteSmsMessage: function(aMessage) {
// Purge concatenation info
delete aMessage.segmentRef;
delete aMessage.segmentSeq;
delete aMessage.segmentMaxSeq;
// Purge partial message body
delete aMessage.data;
delete aMessage.body;
},
/**
* handle concatenation of received SMS.
*/
handleSmsMultipart: function(aMessage) {
if (DEBUG) this.debug("handleSmsMultipart: " + JSON.stringify(aMessage));
this._acquireSmsHandledWakeLock();
let segment = this._createSavableSmsSegment(aMessage);
let isMultipart = (segment.segmentMaxSeq && (segment.segmentMaxSeq > 1));
let messageClass = segment.messageClass;
let handleReceivedAndAck = function(aRvOfIncompleteMsg, aCompleteMessage) {
if (aCompleteMessage) {
this._purgeCompleteSmsMessage(aCompleteMessage);
if (this.handleSmsReceived(aCompleteMessage)) {
this.sendAckSms(Cr.NS_OK, aCompleteMessage);
}
// else Ack will be sent after further process in handleSmsReceived.
} else {
this.sendAckSms(aRvOfIncompleteMsg, segment);
}
}.bind(this);
// No need to access SmsSegmentStore for Class 0 SMS and Single SMS.
if (!isMultipart ||
(messageClass == RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0])) {
// `When a mobile terminated message is class 0 and the MS has the
// capability of displaying short messages, the MS shall display the
// message immediately and send an acknowledgement to the SC when the
// message has successfully reached the MS irrespective of whether
// there is memory available in the (U)SIM or ME. The message shall
// not be automatically stored in the (U)SIM or ME.`
// ~ 3GPP 23.038 clause 4
handleReceivedAndAck(Cr.NS_OK, // ACK OK For Incomplete Class 0
this._processReceivedSmsSegment(segment));
} else {
gMobileMessageDatabaseService
.saveSmsSegment(segment, function notifyResult(aRv, aCompleteMessage) {
handleReceivedAndAck(aRv, // Ack according to the result after saving
aCompleteMessage);
});
}
},
portAddressedSmsApps: null,
handleSmsReceived: function(message) {
if (DEBUG) this.debug("handleSmsReceived: " + JSON.stringify(message));
if (message.messageType == RIL.PDU_CDMA_MSG_TYPE_BROADCAST) {
gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
this.clientId, message);
return true;
}
// Dispatch to registered handler if application port addressing is
// available. Note that the destination port can possibly be zero when
// representing a UDP/TCP port.
if (message.header && message.header.destinationPort != null) {
let handler = this.portAddressedSmsApps[message.header.destinationPort];
if (message.destinationPort != null) {
let handler = this.portAddressedSmsApps[message.destinationPort];
if (handler) {
handler(message);
}
@ -2824,8 +3039,6 @@ RadioInterface.prototype = {
message.sender = message.sender || null;
message.receiver = this.getPhoneNumber();
message.body = message.fullBody = message.fullBody || null;
message.timestamp = Date.now();
message.iccId = this.getIccId();
if (gSmsService.isSilentNumber(message.sender)) {
message.id = -1;
@ -2855,8 +3068,14 @@ RadioInterface.prototype = {
return true;
}
let mwi = message.mwi;
if (mwi) {
if (message.mwiPresent) {
let mwi = {
discard: message.mwiDiscard,
msgCount: message.mwiMsgCount,
active: message.mwiActive
};
this.workerMessenger.send("updateMwis", { mwi: mwi });
mwi.returnNumber = message.sender;
mwi.returnMessage = message.fullBody;
gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification",
@ -2864,7 +3083,7 @@ RadioInterface.prototype = {
// Dicarded MWI comes without text body.
// Hence, we discard it here after notifying the MWI status.
if (mwi.discard) {
if (message.mwiDiscard) {
return true;
}
}
@ -2872,14 +3091,7 @@ RadioInterface.prototype = {
let notifyReceived = function notifyReceived(rv, domMessage) {
let success = Components.isSuccessCode(rv);
// Acknowledge the reception of the SMS.
// Note: Ack has been done by modem for NEW_SMS_ON_SIM
if (message.simStatus === undefined) {
this.workerMessenger.send("ackSMS", {
result: (success ? RIL.PDU_FCS_OK
: RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED)
});
}
this.sendAckSms(rv, message);
if (!success) {
// At this point we could send a message to content to notify the user
@ -2926,6 +3138,26 @@ RadioInterface.prototype = {
return false;
},
/**
* Handle ACK response of received SMS.
*/
sendAckSms: function(aRv, aMessage) {
if (aMessage.messageClass === RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_2]) {
return;
}
let result = RIL.PDU_FCS_OK;
if (!Components.isSuccessCode(aRv)) {
if (DEBUG) this.debug("Failed to handle received sms: " + aRv);
result = (aRv === Cr.NS_ERROR_FILE_NO_DEVICE_SPACE)
? RIL.PDU_FCS_MEMORY_CAPACITY_EXCEEDED
: RIL.PDU_FCS_UNSPECIFIED;
}
this.workerMessenger.send("ackSMS", { result: result });
},
/**
* Set the setting value of "time.clock.automatic-update.available".
*/

View File

@ -213,7 +213,6 @@ function RilObject(aContext) {
this.currentCalls = {};
this.currentConference = {state: null, participants: {}};
this.currentDataCalls = {};
this._receivedSmsSegmentsMap = {};
this._pendingSentSmsMap = {};
this.pendingNetworkType = {};
this._receivedSmsCbPagesMap = {};
@ -243,13 +242,6 @@ RilObject.prototype = {
*/
currentDataCalls: null,
/**
* Hash map for received multipart sms fragments. Messages are hashed with
* its sender address and concatenation reference number. Three additional
* attributes `segmentMaxSeq`, `receivedSegments`, `segments` are inserted.
*/
_receivedSmsSegmentsMap: null,
/**
* Outgoing messages waiting for SMS-STATUS-REPORT.
*/
@ -378,7 +370,7 @@ RilObject.prototype = {
this.deactivateDataCall(datacall);
}
// Don't clean up this._receivedSmsSegmentsMap or this._pendingSentSmsMap
// Don't clean up this._pendingSentSmsMap
// because on rild restart: we may continue with the pending segments.
/**
@ -1814,6 +1806,15 @@ RilObject.prototype = {
Buf.sendParcel();
},
/**
* Update received MWI into EF_MWIS.
*/
updateMwis: function(options) {
if (this.context.ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
this.context.SimRecordHelper.updateMWIS(options.mwi);
}
},
setCellBroadcastDisabled: function(options) {
this.cellBroadcastDisabled = options.disabled;
@ -4396,55 +4397,19 @@ RilObject.prototype = {
},
/**
* Helper for processing multipart SMS.
* Helper to delegate the received sms segment to RadioInterface to process.
*
* @param message
* Received sms message.
*
* @return A failure cause defined in 3GPP 23.040 clause 9.2.3.22.
* @return MOZ_FCS_WAIT_FOR_EXPLICIT_ACK
*/
_processSmsMultipart: function(message) {
if (message.header && message.header.segmentMaxSeq &&
(message.header.segmentMaxSeq > 1)) {
message = this._processReceivedSmsSegment(message);
} else {
if (message.encoding == PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
message.fullData = message.data;
delete message.data;
} else {
message.fullBody = message.body;
delete message.body;
}
}
message.rilMessageType = "sms-received";
if (message) {
message.result = PDU_FCS_OK;
if (message.messageClass == GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_2]) {
// `MS shall ensure that the message has been to the SMS data field in
// the (U)SIM before sending an ACK to the SC.` ~ 3GPP 23.038 clause 4
message.result = PDU_FCS_RESERVED;
}
this.sendChromeMessage(message);
if (message.messageType == PDU_CDMA_MSG_TYPE_BROADCAST) {
message.rilMessageType = "broadcastsms-received";
} else {
message.rilMessageType = "sms-received";
}
this.sendChromeMessage(message);
// Update MWI Status into ICC if present.
if (message.mwi &&
this.context.ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
this.context.SimRecordHelper.updateMWIS(message.mwi);
}
// We will acknowledge receipt of the SMS after we try to store it
// in the database.
return MOZ_FCS_WAIT_FOR_EXPLICIT_ACK;
}
return PDU_FCS_OK;
return MOZ_FCS_WAIT_FOR_EXPLICIT_ACK;
},
/**
@ -4603,95 +4568,6 @@ RilObject.prototype = {
return this._processSmsMultipart(message);
},
/**
* Helper for processing received multipart SMS.
*
* @return null for handled segments, and an object containing full message
* body/data once all segments are received.
*/
_processReceivedSmsSegment: function(original) {
let hash = original.sender + ":" + original.header.segmentRef;
let seq = original.header.segmentSeq;
let options = this._receivedSmsSegmentsMap[hash];
if (!options) {
options = original;
this._receivedSmsSegmentsMap[hash] = options;
options.segmentMaxSeq = original.header.segmentMaxSeq;
options.receivedSegments = 0;
options.segments = [];
} else if (options.segments[seq]) {
// Duplicated segment?
if (DEBUG) {
this.context.debug("Got duplicated segment no." + seq +
" of a multipart SMS: " + JSON.stringify(original));
}
return null;
}
if (options.encoding == PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
options.segments[seq] = original.data;
delete original.data;
} else {
options.segments[seq] = original.body;
delete original.body;
}
options.receivedSegments++;
// The port information is only available in 1st segment for CDMA WAP Push.
// If the segments of a WAP Push are not received in sequence
// (e.g., SMS with seq == 1 is not the 1st segment received by the device),
// we have to retrieve the port information from 1st segment and
// save it into the cached options.header.
if (original.teleservice === PDU_CDMA_MSG_TELESERIVCIE_ID_WAP && seq === 1) {
if (!options.header.originatorPort && original.header.originatorPort) {
options.header.originatorPort = original.header.originatorPort;
}
if (!options.header.destinationPort && original.header.destinationPort) {
options.header.destinationPort = original.header.destinationPort;
}
}
if (options.receivedSegments < options.segmentMaxSeq) {
if (DEBUG) {
this.context.debug("Got segment no." + seq + " of a multipart SMS: " +
JSON.stringify(options));
}
return null;
}
// Remove from map
delete this._receivedSmsSegmentsMap[hash];
// Rebuild full body
if (options.encoding == PDU_DCS_MSG_CODING_8BITS_ALPHABET) {
// Uint8Array doesn't have `concat`, so we have to merge all segements
// by hand.
let fullDataLen = 0;
for (let i = 1; i <= options.segmentMaxSeq; i++) {
fullDataLen += options.segments[i].length;
}
options.fullData = new Uint8Array(fullDataLen);
for (let d= 0, i = 1; i <= options.segmentMaxSeq; i++) {
let data = options.segments[i];
for (let j = 0; j < data.length; j++) {
options.fullData[d++] = data[j];
}
}
} else {
options.fullBody = options.segments.join("");
}
if (DEBUG) {
this.context.debug("Got full multipart SMS: " + JSON.stringify(options));
}
return options;
},
/**
* Helper for processing sent multipart SMS.
*/
@ -7618,24 +7494,24 @@ GsmPDUHelperObject.prototype = {
// D:DELIVER, DR:DELIVER-REPORT, S:SUBMIT, SR:SUBMIT-REPORT,
// ST:STATUS-REPORT, C:COMMAND
// M:Mandatory, O:Optional, X:Unavailable
// D DR S SR ST C
SMSC: null, // M M M M M M
mti: null, // M M M M M M
udhi: null, // M M O M M M
sender: null, // M X X X X X
recipient: null, // X X M X M M
pid: null, // M O M O O M
epid: null, // M O M O O M
dcs: null, // M O M O O X
mwi: null, // O O O O O O
replace: false, // O O O O O O
header: null, // M M O M M M
body: null, // M O M O O O
data: null, // M O M O O O
timestamp: null, // M X X X X X
status: null, // X X X X M X
scts: null, // X X X M M X
dt: null, // X X X X M X
// D DR S SR ST C
SMSC: null, // M M M M M M
mti: null, // M M M M M M
udhi: null, // M M O M M M
sender: null, // M X X X X X
recipient: null, // X X M X M M
pid: null, // M O M O O M
epid: null, // M O M O O M
dcs: null, // M O M O O X
mwi: null, // O O O O O O
replace: false, // O O O O O O
header: null, // M M O M M M
body: null, // M O M O O O
data: null, // M O M O O O
sentTimestamp: null, // M X X X X X
status: null, // X X X X M X
scts: null, // X X X M M X
dt: null, // X X X X M X
};
// SMSC info
@ -8904,7 +8780,7 @@ CdmaPDUHelperObject.prototype = {
header: message.header,
body: message.body,
data: message.data,
timestamp: message[PDU_CDMA_MSG_USERDATA_TIMESTAMP],
sentTimestamp: message[PDU_CDMA_MSG_USERDATA_TIMESTAMP],
language: message[PDU_CDMA_LANGUAGE_INDICATOR],
status: null,
scts: null,