Bug 743008 - WebTelephony: support multiprocess. r=philikon a=b2g-only

This commit is contained in:
Hsin-Yi Tsai 2012-04-24 12:44:42 -03:00
parent 2e9f94f527
commit b6798783e8
5 changed files with 256 additions and 129 deletions

View File

@ -23,6 +23,8 @@ const RIL_IPC_MSG_NAMES = [
"RIL:CardStateChanged",
"RIL:VoiceInfoChanged",
"RIL:DataInfoChanged",
"RIL:EnumerateCalls",
"RIL:CallStateChanged",
];
const kVoiceChangedTopic = "mobile-connection-voice-changed";
@ -101,6 +103,93 @@ RILContentHelper.prototype = {
throw Components.Exception("Not implemented", Cr.NS_ERROR_NOT_IMPLEMENTED);
},
_telephonyCallbacks: null,
_enumerationTelephonyCallbacks: null,
registerTelephonyCallback: function registerTelephonyCallback(callback) {
if (this._telephonyCallbacks) {
if (this._telephonyCallbacks.indexOf(callback) != -1) {
throw new Error("Already registered this callback!");
}
} else {
this._telephonyCallbacks = [];
}
this._telephonyCallbacks.push(callback);
debug("Registered telephony callback: " + callback);
},
unregisterTelephonyCallback: function unregisterTelephonyCallback(callback) {
if (!this._telephonyCallbacks) {
return;
}
let index = this._telephonyCallbacks.indexOf(callback);
if (index != -1) {
this._telephonyCallbacks.splice(index, 1);
debug("Unregistered telephony callback: " + callback);
}
},
enumerateCalls: function enumerateCalls(callback) {
debug("Requesting enumeration of calls for callback: " + callback);
cpmm.sendAsyncMessage("RIL:EnumerateCalls");
if (!this._enumerationTelephonyCallbacks) {
this._enumerationTelephonyCallbacks = [];
}
this._enumerationTelephonyCallbacks.push(callback);
},
startTone: function startTone(dtmfChar) {
debug("Sending Tone for " + dtmfChar);
cpmm.sendAsyncMessage("RIL:StartTone", dtmfChar);
},
stopTone: function stopTone() {
debug("Stopping Tone");
cpmm.sendAsyncMessage("RIL:StopTone");
},
dial: function dial(number) {
debug("Dialing " + number);
cpmm.sendAsyncMessage("RIL:Dial", number);
},
hangUp: function hangUp(callIndex) {
debug("Hanging up call no. " + callIndex);
cpmm.sendAsyncMessage("RIL:HangUp", callIndex);
},
answerCall: function answerCall(callIndex) {
cpmm.sendAsyncMessage("RIL:AnswerCall", callIndex);
},
rejectCall: function rejectCall(callIndex) {
cpmm.sendAsyncMessage("RIL:RejectCall", callIndex);
},
holdCall: function holdCall(callIndex) {
cpmm.sendAsyncMessage("RIL:HoldCall", callIndex);
},
resumeCall: function resumeCall(callIndex) {
cpmm.sendAsyncMessage("RIL:ResumeCall", callIndex);
},
get microphoneMuted() {
return cpmm.sendSyncMessage("RIL:GetMicrophoneMuted")[0];
},
set microphoneMuted(value) {
cpmm.sendAsyncMessage("RIL:SetMicrophoneMuted", value);
},
get speakerEnabled() {
return cpmm.sendSyncMessage("RIL:GetSpeakerEnabled")[0];
},
set speakerEnabled(value) {
cpmm.sendAsyncMessage("RIL:SetSpeakerEnabled", value);
},
// nsIObserver
observe: function observe(subject, topic, data) {
@ -136,6 +225,58 @@ RILContentHelper.prototype = {
}
Services.obs.notifyObservers(null, kDataChangedTopic, null);
break;
case "RIL:EnumerateCalls":
this.handleEnumerateCalls(msg.json);
break;
case "RIL:CallStateChanged":
this._deliverTelephonyCallback("callStateChanged",
[msg.json.callIndex, msg.json.state,
msg.json.number]);
}
},
handleEnumerateCalls: function handleEnumerateCalls(message) {
debug("handleEnumerateCalls: " + JSON.stringify(message));
let callback = this._enumerationTelephonyCallbacks.shift();
let calls = message.calls;
let activeCallIndex = message.activeCallIndex;
for (let i in calls) {
let call = calls[i];
let keepGoing;
try {
keepGoing =
callback.enumerateCallState(call.callIndex, call.state, call.number,
call.callIndex == activeCallIndex);
} catch (e) {
debug("callback handler for 'enumerateCallState' threw an " +
" exception: " + e);
keepGoing = true;
}
if (!keepGoing) {
break;
}
}
},
_deliverTelephonyCallback: function _deliverTelephonyCallback(name, args) {
if (!this._telephonyCallbacks) {
return;
}
let callbacks = this._telephonyCallbacks.slice();
for each (let callback in callbacks) {
if (this._telephonyCallbacks.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);
}
}
},

