Bug 881174 - part2 - cdma 3way call RIL impl. r=vicamo

This commit is contained in:
Hsin-Yi Tsai 2014-02-21 17:46:58 +08:00
Родитель 06a3eb5083
Коммит 9f41a650ba
4 изменённых файлов: 474 добавлений и 118 удалений

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

@ -2152,10 +2152,6 @@ RadioInterface.prototype = {
message.callIndex, message.callIndex,
message.notification); message.notification);
break; break;
case "conferenceError":
gTelephonyProvider.notifyConferenceError(message.errorName,
message.errorMsg);
break;
case "datacallerror": case "datacallerror":
connHandler.handleDataCallError(message); connHandler.handleDataCallError(message);
break; break;
@ -4179,9 +4175,13 @@ RadioInterface.prototype = {
}, },
sendWorkerMessage: function(rilMessageType, message, callback) { sendWorkerMessage: function(rilMessageType, message, callback) {
if (callback) {
this.workerMessenger.send(rilMessageType, message, function(response) { this.workerMessenger.send(rilMessageType, message, function(response) {
return callback.handleResponse(response); return callback.handleResponse(response);
}); });
} else {
this.workerMessenger.send(rilMessageType, message);
}
} }
}; };

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

@ -36,7 +36,6 @@ this.REQUEST_HANGUP = 12;
this.REQUEST_HANGUP_WAITING_OR_BACKGROUND = 13; this.REQUEST_HANGUP_WAITING_OR_BACKGROUND = 13;
this.REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND = 14; this.REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND = 14;
this.REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE = 15; this.REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE = 15;
this.REQUEST_SWITCH_HOLDING_AND_ACTIVE = 15;
this.REQUEST_CONFERENCE = 16; this.REQUEST_CONFERENCE = 16;
this.REQUEST_UDUB = 17; this.REQUEST_UDUB = 17;
this.REQUEST_LAST_CALL_FAIL_CAUSE = 18; this.REQUEST_LAST_CALL_FAIL_CAUSE = 18;

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

