Bug 713426 - Support PDP data call in ril_worker. r=philikon

This commit is contained in:
Thinker Li 2012-01-17 17:34:09 -08:00
parent be3470fa42
commit 2fcc5aade6
4 changed files with 440 additions and 12 deletions

View File

@ -53,7 +53,31 @@ interface nsITelephoneCallback : nsISupports
in boolean isActive);
};
[scriptable, uuid(5be6e41d-3aee-4f5c-8284-95cf529dd6fe)]
[scriptable, uuid(8399fddd-471c-41ac-8f35-99f7dbb738ec)]
interface nsIDataCallInfo : nsISupports
{
readonly attribute unsigned long callState;
readonly attribute AString cid;
readonly attribute AString apn;
};
[scriptable, uuid(36cc4b89-0338-4ff7-a3c2-d78e60f2ea98)]
interface nsIPhoneDataCallCallback : nsISupports
{
/**
* This method is called when the state of a data call is changed.
*
* @param dataState use DATACALL_STATE_* values from nsITelephone.
*/
void dataCallStateChanged(in AString cid,
in AString interfaceName,
in unsigned short callState);
void receiveDataCallList([array,size_is(aLength)] in nsIDataCallInfo aDataCalls,
in unsigned long aLength);
};
[scriptable, uuid(78ed0beb-d6ad-42f8-929a-8d003285784f)]
interface nsITelephone : nsISupports
{
const unsigned short CALL_STATE_UNKNOWN = 0;
@ -69,6 +93,13 @@ interface nsITelephone : nsISupports
const unsigned short CALL_STATE_DISCONNECTED = 10;
const unsigned short CALL_STATE_INCOMING = 11;
// Keep consistent with GECKO_DATACALL_STATE_* values in ril_consts.js
const unsigned short DATACALL_STATE_UNKNOWN = 0;
const unsigned short DATACALL_STATE_CONNECTING = 1;
const unsigned short DATACALL_STATE_CONNECTED = 2;
const unsigned short DATACALL_STATE_DISCONNECTING = 3;
const unsigned short DATACALL_STATE_DISCONNECTED = 4;
readonly attribute jsval currentState;
void registerCallback(in nsITelephoneCallback callback);
@ -95,6 +126,20 @@ interface nsITelephone : nsISupports
attribute bool microphoneMuted;
attribute bool speakerEnabled;
// PDP APIs
void setupDataCall(in long radioTech,
in DOMString apn,
in DOMString user,
in DOMString passwd,
in long chappap,
in DOMString pdptype);
void deactivateDataCall(in DOMString cid,
in DOMString reason);
void getDataCallList();
void registerDataCallCallback(in nsIPhoneDataCallCallback callback);
void unregisterDataCallCallback(in nsIPhoneDataCallCallback callback);
/**
* SMS-related functionality.
*/

View File

@ -50,6 +50,8 @@ const DEBUG = true; // set to false to suppress debug messages
const TELEPHONYWORKER_CID =
Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
const DATACALLINFO_CID =
Components.ID("{ef474cd9-94f7-4c05-a31b-29b9de8a10d2}");
const nsIAudioManager = Ci.nsIAudioManager;
const nsITelephone = Ci.nsITelephone;
@ -110,6 +112,20 @@ XPCOMUtils.defineLazyGetter(this, "gAudioManager", function getAudioManager() {
});
function DataCallInfo(state, cid, apn) {
this.callState = state;
this.cid = cid;
this.apn = apn;
}
DataCallInfo.protoptype = {
classID: DATACALLINFO_CID,
classInfo: XPCOMUtils.generateCI({classID: DATACALLINFO_CID,
classDescription: "DataCallInfo",
interfaces: [Ci.nsIDataCallInfo]}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataCallInfo]),
};
function nsTelephonyWorker() {
this.worker = new ChromeWorker("resource://gre/modules/ril_worker.js");
this.worker.onerror = this.onerror.bind(this);
@ -178,6 +194,12 @@ nsTelephonyWorker.prototype = {
case "sms-received":
this.handleSmsReceived(message);
return;
case "datacallstatechange":
this.handleDataCallState(message);
break;
case "datacalllist":
this.handleDataCallList(message);
break;
default:
throw new Error("Don't know about this message type: " + message.type);
}
@ -278,6 +300,29 @@ nsTelephonyWorker.prototype = {
Services.obs.notifyObservers(sms, kSmsReceivedObserverTopic, null);
},
/**
* Handle data call state changes.
*/
handleDataCallState: function handleDataCallState(message) {
let ifname = message.ifname ? message.ifname : "";
this._deliverDataCallCallback("dataCallStateChanged",
[message.cid, ifname, message.state]);
},
/**
* Handle data call list.
*/
handleDataCallList: function handleDataCallList(message) {
let datacalls = [];
for each (let datacall in message.datacalls) {
datacalls.push(new DataCallInfo(datacall.state,
datacall.cid,
datacall.apn));
}
this._deliverDataCallCallback("receiveDataCallList",
[datacalls, datacalls.length]);
},
// nsIRadioWorker
worker: null,
@ -295,7 +340,7 @@ nsTelephonyWorker.prototype = {
debug("Hanging up call no. " + callIndex);
this.worker.postMessage({type: "hangUp", callIndex: callIndex});
},
startTone: function startTone(dtmfChar) {
debug("Sending Tone for " + dtmfChar);
this.worker.postMessage({type: "startTone", dtmfChar: dtmfChar});
@ -415,6 +460,84 @@ nsTelephonyWorker.prototype = {
}
}
},
registerDataCallCallback: function registerDataCallCallback(callback) {
if (this._datacall_callbacks) {
if (this._datacall_callbacks.indexOf(callback) != -1) {
throw new Error("Already registered this callback!");
}
} else {
this._datacall_callbacks = [];
}
this._datacall_callbacks.push(callback);
debug("Registering callback: " + callback);
},
unregisterDataCallCallback: function unregisterDataCallCallback(callback) {
if (!this._datacall_callbacks) {
return;
}
let index = this._datacall_callbacks.indexOf(callback);
if (index != -1) {
this._datacall_callbacks.splice(index, 1);
debug("Unregistering callback: " + callback);
}
},
_deliverDataCallCallback: function _deliverDataCallCallback(name, args) {
// We need to worry about callback registration state mutations during the
// callback firing. The behaviour we want is to *not* call any callbacks
// that are added during the firing and to *not* call any callbacks that are
// removed during the firing. To address this, we make a copy of the
// callback list before dispatching and then double-check that each callback
// is still registered before calling it.
if (!this._datacall_callbacks) {
return;
}
let callbacks = this._datacall_callbacks.slice();
for each (let callback in callbacks) {
if (this._datacall_callbacks.indexOf(callback) == -1) {
continue;
}
let handler = callback[name];
if (typeof handler != "function") {
throw new Error("No handler for " + name);
}
try {
handler.apply(callback, args);
} catch (e) {
debug("callback handler for " + name + " threw an exception: " + e);
}
}
},
setupDataCall: function(radioTech, apn, user, passwd, chappap, pdptype) {
this.worker.postMessage({type: "setupDataCall",
radioTech: radioTech,
apn: apn,
user: user,
passwd: passwd,
chappap: chappap,
pdptype: pdptype});
this._deliverDataCallCallback("dataCallStateChanged",
[message.cid, "",
RIL.GECKO_DATACALL_STATE_CONNECTING]);
},
deactivateDataCall: function(cid, reason) {
this.worker.postMessage({type: "deactivateDataCall",
cid: cid,
reason: reason});
this._deliverDataCallCallback("dataCallStateChanged",
[message.cid,
"",
RIL.GECKO_DATACALL_STATE_DISCONNECTING]);
},
getDataCallList: function getDataCallList() {
this.worker.postMessage({type: "getDataCallList"});
},
};
const NSGetFactory = XPCOMUtils.generateNSGetFactory([nsTelephonyWorker]);