View File

@ -63,6 +63,23 @@ const kSmsDeliveredObserverTopic = "sms-delivered";
const DOM_SMS_DELIVERY_RECEIVED = "received";
const DOM_SMS_DELIVERY_SENT = "sent";
const RIL_IPC_MSG_NAMES = [
"RIL:GetRadioState",
"RIL:EnumerateCalls",
"RIL:GetMicrophoneMuted",
"RIL:SetMicrophoneMuted",
"RIL:GetSpeakerEnabled",
"RIL:SetSpeakerEnabled",
"RIL:StartTone",
"RIL:StopTone",
"RIL:Dial",
"RIL:HangUp",
"RIL:AnswerCall",
"RIL:RejectCall",
"RIL:HoldCall",
"RIL:ResumeCall",
];
XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
"@mozilla.org/sms/smsservice;1",
"nsISmsService");
@ -169,7 +186,9 @@ function RadioInterfaceLayer() {
signalStrength: null,
relSignalStrength: null},
};
ppmm.addMessageListener("RIL:GetRadioState", this);
for each (let msgname in RIL_IPC_MSG_NAMES) {
ppmm.addMessageListener(msgname, this);
}
Services.obs.addObserver(this, "xpcom-shutdown", false);
this._sentSmsEnvelopes = {};
@ -195,6 +214,45 @@ RadioInterfaceLayer.prototype = {
case "RIL:GetRadioState":
// This message is sync.
return this.radioState;
case "RIL:EnumerateCalls":
this.enumerateCalls();
break;
case "RIL:GetMicrophoneMuted":
// This message is sync.
return this.microphoneMuted;
case "RIL:SetMicrophoneMuted":
this.microphoneMuted = msg.json;
break;
case "RIL:GetSpeakerEnabled":
// This message is sync.
return this.speakerEnabled;
case "RIL:SetSpeakerEnabled":
this.speakerEnabled = msg.json;
break;
case "RIL:StartTone":
this.startTone(msg.json);
break;
case "RIL:StopTone":
this.stopTone();
break;
case "RIL:Dial":
this.dial(msg.json);
break;
case "RIL:HangUp":
this.hangUp(msg.json);
break;
case "RIL:AnswerCall":
this.answerCall(msg.json);
break;
case "RIL:RejectCall":
this.rejectCall(msg.json);
break;
case "RIL:HoldCall":
this.holdCall(msg.json);
break;
case "RIL:ResumeCall":
this.resumeCall(msg.json);
break;
}
},
@ -416,8 +474,7 @@ RadioInterfaceLayer.prototype = {
this._activeCall = call;
}
this.updateCallAudioState();
this._deliverCallback("callStateChanged",
[call.callIndex, call.state, call.number]);
ppmm.sendAsyncMessage("RIL:CallStateChanged", call);
},
/**
@ -429,10 +486,8 @@ RadioInterfaceLayer.prototype = {
this._activeCall = null;
}
this.updateCallAudioState();
this._deliverCallback("callStateChanged",
[call.callIndex,
nsIRadioInterfaceLayer.CALL_STATE_DISCONNECTED,
call.number]);
call.state = nsIRadioInterfaceLayer.CALL_STATE_DISCONNECTED;
ppmm.sendAsyncMessage("RIL:CallStateChanged", call);
},
/**
@ -440,25 +495,12 @@ RadioInterfaceLayer.prototype = {
*/
handleEnumerateCalls: function handleEnumerateCalls(calls) {
debug("handleEnumerateCalls: " + JSON.stringify(calls));
let callback = this._enumerationCallbacks.shift();
let activeCallIndex = this._activeCall ? this._activeCall.callIndex : -1;
for (let i in calls) {
let call = calls[i];
let state = convertRILCallState(call.state);
let keepGoing;
try {
keepGoing =
callback.enumerateCallState(call.callIndex, state, call.number,
call.callIndex == activeCallIndex);
} catch (e) {
debug("callback handler for 'enumerateCallState' threw an " +
" exception: " + e);
keepGoing = true;
}
if (!keepGoing) {
break;
}
calls[i].state = convertRILCallState(calls[i].state);
}
ppmm.sendAsyncMessage("RIL:EnumerateCalls",
{calls: calls, activeCallIndex: activeCallIndex});
},
portAddressedSmsApps: null,
@ -594,7 +636,9 @@ RadioInterfaceLayer.prototype = {
observe: function observe(subject, topic, data) {
if (topic == "xpcom-shutdown") {
ppmm.removeMessageListener("RIL:GetRadioState", this);
for each (let msgname in RIL_IPC_MSG_NAMES) {
ppmm.removeMessageListener(msgname, this);
}
Services.obs.removeObserver(this, "xpcom-shutdown");
ppmm = null;
}
@ -608,6 +652,13 @@ RadioInterfaceLayer.prototype = {
radioState: null,
// Handle phone functions of nsIRILContentHelper
enumerateCalls: function enumerateCalls() {
debug("Requesting enumeration of calls for callback");
this.worker.postMessage({type: "enumerateCalls"});
},
dial: function dial(number) {
debug("Dialing " + number);
this.worker.postMessage({type: "dial", number: number});
@ -1058,68 +1109,6 @@ RadioInterfaceLayer.prototype = {
this.worker.postMessage(options);
},
_callbacks: null,
_enumerationCallbacks: null,
registerCallback: function registerCallback(callback) {
if (this._callbacks) {
if (this._callbacks.indexOf(callback) != -1) {
throw new Error("Already registered this callback!");
}
} else {
this._callbacks = [];
}
this._callbacks.push(callback);
debug("Registered callback: " + callback);
},
unregisterCallback: function unregisterCallback(callback) {
if (!this._callbacks) {
return;
}
let index = this._callbacks.indexOf(callback);
if (index != -1) {
this._callbacks.splice(index, 1);
debug("Unregistered callback: " + callback);
}
},
enumerateCalls: function enumerateCalls(callback) {
debug("Requesting enumeration of calls for callback: " + callback);
this.worker.postMessage({type: "enumerateCalls"});
if (!this._enumerationCallbacks) {
this._enumerationCallbacks = [];
}
this._enumerationCallbacks.push(callback);
},
_deliverCallback: function _deliverCallback(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._callbacks) {
return;
}
let callbacks = this._callbacks.slice();
for each (let callback in callbacks) {
if (this._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);
}
}
},
registerDataCallCallback: function registerDataCallCallback(callback) {
if (this._datacall_callbacks) {
if (this._datacall_callbacks.indexOf(callback) != -1) {

View File

@ -62,8 +62,8 @@ interface nsIRILTelephonyCallback : nsISupports
in AString number);
/**
* Called when nsIRadioInterfaceLayer is asked to enumerate the current
* telephony call state (nsIRadioInterfaceLayer::enumerateCalls). This is
* Called when nsIRILContentHelper is asked to enumerate the current
* telephony call state (nsIRILContentHelper::enumerateCalls). This is
* called once per call that is currently managed by the RIL.
*
* @param callIndex
@ -125,38 +125,11 @@ interface nsIRILDataCallback : nsISupports
* Helper that runs in the content process and exposes information
* to the DOM.
*/
[scriptable, uuid(cc9832fd-d3ce-4cab-9abe-48c6046bcebe)]
[scriptable, uuid(2f8b0929-2ecf-498c-bfa7-42690509696e)]
interface nsIRILContentHelper : nsIMobileConnectionProvider
{
};
[scriptable, uuid(d2025763-fc32-436e-b207-0228ea1ccd12)]
interface nsIRadioInterfaceLayer : nsISupports
{
const unsigned short CALL_STATE_UNKNOWN = 0;
const unsigned short CALL_STATE_DIALING = 1;
const unsigned short CALL_STATE_ALERTING = 2;
const unsigned short CALL_STATE_BUSY = 3;
const unsigned short CALL_STATE_CONNECTING = 4;
const unsigned short CALL_STATE_CONNECTED = 5;
const unsigned short CALL_STATE_HOLDING = 6;
const unsigned short CALL_STATE_HELD = 7;
const unsigned short CALL_STATE_RESUMING = 8;
const unsigned short CALL_STATE_DISCONNECTING = 9;
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 radioState;
void registerCallback(in nsIRILTelephonyCallback callback);
void unregisterCallback(in nsIRILTelephonyCallback callback);
void registerTelephonyCallback(in nsIRILTelephonyCallback callback);
void unregisterTelephonyCallback(in nsIRILTelephonyCallback callback);
/**
* Will continue calling callback.enumerateCallState until the callback
@ -180,6 +153,32 @@ interface nsIRadioInterfaceLayer : nsISupports
attribute bool microphoneMuted;
attribute bool speakerEnabled;
};
[scriptable, uuid(d976f4c2-af5b-4fe1-97c2-c9c5d0d1af5c)]
interface nsIRadioInterfaceLayer : nsISupports
{
const unsigned short CALL_STATE_UNKNOWN = 0;
const unsigned short CALL_STATE_DIALING = 1;
const unsigned short CALL_STATE_ALERTING = 2;
const unsigned short CALL_STATE_BUSY = 3;
const unsigned short CALL_STATE_CONNECTING = 4;
const unsigned short CALL_STATE_CONNECTED = 5;
const unsigned short CALL_STATE_HOLDING = 6;
const unsigned short CALL_STATE_HELD = 7;
const unsigned short CALL_STATE_RESUMING = 8;
const unsigned short CALL_STATE_DISCONNECTING = 9;
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 radioState;
/**
* PDP APIs

View File

@ -53,6 +53,7 @@
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "SystemWorkerManager.h"
#include "nsRadioInterfaceLayer.h"
#include "CallEvent.h"
#include "TelephonyCall.h"
@ -131,7 +132,7 @@ Telephony::Telephony()
Telephony::~Telephony()
{
if (mRIL && mRILTelephonyCallback) {
mRIL->UnregisterCallback(mRILTelephonyCallback);
mRIL->UnregisterTelephonyCallback(mRILTelephonyCallback);
}
if (mRooted) {
@ -152,7 +153,7 @@ Telephony::~Telephony()
// static
already_AddRefed<Telephony>
Telephony::Create(nsPIDOMWindow* aOwner, nsIRadioInterfaceLayer* aRIL)
Telephony::Create(nsPIDOMWindow* aOwner, nsIRILContentHelper* aRIL)
{
NS_ASSERTION(aOwner, "Null owner!");
NS_ASSERTION(aRIL, "Null RIL!");
@ -173,7 +174,7 @@ Telephony::Create(nsPIDOMWindow* aOwner, nsIRadioInterfaceLayer* aRIL)
nsresult rv = aRIL->EnumerateCalls(telephony->mRILTelephonyCallback);
NS_ENSURE_SUCCESS(rv, nsnull);
rv = aRIL->RegisterCallback(telephony->mRILTelephonyCallback);
rv = aRIL->RegisterTelephonyCallback(telephony->mRILTelephonyCallback);
NS_ENSURE_SUCCESS(rv, nsnull);
return telephony.forget();
@ -571,11 +572,8 @@ NS_NewTelephony(nsPIDOMWindow* aWindow, nsIDOMTelephony** aTelephony)
}
}
// Security checks passed, make a telephony object.
nsIInterfaceRequestor* ireq = SystemWorkerManager::GetInterfaceRequestor();
NS_ENSURE_TRUE(ireq, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetInterface(ireq);
nsCOMPtr<nsIRILContentHelper> ril =
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
NS_ENSURE_TRUE(ril, NS_ERROR_UNEXPECTED);
nsRefPtr<Telephony> telephony = Telephony::Create(innerWindow, ril);

View File

@ -54,7 +54,7 @@ BEGIN_TELEPHONY_NAMESPACE
class Telephony : public nsDOMEventTargetHelper,
public nsIDOMTelephony
{
nsCOMPtr<nsIRadioInterfaceLayer> mRIL;
nsCOMPtr<nsIRILContentHelper> mRIL;
nsCOMPtr<nsIRILTelephonyCallback> mRILTelephonyCallback;
NS_DECL_EVENT_HANDLER(incoming)
@ -79,7 +79,7 @@ public:
nsDOMEventTargetHelper)
static already_AddRefed<Telephony>
Create(nsPIDOMWindow* aOwner, nsIRadioInterfaceLayer* aRIL);
Create(nsPIDOMWindow* aOwner, nsIRILContentHelper* aRIL);
nsIDOMEventTarget*
ToIDOMEventTarget() const
@ -112,7 +112,7 @@ public:
NotifyCallsChanged(aCall);
}
nsIRadioInterfaceLayer*
nsIRILContentHelper*
RIL() const
{
return mRIL;