@ -1466,14 +1466,19 @@ RilObject.prototype = {
this.exitEmergencyCbMode(); this.exitEmergencyCbMode();
} }
if (this._isCdma && Object.keys(this.currentCalls).length == 1) {
// Make a Cdma 3way call.
options.featureStr = options.number;
this.sendCdmaFlashCommand(options);
} else {
options.request = REQUEST_DIAL; options.request = REQUEST_DIAL;
this.sendDialRequest(options); this.sendDialRequest(options);
}
}, },
dialEmergencyNumber: function(options, onerror) { dialEmergencyNumber: function(options, onerror) {
options.request = RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL ? options.request = RILQUIRKS_REQUEST_USE_DIAL_EMERGENCY_CALL ?
REQUEST_DIAL_EMERGENCY_CALL : REQUEST_DIAL; REQUEST_DIAL_EMERGENCY_CALL : REQUEST_DIAL;
if (this.radioState == GECKO_RADIOSTATE_OFF) { if (this.radioState == GECKO_RADIOSTATE_OFF) {
if (DEBUG) { if (DEBUG) {
this.context.debug("Automatically enable radio for an emergency call."); this.context.debug("Automatically enable radio for an emergency call.");
@ -1488,7 +1493,13 @@ RilObject.prototype = {
return; return;
} }
if (this._isCdma && Object.keys(this.currentCalls).length == 1) {
// Make a Cdma 3way call.
options.featureStr = options.number;
this.sendCdmaFlashCommand(options);
} else {
this.sendDialRequest(options); this.sendDialRequest(options);
}
}, },
sendDialRequest: function(options) { sendDialRequest: function(options) {
@ -1503,6 +1514,15 @@ RilObject.prototype = {
Buf.sendParcel(); Buf.sendParcel();
}, },
sendCdmaFlashCommand: function(options) {
let Buf = this.context.Buf;
options.isCdma = true;
options.request = REQUEST_CDMA_FLASH;
Buf.newParcel(options.request, options);
Buf.writeString(options.featureStr);
Buf.sendParcel();
},
/** /**
* Hang up all calls * Hang up all calls
*/ */
@ -1618,22 +1638,37 @@ RilObject.prototype = {
holdCall: function(options) { holdCall: function(options) {
let call = this.currentCalls[options.callIndex]; let call = this.currentCalls[options.callIndex];
if (call && call.state == CALL_STATE_ACTIVE) { if (!call) {
options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
options.success = false;
this.sendChromeMessage(options);
return;
}
let Buf = this.context.Buf; let Buf = this.context.Buf;
if (this._isCdma) { if (this._isCdma) {
Buf.newParcel(REQUEST_CDMA_FLASH); options.featureStr = "";
Buf.writeString(""); this.sendCdmaFlashCommand(options);
Buf.sendParcel(); } else if (call.state == CALL_STATE_ACTIVE) {
} else { Buf.simpleRequest(REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, options);
Buf.simpleRequest(REQUEST_SWITCH_HOLDING_AND_ACTIVE);
}
} }
}, },
resumeCall: function(options) { resumeCall: function(options) {
let call = this.currentCalls[options.callIndex]; let call = this.currentCalls[options.callIndex];
if (call && call.state == CALL_STATE_HOLDING) { if (!call) {
this.context.Buf.simpleRequest(REQUEST_SWITCH_HOLDING_AND_ACTIVE); options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
options.success = false;
this.sendChromeMessage(options);
return;
}
let Buf = this.context.Buf;
if (this._isCdma) {
options.featureStr = "";
this.sendCdmaFlashCommand(options);
} else if (call.state == CALL_STATE_HOLDING) {
Buf.simpleRequest(REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, options);
} }
}, },
@ -1641,24 +1676,52 @@ RilObject.prototype = {
_hasConferenceRequest: false, _hasConferenceRequest: false,
conferenceCall: function(options) { conferenceCall: function(options) {
let Buf = this.context.Buf;
if (this._isCdma) {
options.featureStr = "";
this.sendCdmaFlashCommand(options);
} else {
this._hasConferenceRequest = true; this._hasConferenceRequest = true;
this.context.Buf.simpleRequest(REQUEST_CONFERENCE, options); Buf.simpleRequest(REQUEST_CONFERENCE, options);
}
}, },
separateCall: function(options) { separateCall: function(options) {
let call = this.currentCalls[options.callIndex];
if (!call) {
options.errorName = "removeError";
options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
options.success = false;
this.sendChromeMessage(options);
return;
}
let Buf = this.context.Buf; let Buf = this.context.Buf;
if (this._isCdma) {
options.featureStr = "";
this.sendCdmaFlashCommand(options);
} else {
Buf.newParcel(REQUEST_SEPARATE_CONNECTION, options); Buf.newParcel(REQUEST_SEPARATE_CONNECTION, options);
Buf.writeInt32(1); Buf.writeInt32(1);
Buf.writeInt32(options.callIndex); Buf.writeInt32(options.callIndex);
Buf.sendParcel(); Buf.sendParcel();
}
}, },
holdConference: function() { holdConference: function() {
this.context.Buf.simpleRequest(REQUEST_SWITCH_HOLDING_AND_ACTIVE); if (this._isCdma) {
return;
}
this.context.Buf.simpleRequest(REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE);
}, },
resumeConference: function() { resumeConference: function() {
this.context.Buf.simpleRequest(REQUEST_SWITCH_HOLDING_AND_ACTIVE); if (this._isCdma) {
return;
}
this.context.Buf.simpleRequest(REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE);
}, },
/** /**
@ -5292,31 +5355,27 @@ RilObject.prototype[REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND] = function REQU
this.getCurrentCalls(); this.getCurrentCalls();
}; };
RilObject.prototype[REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = function REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE(length, options) { RilObject.prototype[REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = function REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE(length, options) {
if (options.rilRequestError) { options.success = (options.rilRequestError === 0);
return; if (!options.success) {
} options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.getCurrentCalls();
};
RilObject.prototype[REQUEST_SWITCH_HOLDING_AND_ACTIVE] = function REQUEST_SWITCH_HOLDING_AND_ACTIVE(length, options) {
if (options.rilRequestError) {
return;
}
// XXX Normally we should get a UNSOLICITED_RESPONSE_CALL_STATE_CHANGED parcel
// notifying us of call state changes, but sometimes we don't (have no idea why).
// this.getCurrentCalls() helps update the call state actively.
this.getCurrentCalls();
};
RilObject.prototype[REQUEST_CONFERENCE] = function REQUEST_CONFERENCE(length, options) {
if (options.rilRequestError) {
this._hasConferenceRequest = false;
options = {rilMessageType: "conferenceError",
errorName: "addError",
errorMsg: RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError]};
this.sendChromeMessage(options); this.sendChromeMessage(options);
return; return;
} }
this.sendChromeMessage(options);
this.getCurrentCalls();
};
RilObject.prototype[REQUEST_CONFERENCE] = function REQUEST_CONFERENCE(length, options) {
options.success = (options.rilRequestError === 0);
if (!options.success) {
this._hasConferenceRequest = false;
options.errorName = "addError";
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options);
return;
}
this.sendChromeMessage(options);
}; };
RilObject.prototype[REQUEST_UDUB] = null; RilObject.prototype[REQUEST_UDUB] = null;
RilObject.prototype[REQUEST_LAST_CALL_FAIL_CAUSE] = function REQUEST_LAST_CALL_FAIL_CAUSE(length, options) { RilObject.prototype[REQUEST_LAST_CALL_FAIL_CAUSE] = function REQUEST_LAST_CALL_FAIL_CAUSE(length, options) {
@ -5903,13 +5962,15 @@ RilObject.prototype[REQUEST_BASEBAND_VERSION] = function REQUEST_BASEBAND_VERSIO
if (DEBUG) this.context.debug("Baseband version: " + this.basebandVersion); if (DEBUG) this.context.debug("Baseband version: " + this.basebandVersion);
}; };
RilObject.prototype[REQUEST_SEPARATE_CONNECTION] = function REQUEST_SEPARATE_CONNECTION(length, options) { RilObject.prototype[REQUEST_SEPARATE_CONNECTION] = function REQUEST_SEPARATE_CONNECTION(length, options) {
if (options.rilRequestError) { options.success = (options.rilRequestError === 0);
options = {rilMessageType: "conferenceError", if (!options.success) {
errorName: "removeError", options.errorName = "removeError";
errorMsg: RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError]}; options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options); this.sendChromeMessage(options);
return; return;
} }
this.sendChromeMessage(options);
}; };
RilObject.prototype[REQUEST_SET_MUTE] = null; RilObject.prototype[REQUEST_SET_MUTE] = null;
RilObject.prototype[REQUEST_GET_MUTE] = null; RilObject.prototype[REQUEST_GET_MUTE] = null;
@ -6125,7 +6186,19 @@ RilObject.prototype[REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE] = function
options.enabled = enabled[0] ? true : false; options.enabled = enabled[0] ? true : false;
this.sendChromeMessage(options); this.sendChromeMessage(options);
}; };
RilObject.prototype[REQUEST_CDMA_FLASH] = null; RilObject.prototype[REQUEST_CDMA_FLASH] = function REQUEST_CDMA_FLASH(length, options) {
options.success = (options.rilRequestError === 0);
if (!options.success) {
if (options.rilMessageType === "conferenceCall") {
options.errorName = "addError";
} else if (options.rilMessageType === "separateCall") {
options.errorName = "removeError";
}
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
}
this.sendChromeMessage(options);
};
RilObject.prototype[REQUEST_CDMA_BURST_DTMF] = null; RilObject.prototype[REQUEST_CDMA_BURST_DTMF] = null;
RilObject.prototype[REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY] = null; RilObject.prototype[REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY] = null;
RilObject.prototype[REQUEST_CDMA_SEND_SMS] = function REQUEST_CDMA_SEND_SMS(length, options) { RilObject.prototype[REQUEST_CDMA_SEND_SMS] = function REQUEST_CDMA_SEND_SMS(length, options) {

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

@ -32,6 +32,9 @@ const nsITelephonyProvider = Ci.nsITelephonyProvider;
const CALL_WAKELOCK_TIMEOUT = 5000; const CALL_WAKELOCK_TIMEOUT = 5000;
// Index of the CDMA second call which isn't held in RIL but only in TelephoyProvider.
const CDMA_SECOND_CALL_INDEX = 2;
let DEBUG; let DEBUG;
function debug(s) { function debug(s) {
dump("TelephonyProvider: " + s + "\n"); dump("TelephonyProvider: " + s + "\n");
@ -112,6 +115,7 @@ ConferenceCall.prototype = {
function TelephonyProvider() { function TelephonyProvider() {
this._numClients = gRadioInterfaceLayer.numRadioInterfaces; this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
this._listeners = []; this._listeners = [];
this._currentCalls = {};
this._updateDebugFlag(); this._updateDebugFlag();
this.defaultServiceId = this._getDefaultServiceId(); this.defaultServiceId = this._getDefaultServiceId();
@ -119,6 +123,10 @@ function TelephonyProvider() {
Services.prefs.addObserver(kPrefDefaultServiceId, this, false); Services.prefs.addObserver(kPrefDefaultServiceId, this, false);
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
for (let i = 0; i < this._numClients; ++i) {
this._enumerateCallsForClient(i);
}
} }
TelephonyProvider.prototype = { TelephonyProvider.prototype = {
classID: GONK_TELEPHONYPROVIDER_CID, classID: GONK_TELEPHONYPROVIDER_CID,
@ -340,6 +348,29 @@ TelephonyProvider.prototype = {
return id; return id;
}, },
_currentCalls: null,
_enumerateCallsForClient: function(aClientId) {
if (DEBUG) debug("Enumeration of calls for client " + aClientId);
this._getClient(aClientId).sendWorkerMessage("enumerateCalls", null,
(function(response) {
if (!this._currentCalls[aClientId]) {
this._currentCalls[aClientId] = {};
}
for (let call of response.calls) {
call.clientId = aClientId;
call.state = this._convertRILCallState(call.state);
call.isActive = this._matchActiveSingleCall(call);
call.isSwitchable = true;
call.isMergeable = true;
this._currentCalls[aClientId][call.callIndex] = call;
}
return false;
}).bind(this));
},
/** /**
* nsITelephonyProvider interface. * nsITelephonyProvider interface.
*/ */
@ -363,41 +394,24 @@ TelephonyProvider.prototype = {
this._listeners.splice(index, 1); this._listeners.splice(index, 1);
}, },
_enumerateCallsForClient: function(aClientId, aListener) {
if (DEBUG) debug("Enumeration of calls for client " + aClientId);
let deferred = Promise.defer();
this._getClient(aClientId).sendWorkerMessage("enumerateCalls", null,
(function(response) {
for (let call of response.calls) {
call.clientId = aClientId;
call.state = this._convertRILCallState(call.state);
call.isActive = this._matchActiveSingleCall(call);
aListener.enumerateCallState(call.clientId, call.callIndex,
call.state, call.number,
call.isActive, call.isOutgoing,
call.isEmergency, call.isConference);
}
deferred.resolve();
return false;
}).bind(this));
return deferred.promise;
},
enumerateCalls: function(aListener) { enumerateCalls: function(aListener) {
if (DEBUG) debug("Requesting enumeration of calls for callback"); if (DEBUG) debug("Requesting enumeration of calls for callback");
let promise = Promise.resolve(); for (let cid = 0; cid < this._numClients; ++cid) {
for (let i = 0; i < this._numClients; ++i) { let calls = this._currentCalls[cid];
promise = promise.then(this._enumerateCallsForClient.bind(this, i, aListener)); if (!calls) {
continue;
}
for (let i = 0, indexes = Object.keys(calls); i < indexes.length; ++i) {
let call = calls[indexes[i]];
aListener.enumerateCallState(call.clientId, call.callIndex,
call.state, call.number,
call.isActive, call.isOutgoing,
call.isEmergency, call.isConference,
call.isSwitchable, call.isMergeable);
}
} }
promise.then(function() {
aListener.enumerateCallStateComplete(); aListener.enumerateCallStateComplete();
});
}, },
isDialing: false, isDialing: false,
@ -425,23 +439,70 @@ TelephonyProvider.prototype = {
return; return;
} }
function onCdmaDialSuccess() {
let indexes = Object.keys(this._currentCalls[aClientId]);
if (indexes.length != 1 ) {
aTelephonyCallback.notifyDialSuccess();
return;
}
// RIL doesn't hold the 2nd call. We create one by ourselves.
let childCall = {
callIndex: CDMA_SECOND_CALL_INDEX,
state: RIL.CALL_STATE_DIALING,
number: aNumber,
isOutgoing: true,
isEmergency: false,
isConference: false,
isSwitchable: false,
isMergeable: true,
parentId: indexes[0]
};
aTelephonyCallback.notifyDialSuccess();
// Manual update call state according to the request response.
this.notifyCallStateChanged(aClientId, childCall);
childCall.state = RIL.CALL_STATE_ACTIVE;
this.notifyCallStateChanged(aClientId, childCall);
let parentCall = this._currentCalls[aClientId][childCall.parentId];
parentCall.childId = CDMA_SECOND_CALL_INDEX;
parentCall.state = RIL.CALL_STATE_HOLDING;
parentCall.isSwitchable = false;
parentCall.isMergeable = true;
this.notifyCallStateChanged(aClientId, parentCall);
};
this.isDialing = true; this.isDialing = true;
this._getClient(aClientId).sendWorkerMessage("dial", { this._getClient(aClientId).sendWorkerMessage("dial", {
number: aNumber, number: aNumber,
isDialEmergency: aIsEmergency isDialEmergency: aIsEmergency
}, (function(response) { }, (function(response) {
this.isDialing = false; this.isDialing = false;
if (response.success) { if (!response.success) {
aTelephonyCallback.notifyDialSuccess();
} else {
aTelephonyCallback.notifyDialError(response.errorMsg); aTelephonyCallback.notifyDialError(response.errorMsg);
return false;
}
if (response.isCdma) {
onCdmaDialSuccess.call(this);
} else {
aTelephonyCallback.notifyDialSuccess();
} }
return false; return false;
}).bind(this)); }).bind(this));
}, },
hangUp: function(aClientId, aCallIndex) { hangUp: function(aClientId, aCallIndex) {
let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
if (parentId) {
// Should release both, child and parent, together. Since RIL holds only
// the parent call, we send 'parentId' to RIL.
this.hangUp(aClientId, parentId);
} else {
this._getClient(aClientId).sendWorkerMessage("hangUp", { callIndex: aCallIndex }); this._getClient(aClientId).sendWorkerMessage("hangUp", { callIndex: aCallIndex });
}
}, },
startTone: function(aClientId, aDtmfChar) { startTone: function(aClientId, aDtmfChar) {
@ -461,19 +522,187 @@ TelephonyProvider.prototype = {
}, },
holdCall: function(aClientId, aCallIndex) { holdCall: function(aClientId, aCallIndex) {
this._getClient(aClientId).sendWorkerMessage("holdCall", { callIndex: aCallIndex }); let call = this._currentCalls[aClientId][aCallIndex];
if (!call || !call.isSwitchable) {
// TODO: Bug 975949 - [B2G] Telephony should throw exceptions when some
// operations aren't allowed instead of simply ignoring them.
return;
}
let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
if (parentId) {
this.resumeCall(aClientId, parentId);
return;
}
function onCdmaHoldCallSuccess() {
let call = this._currentCalls[aClientId][aCallIndex];
if (!call) {
return;
}
call.state = RIL.CALL_STATE_HOLDING;
this.notifyCallStateChanged(aClientId, call);
if (!call.childId) {
return;
}
let childCall = this._currentCalls[aClientId][call.childId];
childCall.state = RIL.CALL_STATE_ACTIVE;
this.notifyCallStateChanged(aClientId, childCall);
};
this._getClient(aClientId).sendWorkerMessage("holdCall", {
callIndex: aCallIndex
},(function(response) {
if (!response.success) {
return false;
}
if (response.isCdma) {
onCdmaHoldCallSuccess.call(this);
}
return false;
}).bind(this));
}, },
resumeCall: function(aClientId, aCallIndex) { resumeCall: function(aClientId, aCallIndex) {
this._getClient(aClientId).sendWorkerMessage("resumeCall", { callIndex: aCallIndex }); let call = this._currentCalls[aClientId][aCallIndex];
if (!call || !call.isSwitchable) {
// TODO: Bug 975949 - [B2G] Telephony should throw exceptions when some
// operations aren't allowed instead of simply ignoring them.
return;
}
let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
if (parentId) {
this.holdCall(aClientId, parentId);
return;
}
function onCdmaResumeCallSuccess() {
let call = this._currentCalls[aClientId][aCallIndex];
if (!call) {
return;
}
call.state = RIL.CALL_STATE_ACTIVE;
this.notifyCallStateChanged(aClientId, call);
let childId = call.childId;
if (!childId) {
return;
}
let childCall = this._currentCalls[aClientId][childId];
childCall.state = RIL.CALL_STATE_HOLDING;
this.notifyCallStateChanged(aClientId, childCall);
};
this._getClient(aClientId).sendWorkerMessage("resumeCall", {
callIndex: aCallIndex
},(function(response) {
if (!response.success) {
return false;
}
if (response.isCdma) {
onCdmaResumeCallSuccess.call(this);
}
return false;
}).bind(this));
}, },
conferenceCall: function(aClientId) { conferenceCall: function(aClientId) {
this._getClient(aClientId).sendWorkerMessage("conferenceCall"); let indexes = Object.keys(this._currentCalls[aClientId]);
if (indexes.length < 2) {
// TODO: Bug 975949 - [B2G] Telephony should throw exceptions when some
// operations aren't allowed instead of simply ignoring them.
return;
}
for (let i = 0; i < indexes.length; ++i) {
let call = this._currentCalls[aClientId][indexes[i]];
if (!call.isMergeable) {
return;
}
}
function onCdmaConferenceCallSuccess() {
let indexes = Object.keys(this._currentCalls[aClientId]);
if (indexes.length < 2) {
return;
}
for (let i = 0; i < indexes.length; ++i) {
let call = this._currentCalls[aClientId][indexes[i]];
call.state = RIL.CALL_STATE_ACTIVE;
call.isConference = true;
this.notifyCallStateChanged(aClientId, call);
}
this.notifyConferenceCallStateChanged(RIL.CALL_STATE_ACTIVE);
};
this._getClient(aClientId).sendWorkerMessage("conferenceCall", null,
(function(response) {
if (!response.success) {
this._notifyAllListeners("notifyConferenceError", [response.errorName,
response.errorMsg]);
return false;
}
if (response.isCdma) {
onCdmaConferenceCallSuccess.call(this);
}
return false;
}).bind(this));
}, },
separateCall: function(aClientId, aCallIndex) { separateCall: function(aClientId, aCallIndex) {
this._getClient(aClientId).sendWorkerMessage("separateCall", { callIndex: aCallIndex }); let call = this._currentCalls[aClientId][aCallIndex];
if (!call || !call.isConference) {
// TODO: Bug 975949 - [B2G] Telephony should throw exceptions when some
// operations aren't allowed instead of simply ignoring them.
return;
}
let parentId = call.parentId;
if (parentId) {
this.separateCall(aClientId, parentId);
return;
}
function onCdmaSeparateCallSuccess() {
// See 3gpp2, S.R0006-522-A v1.0. Table 4, XID 6S.
let call = this._currentCalls[aClientId][aCallIndex];
if (!call || !call.isConference) {
return;
}
let childId = call.childId;
if (!childId) {
return;
}
let childCall = this._currentCalls[aClientId][childId];
this.notifyCallDisconnected(aClientId, childCall);
};
this._getClient(aClientId).sendWorkerMessage("separateCall", {
callIndex: aCallIndex
}, (function(response) {
if (!response.success) {
this._notifyAllListeners("notifyConferenceError", [response.errorName,
response.errorMsg]);
return false;
}
if (response.isCdma) {
onCdmaSeparateCallSuccess.call(this);
}
return false;
}).bind(this));
}, },
holdConference: function(aClientId) { holdConference: function(aClientId) {
@ -540,6 +769,31 @@ TelephonyProvider.prototype = {
aCall.clientId = aClientId; aCall.clientId = aClientId;
this._updateCallAudioState(aCall, null); this._updateCallAudioState(aCall, null);
let manualConfStateChange = false;
let childId = this._currentCalls[aClientId][aCall.callIndex].childId;
if (childId) {
// Child cannot live without parent.
let childCall = this._currentCalls[aClientId][childId];
this.notifyCallDisconnected(aClientId, childCall);
} else {
let parentId = this._currentCalls[aClientId][aCall.callIndex].parentId;
if (parentId) {
let parentCall = this._currentCalls[aClientId][parentId];
// The child is going to be released.
delete parentCall.childId;
if (parentCall.isConference) {
// As the child is going to be gone, the parent should be moved out
// of conference accordingly.
manualConfStateChange = true;
parentCall.isConference = false;
parentCall.isSwitchable = true;
parentCall.isMergeable = true;
aCall.isConference = false;
this.notifyCallStateChanged(aClientId, parentCall, true);
}
}
}
if (!aCall.failCause || if (!aCall.failCause ||
aCall.failCause === RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) { aCall.failCause === RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) {
this._notifyAllListeners("callStateChanged", [aClientId, this._notifyAllListeners("callStateChanged", [aClientId,
@ -549,11 +803,17 @@ TelephonyProvider.prototype = {
aCall.isActive, aCall.isActive,
aCall.isOutgoing, aCall.isOutgoing,
aCall.isEmergency, aCall.isEmergency,
aCall.isConference]); aCall.isConference,
return; aCall.isSwitchable,
} aCall.isMergeable]);
} else {
this.notifyCallError(aClientId, aCall.callIndex, aCall.failCause); this.notifyCallError(aClientId, aCall.callIndex, aCall.failCause);
}
delete this._currentCalls[aClientId][aCall.callIndex];
if (manualConfStateChange) {
this.notifyConferenceCallStateChanged(RIL.CALL_STATE_UNKNOWN);
}
}, },
/** /**
@ -581,10 +841,13 @@ TelephonyProvider.prototype = {
* Handle call state changes by updating our current state and the audio * Handle call state changes by updating our current state and the audio
* system. * system.
*/ */
notifyCallStateChanged: function(aClientId, aCall) { notifyCallStateChanged: function(aClientId, aCall, aSkipStateConversion) {
if (DEBUG) debug("handleCallStateChange: " + JSON.stringify(aCall)); if (DEBUG) debug("handleCallStateChange: " + JSON.stringify(aCall));
if (!aSkipStateConversion) {
aCall.state = this._convertRILCallState(aCall.state); aCall.state = this._convertRILCallState(aCall.state);
}
if (aCall.state == nsITelephonyProvider.CALL_STATE_DIALING) { if (aCall.state == nsITelephonyProvider.CALL_STATE_DIALING) {
gSystemMessenger.broadcastMessage("telephony-new-call", {}); gSystemMessenger.broadcastMessage("telephony-new-call", {});
} }
@ -592,14 +855,35 @@ TelephonyProvider.prototype = {
aCall.clientId = aClientId; aCall.clientId = aClientId;
this._updateCallAudioState(aCall, null); this._updateCallAudioState(aCall, null);
let call = this._currentCalls[aClientId][aCall.callIndex];
if (call) {
call.state = aCall.state;
call.isConference = aCall.isConference;
call.isEmergency = aCall.isEmergency;
call.isActive = aCall.isActive;
call.isSwitchable = aCall.isSwitchable != null ?
aCall.isSwitchable : call.isSwitchable;
call.isMergeable = aCall.isMergeable != null ?
aCall.isMergeable : call.isMergeable;
} else {
call = aCall;
call.isSwitchable = aCall.isSwitchable != null ?
aCall.isSwitchable : true;
call.isMergeable = aCall.isMergeable != null ?
aCall.isMergeable : true;
this._currentCalls[aClientId][aCall.callIndex] = call;
}
this._notifyAllListeners("callStateChanged", [aClientId, this._notifyAllListeners("callStateChanged", [aClientId,
aCall.callIndex, call.callIndex,
aCall.state, call.state,
aCall.number, call.number,
aCall.isActive, call.isActive,
aCall.isOutgoing, call.isOutgoing,
aCall.isEmergency, call.isEmergency,
aCall.isConference]); call.isConference,
call.isSwitchable,
call.isMergeable]);
}, },
notifyCdmaCallWaiting: function(aClientId, aNumber) { notifyCdmaCallWaiting: function(aClientId, aNumber) {
@ -607,6 +891,12 @@ TelephonyProvider.prototype = {
// the sleep mode when the RIL handles the incoming call. // the sleep mode when the RIL handles the incoming call.
this._acquireCallRingWakeLock(); this._acquireCallRingWakeLock();
let call = this._currentCalls[aClientId][CDMA_SECOND_CALL_INDEX];
if (call) {
// TODO: Bug 977503 - B2G RIL: [CDMA] update callNumber when a waiting
// call comes after a 3way call.
this.notifyCallDisconnected(aClientId, call);
}
this._notifyAllListeners("notifyCdmaCallWaiting", [aClientId, aNumber]); this._notifyAllListeners("notifyCdmaCallWaiting", [aClientId, aNumber]);
}, },
@ -624,12 +914,6 @@ TelephonyProvider.prototype = {
this._notifyAllListeners("conferenceCallStateChanged", [aState]); this._notifyAllListeners("conferenceCallStateChanged", [aState]);
}, },
notifyConferenceError: function(aName, aMessage) {
if (DEBUG) debug("handleConferenceError: " + aName + "." +
" Error details: " + aMessage);
this._notifyAllListeners("notifyConferenceError", [aName, aMessage]);
},
/** /**
* nsIObserver interface. * nsIObserver interface.
*/ */