View File

@ -407,6 +407,32 @@ const PDU_ALPHABET_7BIT_DEFAULT = [
"\xe0" // LATIN SMALL LETTER A WITH GRAVE
];
const DATACALL_RADIOTECHONLOGY_CDMA = 0;
const DATACALL_RADIOTECHONLOGY_GSM = 1;
const DATACALL_AUTH_NONE = 0;
const DATACALL_AUTH_PAP = 1;
const DATACALL_AUTH_CHAP = 2;
const DATACALL_AUTH_PAP_OR_CHAP = 3;
const DATACALL_PROFILE_DEFAULT = 0;
const DATACALL_PROFILE_TETHERED = 1;
const DATACALL_PROFILE_OEM_BASE = 1000;
const DATACALL_DEACTIVATE_NO_REASON = 0;
const DATACALL_DEACTIVATE_RADIO_SHUTDOWN = 1;
const DATACALL_INACTIVE = 0;
const DATACALL_ACTIVE_DOWN = 1;
const DATACALL_ACTIVE_UP = 2;
// Keep consistent with nsITelephone.DATACALL_STATE_*.
const GECKO_DATACALL_STATE_UNKNOWN = 0;
const GECKO_DATACALL_STATE_CONNECTING = 1;
const GECKO_DATACALL_STATE_CONNECTED = 2;
const GECKO_DATACALL_STATE_DISCONNECTING = 3;
const GECKO_DATACALL_STATE_DISCONNECTED = 4;
// Allow this file to be imported via Components.utils.import().
const EXPORTED_SYMBOLS = Object.keys(this);

View File

@ -116,6 +116,9 @@ let Buf = {
// Maps tokens we send out with requests to the request type, so that
// when we get a response parcel back, we know what request it was for.
this.tokenRequestMap = {};
// This is the token of last solicited response.
this.lastSolicitedToken = 0;
},
/**
@ -449,6 +452,7 @@ let Buf = {
debug("Solicited response for request type " + request_type +
", token " + token);
delete this.tokenRequestMap[token];
this.lastSolicitedToken = token;
} else if (response_type == RESPONSE_TYPE_UNSOLICITED) {
request_type = this.readUint32();
length -= UINT32_SIZE;
@ -750,7 +754,6 @@ let RIL = {
* @param dtmfChar
* DTMF signal to send, 0-9, *, +
*/
startTone: function startTone(dtmfChar) {
Buf.newParcel(REQUEST_DTMF_START);
Buf.writeString(dtmfChar);
@ -780,11 +783,78 @@ let RIL = {
* @param smsc
* Short Message Service Center address in PDU format.
*/
setSMSCAddress: function setSMSCAddress(smsc) {
Buf.newParcel(REQUEST_SET_SMSC_ADDRESS);
Buf.writeString(smsc);
Buf.sendParcel();
},
setSMSCAddress: function setSMSCAddress(smsc) {
Buf.newParcel(REQUEST_SET_SMSC_ADDRESS);
Buf.writeString(smsc);
Buf.sendParcel();
},
/**
* Setup a data call.
*
* @param radioTech
* Integer to indicate radio technology.
* DATACALL_RADIOTECHONLOGY_CDMA => CDMA.
* DATACALL_RADIOTECHONLOGY_GSM => GSM.
* @param apn
* String containing the name of the APN to connect to.
* @param user
* String containing the username for the APN.
* @param passwd
* String containing the password for the APN.
* @param chappap
* Integer containing CHAP/PAP auth type.
* DATACALL_AUTH_NONE => PAP and CHAP is never performed.
* DATACALL_AUTH_PAP => PAP may be performed.
* DATACALL_AUTH_CHAP => CHAP may be performed.
* DATACALL_AUTH_PAP_OR_CHAP => PAP / CHAP may be performed.
* @param pdptype
* String containing PDP type to request. ("IP", "IPV6", ...)
*/
setupDataCall: function (radioTech, apn, user, passwd, chappap, pdptype) {
let token = Buf.newParcel(REQUEST_SETUP_DATA_CALL);
Buf.writeUint32(7);
Buf.writeString(radioTech.toString());
Buf.writeString(DATACALL_PROFILE_DEFAULT.toString());
Buf.writeString(apn);
Buf.writeString(user);
Buf.writeString(passwd);
Buf.writeString(chappap.toString());
Buf.writeString(pdptype);
Buf.sendParcel();
return token;
},
/**
* Deactivate a data call.
*
* @param cid
* String containing CID.
* @param reason
* One of DATACALL_DEACTIVATE_* constants.
*/
deactivateDataCall: function (cid, reason) {
let token = Buf.newParcel(REQUEST_DEACTIVATE_DATA_CALL);
Buf.writeUint32(2);
Buf.writeString(cid);
Buf.writeString(reason);
Buf.sendParcel();
return token;
},
/**
* Get a list of data calls.
*/
getDataCallList: function getDataCallList() {
Buf.simpleRequest(REQUEST_DATA_CALL_LIST);
},
/**
* Get failure casue code for the most recently failed PDP context.
*/
getFailCauseCode: function getFailCauseCode() {
Buf.simpleRequest(REQUEST_LAST_CALL_FAIL_CAUSE);
},
/**
* Handle incoming requests from the RIL. We find the method that
@ -949,7 +1019,10 @@ RIL[REQUEST_SEND_SMS] = function REQUEST_SEND_SMS() {
Phone.onSendSMS(messageRef, ackPDU, errorCode);
};
RIL[REQUEST_SEND_SMS_EXPECT_MORE] = null;
RIL[REQUEST_SETUP_DATA_CALL] = null;
RIL[REQUEST_SETUP_DATA_CALL] = function REQUEST_SETUP_DATA_CALL() {
let [cid, ifname, ipaddr, dns, gw] = Buf.readStringList();
Phone.onSetupDataCall(Buf.lastSolicitedToken, cid, ifname, ipaddr, dns, gw);
};
RIL[REQUEST_SIM_IO] = null;
RIL[REQUEST_SEND_USSD] = null;
RIL[REQUEST_CANCEL_USSD] = null;
@ -973,7 +1046,9 @@ RIL[REQUEST_GET_IMEISV] = function REQUEST_GET_IMEISV() {
RIL[REQUEST_ANSWER] = function REQUEST_ANSWER(length) {
Phone.onAnswerCall();
};
RIL[REQUEST_DEACTIVATE_DATA_CALL] = null;
RIL[REQUEST_DEACTIVATE_DATA_CALL] = function REQUEST_DEACTIVATE_DATA_CALL() {
Phone.onDeactivateDataCall(Buf.lastSolicitedToken);
};
RIL[REQUEST_QUERY_FACILITY_LOCK] = null;
RIL[REQUEST_SET_FACILITY_LOCK] = null;
RIL[REQUEST_CHANGE_BARRING_PASSWORD] = null;
@ -993,7 +1068,7 @@ RIL[REQUEST_DTMF_STOP] = function REQUEST_DTMF_STOP() {
RIL[REQUEST_BASEBAND_VERSION] = function REQUEST_BASEBAND_VERSION() {
let version = Buf.readString();
Phone.onBasebandVersion(version);
},
};
RIL[REQUEST_SEPARATE_CONNECTION] = null;
RIL[REQUEST_SET_MUTE] = function REQUEST_SET_MUTE(length) {
Phone.onSetMute();
@ -1001,7 +1076,26 @@ RIL[REQUEST_SET_MUTE] = function REQUEST_SET_MUTE(length) {
RIL[REQUEST_GET_MUTE] = null;
RIL[REQUEST_QUERY_CLIP] = null;
RIL[REQUEST_LAST_DATA_CALL_FAIL_CAUSE] = null;
RIL[REQUEST_DATA_CALL_LIST] = null;
RIL[REQUEST_DATA_CALL_LIST] = function REQUEST_DATA_CALL_LIST(length) {
let datacalls = [];
if (!length) {
return;
}
let num = Buf.readUint32();
for (let i = 0; i < num; i++) {
datacalls.push({
cid: Buf.readUint32().toString(),
active: Buf.readUint32(),
type: Buf.readString(),
apn: Buf.readString(),
address: Buf.readString()
});
}
Phone.onDataCallList(datacalls);
};
RIL[REQUEST_RESET_RADIO] = null;
RIL[REQUEST_OEM_HOOK_RAW] = null;
RIL[REQUEST_OEM_HOOK_STRINGS] = null;
@ -1176,6 +1270,16 @@ let Phone = {
*/
_muted: true,
/**
* Existing data calls.
*/
currentDataCalls: {},
/**
* Tracks active requests to the RIL concerning 3G data calls.
*/
activeDataRequests: {},
get muted() {
return this._muted;
},
@ -1583,6 +1687,88 @@ let Phone = {
onAcknowledgeSMS: function onAcknowledgeSMS() {
},
onSetupDataCall: function onSetupDataCall(token, cid, ifname, ipaddr,
dns, gw) {
let options = this.activeDataRequests[token];
delete this.activeDataRequests[token];
this.currentDataCalls[cid] = {
state: GECKO_DATACALL_STATE_CONNECTED,
cid: cid,
apn: options.apn,
ifname: ifname,
ipaddr: ipaddr,
dns: dns,
gw: gw,
};
this.sendDOMMessage({type: "datacallstatechange",
state: GECKO_DATACALL_STATE_CONNECTED,
cid: cid,
apn: options.apn,
ifname: ifname,
ipaddr: ipaddr,
dns: dns,
gateway: gw});
},
onDeactivateDataCall: function onDeactivateDataCall(token) {
let options = this.activeDataRequests[token];
delete this.activeDataRequests[token];
let cid = options.cid;
if (!(cid in this.currentDataCalls)) {
return;
}
let apn = this.currentDataCalls[cid].apn;
delete this.currentDataCalls[cid];
this.sendDOMMessage({type: "datacallstatechange",
state: GECKO_DATACALL_STATE_DISCONNECTED,
cid: cid,
apn: apn});
},
onDataCallList: function onDataCallList(datacalls) {
let currentDataCalls = this.currentDataCalls;
// Sync content of currentDataCalls and data call list.
for each (let datacall in datacalls) {
let {cid, apn} = datacall;
if (datacall.active != DATACALL_INACTIVE) {
// XXX: This should be followed up.
// datacall.active == DATACALL_ACTIVE_DOWN(1) for my device
if (!(cid in currentDataCalls)) {
let datacall = {state: GECKO_DATACALL_STATE_CONNECTED,
cid: cid,
apn: apn,
ipaddr: datacall.address};
currentDataCalls[cid] = datacall;
this.sendDOMMessage({type: "datacallstatechange",
state: GECKO_DATACALL_STATE_CONNECTED,
cid: cid,
apn: apn});
}
} else { // datacall.active == DATACALL_INACTIVE
if (cid in currentDataCalls) {
delete currentDataCalls[cid];
this.sendDOMMessage({type: "datacallstatechange",
state: GECKO_DATACALL_STATE_DISCONNECTED,
cid: cid,
apn: apn});
}
}
}
let datacall_list = [];
for each (let datacall in this.currentDataCalls) {
datacall_list.push(datacall);
}
this.sendDOMMessage({type: "datacalllist",
datacalls: datacall_list});
},
/**
* Outgoing requests to the RIL. These can be triggered from the
* main thread via messages that look like this:
@ -1729,6 +1915,54 @@ let Phone = {
Math.ceil(options.body.length * 7 / 8)); //TODO: ditto
},
/**
* Setup a data call (PDP).
*/
setupDataCall: function setupDataCall(options) {
if (DEBUG) debug("setupDataCall: " + JSON.stringify(options));
let token = RIL.setupDataCall(options.radioTech, options.apn,
options.user, options.passwd,
options.chappap, options.reason);
this.activeDataRequests[token] = options;
this.sendDOMMessage({type: "datacallstatechange",
state: GECKO_DATACALL_STATE_CONNECTING,
apn: options.apn});
},
/**
* Deactivate a data call (PDP).
*/
deactivateDataCall: function deactivateDataCall(options) {
if (!(options.cid in this.currentDataCalls)) {
return;
}
let datacall = this.currentDataCalls[options.cid];
datacall.state = GECKO_DATACALL_STATE_DISCONNECTING;
let token = RIL.deactivateDataCall(options.cid, options.reason);
this.activeDataRequests[token] = options;
this.sendDOMMessage({type: "datacallstatechange",
state: GECKO_DATACALL_STATE_DISCONNECTING,
cid: options.cid,
apn: datacall.apn});
},
/**
* Get the list of data calls.
*/
getDataCallList: function getDataCallList(options) {
RIL.getDataCallList();
},
/**
* Get failure cause code for the last failed PDP context.
*/
getFailCauseCode: function getFailCauseCode(options) {
RIL.getFailCauseCode();
},
/**
* Handle incoming messages from the main UI thread.
*