2014-06-25 05:12:07 +00:00
|
|
|
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
2013-09-07 06:19:57 +00:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
2013-10-31 12:05:51 +00:00
|
|
|
Cu.import("resource://gre/modules/Promise.jsm");
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2014-05-07 22:53:17 +00:00
|
|
|
XPCOMUtils.defineLazyGetter(this, "RIL", function () {
|
|
|
|
let obj = {};
|
|
|
|
Cu.import("resource://gre/modules/ril_consts.js", obj);
|
|
|
|
return obj;
|
|
|
|
});
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2014-06-03 14:15:25 +00:00
|
|
|
const GONK_TELEPHONYSERVICE_CONTRACTID =
|
|
|
|
"@mozilla.org/telephony/gonktelephonyservice;1";
|
2015-01-08 08:33:51 +00:00
|
|
|
|
2014-06-03 14:15:25 +00:00
|
|
|
const GONK_TELEPHONYSERVICE_CID =
|
2013-09-07 06:19:57 +00:00
|
|
|
Components.ID("{67d26434-d063-4d28-9f48-5b3189788155}");
|
2014-11-06 02:20:54 +00:00
|
|
|
const MOBILECALLFORWARDINGOPTIONS_CID =
|
|
|
|
Components.ID("{79b5988b-9436-48d8-a652-88fa033f146c}");
|
2015-01-08 08:33:51 +00:00
|
|
|
const TELEPHONYCALLINFO_CID =
|
|
|
|
Components.ID("{d9e8b358-a02c-4cf3-9fc7-816c2e8d46e4}");
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2014-08-11 06:48:00 +00:00
|
|
|
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
|
2013-10-24 08:14:59 +00:00
|
|
|
|
|
|
|
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
|
|
|
|
|
2013-10-24 08:15:06 +00:00
|
|
|
const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
|
2013-10-24 08:14:59 +00:00
|
|
|
const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
|
2013-10-24 08:15:06 +00:00
|
|
|
const kPrefDefaultServiceId = "dom.telephony.defaultServiceId";
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2014-11-14 07:20:44 +00:00
|
|
|
const nsITelephonyAudioService = Ci.nsITelephonyAudioService;
|
2014-06-03 14:15:25 +00:00
|
|
|
const nsITelephonyService = Ci.nsITelephonyService;
|
2013-09-07 06:19:57 +00:00
|
|
|
|
|
|
|
const CALL_WAKELOCK_TIMEOUT = 5000;
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
// In CDMA, RIL only hold one call index. We need to fake a second call index
|
|
|
|
// in TelephonyService for 3-way calling.
|
|
|
|
const CDMA_FIRST_CALL_INDEX = 1;
|
2014-02-21 09:46:58 +00:00
|
|
|
const CDMA_SECOND_CALL_INDEX = 2;
|
|
|
|
|
2014-04-01 12:58:55 +00:00
|
|
|
const DIAL_ERROR_INVALID_STATE_ERROR = "InvalidStateError";
|
|
|
|
const DIAL_ERROR_OTHER_CONNECTION_IN_USE = "OtherConnectionInUse";
|
2014-08-11 06:48:00 +00:00
|
|
|
const DIAL_ERROR_BAD_NUMBER = RIL.GECKO_CALL_ERROR_BAD_NUMBER;
|
2014-04-01 12:58:55 +00:00
|
|
|
|
2014-06-24 10:32:41 +00:00
|
|
|
|
2015-01-07 06:37:03 +00:00
|
|
|
const TONES_GAP_DURATION = 70;
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
let DEBUG;
|
|
|
|
function debug(s) {
|
2014-06-03 14:15:25 +00:00
|
|
|
dump("TelephonyService: " + s + "\n");
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
|
|
|
|
"@mozilla.org/ril;1",
|
|
|
|
"nsIRadioInterfaceLayer");
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
|
|
|
|
"@mozilla.org/power/powermanagerservice;1",
|
|
|
|
"nsIPowerManagerService");
|
|
|
|
|
2014-10-20 08:50:57 +00:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gTelephonyMessenger",
|
|
|
|
"@mozilla.org/ril/system-messenger-helper;1",
|
|
|
|
"nsITelephonyMessenger");
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2014-11-14 07:20:44 +00:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gAudioService",
|
|
|
|
"@mozilla.org/telephony/audio-service;1",
|
|
|
|
"nsITelephonyAudioService");
|
|
|
|
|
2014-09-22 05:36:00 +00:00
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gGonkMobileConnectionService",
|
|
|
|
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
|
|
|
|
"nsIGonkMobileConnectionService");
|
|
|
|
|
2014-01-13 02:44:33 +00:00
|
|
|
XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function() {
|
2013-09-07 06:19:57 +00:00
|
|
|
let ns = {};
|
|
|
|
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
|
|
|
|
return ns.PhoneNumberUtils;
|
|
|
|
});
|
|
|
|
|
2015-01-23 10:34:13 +00:00
|
|
|
XPCOMUtils.defineLazyGetter(this, "gDialNumberUtils", function() {
|
|
|
|
let ns = {};
|
|
|
|
Cu.import("resource://gre/modules/DialNumberUtils.jsm", ns);
|
|
|
|
return ns.DialNumberUtils;
|
|
|
|
});
|
|
|
|
|
2014-11-06 02:20:54 +00:00
|
|
|
function MobileCallForwardingOptions(aOptions) {
|
|
|
|
for (let key in aOptions) {
|
|
|
|
this[key] = aOptions[key];
|
|
|
|
}
|
2014-09-22 05:36:00 +00:00
|
|
|
}
|
2014-11-06 02:20:54 +00:00
|
|
|
MobileCallForwardingOptions.prototype = {
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileCallForwardingOptions]),
|
|
|
|
classID: MOBILECALLFORWARDINGOPTIONS_CID,
|
|
|
|
classInfo: XPCOMUtils.generateCI({
|
|
|
|
classID: MOBILECALLFORWARDINGOPTIONS_CID,
|
|
|
|
classDescription: "MobileCallForwardingOptions",
|
|
|
|
interfaces: [Ci.nsIMobileCallForwardingOptions]
|
|
|
|
}),
|
|
|
|
|
|
|
|
// nsIMobileForwardingOptions
|
|
|
|
|
|
|
|
active: false,
|
|
|
|
action: Ci.nsIMobileConnection.CALL_FORWARD_ACTION_UNKNOWN,
|
|
|
|
reason: Ci.nsIMobileConnection.CALL_FORWARD_REASON_UNKNOWN,
|
|
|
|
number: null,
|
|
|
|
timeSeconds: -1,
|
|
|
|
serviceClass: Ci.nsIMobileConnection.ICC_SERVICE_CLASS_NONE
|
2014-09-22 05:36:00 +00:00
|
|
|
};
|
|
|
|
|
2015-01-08 08:33:51 +00:00
|
|
|
function TelephonyCallInfo(aCall) {
|
|
|
|
this.clientId = aCall.clientId;
|
|
|
|
this.callIndex = aCall.callIndex;
|
|
|
|
this.callState = aCall.state;
|
|
|
|
this.number = aCall.number;
|
|
|
|
this.numberPresentation = aCall.numberPresentation;
|
|
|
|
this.name = aCall.name;
|
|
|
|
this.namePresentation = aCall.namePresentation;
|
|
|
|
this.isOutgoing = aCall.isOutgoing;
|
|
|
|
this.isEmergency = aCall.isEmergency;
|
|
|
|
this.isConference = aCall.isConference;
|
|
|
|
this.isSwitchable = aCall.isSwitchable;
|
|
|
|
this.isMergeable = aCall.isMergeable;
|
|
|
|
}
|
|
|
|
TelephonyCallInfo.prototype = {
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyCallInfo]),
|
|
|
|
classID: TELEPHONYCALLINFO_CID,
|
|
|
|
classInfo: XPCOMUtils.generateCI({
|
|
|
|
classID: TELEPHONYCALLINFO_CID,
|
|
|
|
classDescription: "TelephonyCallInfo",
|
|
|
|
interfaces: [Ci.nsITelephonyCallInfo]
|
|
|
|
}),
|
|
|
|
|
|
|
|
// nsITelephonyCallInfo
|
|
|
|
|
|
|
|
clientId: 0,
|
|
|
|
callIndex: 0,
|
|
|
|
callState: nsITelephonyService.CALL_STATE_UNKNOWN,
|
|
|
|
number: "",
|
|
|
|
numberPresentation: nsITelephonyService.CALL_PRESENTATION_ALLOWED,
|
|
|
|
name: "",
|
|
|
|
namePresentation: nsITelephonyService.CALL_PRESENTATION_ALLOWED,
|
|
|
|
isOutgoing: true,
|
|
|
|
isEmergency: false,
|
|
|
|
isConference: false,
|
|
|
|
isSwitchable: true,
|
|
|
|
isMergeable: true
|
|
|
|
};
|
|
|
|
|
2014-06-03 14:15:25 +00:00
|
|
|
function TelephonyService() {
|
2013-10-31 12:05:51 +00:00
|
|
|
this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
|
2013-09-07 06:19:57 +00:00
|
|
|
this._listeners = [];
|
2014-09-22 05:36:00 +00:00
|
|
|
|
|
|
|
this._isDialing = false;
|
|
|
|
this._cachedDialRequest = null;
|
2014-02-21 09:46:58 +00:00
|
|
|
this._currentCalls = {};
|
2015-02-03 04:18:20 +00:00
|
|
|
this._currentConferenceState = nsITelephonyService.CALL_STATE_UNKNOWN;
|
2014-10-16 21:40:00 +00:00
|
|
|
this._audioStates = {};
|
2014-06-09 06:47:00 +00:00
|
|
|
|
2014-07-17 11:18:28 +00:00
|
|
|
this._cdmaCallWaitingNumber = null;
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
this._updateDebugFlag();
|
2013-10-24 08:15:06 +00:00
|
|
|
this.defaultServiceId = this._getDefaultServiceId();
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2013-10-24 08:14:59 +00:00
|
|
|
Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
|
2013-10-24 08:15:06 +00:00
|
|
|
Services.prefs.addObserver(kPrefDefaultServiceId, this, false);
|
2013-10-24 08:14:59 +00:00
|
|
|
|
|
|
|
Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
2014-02-21 09:46:58 +00:00
|
|
|
|
|
|
|
for (let i = 0; i < this._numClients; ++i) {
|
|
|
|
this._enumerateCallsForClient(i);
|
2014-10-16 21:40:00 +00:00
|
|
|
this._audioStates[i] = RIL.AUDIO_STATE_NO_CALL;
|
2014-02-21 09:46:58 +00:00
|
|
|
}
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
2014-06-03 14:15:25 +00:00
|
|
|
TelephonyService.prototype = {
|
|
|
|
classID: GONK_TELEPHONYSERVICE_CID,
|
|
|
|
classInfo: XPCOMUtils.generateCI({classID: GONK_TELEPHONYSERVICE_CID,
|
|
|
|
contractID: GONK_TELEPHONYSERVICE_CONTRACTID,
|
|
|
|
classDescription: "TelephonyService",
|
|
|
|
interfaces: [Ci.nsITelephonyService,
|
|
|
|
Ci.nsIGonkTelephonyService],
|
2013-09-07 06:19:57 +00:00
|
|
|
flags: Ci.nsIClassInfo.SINGLETON}),
|
2014-06-03 14:15:25 +00:00
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyService,
|
|
|
|
Ci.nsIGonkTelephonyService,
|
2013-09-07 06:19:57 +00:00
|
|
|
Ci.nsIObserver]),
|
|
|
|
|
2013-10-28 06:07:28 +00:00
|
|
|
// The following attributes/functions are used for acquiring/releasing the
|
|
|
|
// CPU wake lock when the RIL handles the incoming call. Note that we need
|
|
|
|
// a timer to bound the lock's life cycle to avoid exhausting the battery.
|
2013-09-07 06:19:57 +00:00
|
|
|
_callRingWakeLock: null,
|
|
|
|
_callRingWakeLockTimer: null,
|
2013-10-28 06:07:28 +00:00
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
_acquireCallRingWakeLock: function() {
|
2013-10-28 06:07:28 +00:00
|
|
|
if (!this._callRingWakeLock) {
|
|
|
|
if (DEBUG) debug("Acquiring a CPU wake lock for handling incoming call.");
|
|
|
|
this._callRingWakeLock = gPowerManagerService.newWakeLock("cpu");
|
|
|
|
}
|
|
|
|
if (!this._callRingWakeLockTimer) {
|
|
|
|
if (DEBUG) debug("Creating a timer for releasing the CPU wake lock.");
|
|
|
|
this._callRingWakeLockTimer =
|
|
|
|
Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
|
|
}
|
|
|
|
if (DEBUG) debug("Setting the timer for releasing the CPU wake lock.");
|
|
|
|
this._callRingWakeLockTimer
|
|
|
|
.initWithCallback(this._releaseCallRingWakeLock.bind(this),
|
|
|
|
CALL_WAKELOCK_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
_releaseCallRingWakeLock: function() {
|
2013-10-28 06:07:28 +00:00
|
|
|
if (DEBUG) debug("Releasing the CPU wake lock for handling incoming call.");
|
2013-09-07 06:19:57 +00:00
|
|
|
if (this._callRingWakeLockTimer) {
|
|
|
|
this._callRingWakeLockTimer.cancel();
|
|
|
|
}
|
|
|
|
if (this._callRingWakeLock) {
|
|
|
|
this._callRingWakeLock.unlock();
|
|
|
|
this._callRingWakeLock = null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
_getClient: function(aClientId) {
|
2013-10-31 12:05:51 +00:00
|
|
|
return gRadioInterfaceLayer.getRadioInterface(aClientId);
|
|
|
|
},
|
|
|
|
|
2014-08-19 04:23:00 +00:00
|
|
|
_sendToRilWorker: function(aClientId, aType, aMessage, aCallback) {
|
|
|
|
this._getClient(aClientId).sendWorkerMessage(aType, aMessage, aCallback);
|
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:19 +00:00
|
|
|
_isGsmTechGroup: function(aType) {
|
|
|
|
switch (aType) {
|
|
|
|
case null: // Handle unknown as gsm.
|
|
|
|
case "gsm":
|
|
|
|
case "gprs":
|
|
|
|
case "edge":
|
|
|
|
case "umts":
|
|
|
|
case "hsdpa":
|
|
|
|
case "hsupa":
|
|
|
|
case "hspa":
|
|
|
|
case "hspa+":
|
|
|
|
case "lte":
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_isCdmaClient: function(aClientId) {
|
|
|
|
let type = gGonkMobileConnectionService.getItemByServiceId(aClientId).voice.type;
|
|
|
|
return !this._isGsmTechGroup(type);
|
|
|
|
},
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
// An array of nsITelephonyListener instances.
|
|
|
|
_listeners: null,
|
2014-01-13 02:44:40 +00:00
|
|
|
_notifyAllListeners: function(aMethodName, aArgs) {
|
2013-09-07 06:19:57 +00:00
|
|
|
let listeners = this._listeners.slice();
|
|
|
|
for (let listener of listeners) {
|
|
|
|
if (this._listeners.indexOf(listener) == -1) {
|
|
|
|
// Listener has been unregistered in previous run.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let handler = listener[aMethodName];
|
|
|
|
try {
|
|
|
|
handler.apply(listener, aArgs);
|
|
|
|
} catch (e) {
|
|
|
|
debug("listener for " + aMethodName + " threw an exception: " + e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-10-16 21:40:00 +00:00
|
|
|
_updateAudioState: function(aAudioState) {
|
2014-06-09 06:47:00 +00:00
|
|
|
switch (aAudioState) {
|
2014-10-16 21:40:00 +00:00
|
|
|
case RIL.AUDIO_STATE_NO_CALL:
|
2014-11-14 07:20:44 +00:00
|
|
|
gAudioService.setPhoneState(nsITelephonyAudioService.PHONE_STATE_NORMAL);
|
2014-06-09 06:47:00 +00:00
|
|
|
break;
|
2014-10-16 21:40:00 +00:00
|
|
|
case RIL.AUDIO_STATE_INCOMING:
|
2014-11-14 07:20:44 +00:00
|
|
|
gAudioService.setPhoneState(nsITelephonyAudioService.PHONE_STATE_RINGTONE);
|
2014-06-09 06:47:00 +00:00
|
|
|
break;
|
2014-10-16 21:40:00 +00:00
|
|
|
case RIL.AUDIO_STATE_IN_CALL:
|
2014-11-14 07:20:44 +00:00
|
|
|
gAudioService.setPhoneState(nsITelephonyAudioService.PHONE_STATE_IN_CALL);
|
2014-06-09 06:47:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-05-06 01:01:00 +00:00
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
_convertRILCallState: function(aState) {
|
2013-09-07 06:19:57 +00:00
|
|
|
switch (aState) {
|
2013-09-12 13:00:18 +00:00
|
|
|
case RIL.CALL_STATE_UNKNOWN:
|
2014-06-03 14:15:25 +00:00
|
|
|
return nsITelephonyService.CALL_STATE_UNKNOWN;
|
2013-09-07 06:19:57 +00:00
|
|
|
case RIL.CALL_STATE_ACTIVE:
|
2014-06-03 14:15:25 +00:00
|
|
|
return nsITelephonyService.CALL_STATE_CONNECTED;
|
2013-09-07 06:19:57 +00:00
|
|
|
case RIL.CALL_STATE_HOLDING:
|
2014-06-03 14:15:25 +00:00
|
|
|
return nsITelephonyService.CALL_STATE_HELD;
|
2013-09-07 06:19:57 +00:00
|
|
|
case RIL.CALL_STATE_DIALING:
|
2014-06-03 14:15:25 +00:00
|
|
|
return nsITelephonyService.CALL_STATE_DIALING;
|
2013-09-07 06:19:57 +00:00
|
|
|
case RIL.CALL_STATE_ALERTING:
|
2014-06-03 14:15:25 +00:00
|
|
|
return nsITelephonyService.CALL_STATE_ALERTING;
|
2013-09-07 06:19:57 +00:00
|
|
|
case RIL.CALL_STATE_INCOMING:
|
|
|
|
case RIL.CALL_STATE_WAITING:
|
2014-06-03 14:15:25 +00:00
|
|
|
return nsITelephonyService.CALL_STATE_INCOMING;
|
2013-09-07 06:19:57 +00:00
|
|
|
default:
|
|
|
|
throw new Error("Unknown rilCallState: " + aState);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
_convertRILSuppSvcNotification: function(aNotification) {
|
2013-09-07 06:19:57 +00:00
|
|
|
switch (aNotification) {
|
|
|
|
case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD:
|
2014-06-03 14:15:25 +00:00
|
|
|
return nsITelephonyService.NOTIFICATION_REMOTE_HELD;
|
2013-09-07 06:19:57 +00:00
|
|
|
case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED:
|
2014-06-03 14:15:25 +00:00
|
|
|
return nsITelephonyService.NOTIFICATION_REMOTE_RESUMED;
|
2013-09-07 06:19:57 +00:00
|
|
|
default:
|
2014-02-06 10:58:46 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
debug("Unknown rilSuppSvcNotification: " + aNotification);
|
|
|
|
}
|
|
|
|
return;
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-09-22 05:36:00 +00:00
|
|
|
_rulesToCallForwardingOptions: function(aRules) {
|
2014-11-06 02:20:54 +00:00
|
|
|
return aRules.map(rule => new MobileCallForwardingOptions(rule));
|
2014-09-22 05:36:00 +00:00
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
_updateDebugFlag: function() {
|
2013-09-07 06:19:57 +00:00
|
|
|
try {
|
|
|
|
DEBUG = RIL.DEBUG_RIL ||
|
2013-10-24 08:14:59 +00:00
|
|
|
Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
|
2013-09-07 06:19:57 +00:00
|
|
|
} catch (e) {}
|
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
_getDefaultServiceId: function() {
|
2013-10-24 08:15:06 +00:00
|
|
|
let id = Services.prefs.getIntPref(kPrefDefaultServiceId);
|
|
|
|
let numRil = Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
|
|
|
|
|
|
|
|
if (id >= numRil || id < 0) {
|
|
|
|
id = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return id;
|
|
|
|
},
|
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
_currentCalls: null,
|
|
|
|
_enumerateCallsForClient: function(aClientId) {
|
|
|
|
if (DEBUG) debug("Enumeration of calls for client " + aClientId);
|
|
|
|
|
2014-08-19 04:23:00 +00:00
|
|
|
this._sendToRilWorker(aClientId, "enumerateCalls", null, response => {
|
2014-02-21 09:46:58 +00:00
|
|
|
if (!this._currentCalls[aClientId]) {
|
|
|
|
this._currentCalls[aClientId] = {};
|
|
|
|
}
|
|
|
|
for (let call of response.calls) {
|
|
|
|
call.clientId = aClientId;
|
|
|
|
call.state = this._convertRILCallState(call.state);
|
|
|
|
call.isSwitchable = true;
|
|
|
|
call.isMergeable = true;
|
|
|
|
|
|
|
|
this._currentCalls[aClientId][call.callIndex] = call;
|
|
|
|
}
|
2014-08-19 04:23:00 +00:00
|
|
|
});
|
2014-02-21 09:46:58 +00:00
|
|
|
},
|
|
|
|
|
2014-08-19 04:24:00 +00:00
|
|
|
/**
|
|
|
|
* Checks whether to temporarily suppress caller id for the call.
|
|
|
|
*
|
|
|
|
* @param aMmi
|
|
|
|
* MMI full object.
|
|
|
|
*/
|
|
|
|
_isTemporaryCLIR: function(aMmi) {
|
|
|
|
return (aMmi && aMmi.serviceCode === RIL.MMI_SC_CLIR) && aMmi.dialNumber;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map MMI procedure to CLIR MODE.
|
|
|
|
*
|
|
|
|
* @param aProcedure
|
|
|
|
* MMI procedure
|
|
|
|
*/
|
|
|
|
_getTemporaryCLIRMode: function(aProcedure) {
|
|
|
|
// In temporary mode, MMI_PROCEDURE_ACTIVATION means allowing CLI
|
|
|
|
// presentation, i.e. CLIR_SUPPRESSION. See TS 22.030, Annex B.
|
|
|
|
switch (aProcedure) {
|
|
|
|
case RIL.MMI_PROCEDURE_ACTIVATION:
|
|
|
|
return RIL.CLIR_SUPPRESSION;
|
|
|
|
case RIL.MMI_PROCEDURE_DEACTIVATION:
|
|
|
|
return RIL.CLIR_INVOCATION;
|
|
|
|
default:
|
|
|
|
return RIL.CLIR_DEFAULT;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
/**
|
2014-06-03 14:15:25 +00:00
|
|
|
* nsITelephonyService interface.
|
2013-09-07 06:19:57 +00:00
|
|
|
*/
|
|
|
|
|
2013-10-24 08:15:06 +00:00
|
|
|
defaultServiceId: 0,
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
registerListener: function(aListener) {
|
|
|
|
if (this._listeners.indexOf(aListener) >= 0) {
|
|
|
|
throw Cr.NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._listeners.push(aListener);
|
|
|
|
},
|
|
|
|
|
|
|
|
unregisterListener: function(aListener) {
|
|
|
|
let index = this._listeners.indexOf(aListener);
|
|
|
|
if (index < 0) {
|
|
|
|
throw Cr.NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._listeners.splice(index, 1);
|
|
|
|
},
|
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
enumerateCalls: function(aListener) {
|
|
|
|
if (DEBUG) debug("Requesting enumeration of calls for callback");
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
for (let cid = 0; cid < this._numClients; ++cid) {
|
|
|
|
let calls = this._currentCalls[cid];
|
|
|
|
if (!calls) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (let i = 0, indexes = Object.keys(calls); i < indexes.length; ++i) {
|
|
|
|
let call = calls[indexes[i]];
|
2015-01-08 08:33:51 +00:00
|
|
|
let callInfo = new TelephonyCallInfo(call);
|
|
|
|
aListener.enumerateCallState(callInfo);
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
2013-10-31 12:05:51 +00:00
|
|
|
}
|
2014-02-21 09:46:58 +00:00
|
|
|
aListener.enumerateCallStateComplete();
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-09-22 05:36:00 +00:00
|
|
|
_hasCalls: function(aClientId) {
|
|
|
|
return Object.keys(this._currentCalls[aClientId]).length !== 0;
|
|
|
|
},
|
|
|
|
|
2014-07-02 07:47:00 +00:00
|
|
|
_hasCallsOnOtherClient: function(aClientId) {
|
|
|
|
for (let cid = 0; cid < this._numClients; ++cid) {
|
2014-09-22 05:36:00 +00:00
|
|
|
if (cid !== aClientId && this._hasCalls(cid)) {
|
2014-07-02 07:47:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
// All calls in the conference is regarded as one conference call.
|
|
|
|
_numCallsOnLine: function(aClientId) {
|
|
|
|
let numCalls = 0;
|
|
|
|
let hasConference = false;
|
|
|
|
|
|
|
|
for (let cid in this._currentCalls[aClientId]) {
|
|
|
|
let call = this._currentCalls[aClientId][cid];
|
|
|
|
if (call.isConference) {
|
|
|
|
hasConference = true;
|
|
|
|
} else {
|
|
|
|
numCalls++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hasConference ? numCalls + 1 : numCalls;
|
|
|
|
},
|
|
|
|
|
2014-08-22 07:26:00 +00:00
|
|
|
/**
|
|
|
|
* Get arbitrary one of active call.
|
|
|
|
*/
|
|
|
|
_getOneActiveCall: function(aClientId) {
|
|
|
|
for (let index in this._currentCalls[aClientId]) {
|
|
|
|
let call = this._currentCalls[aClientId][index];
|
|
|
|
if (call.state === nsITelephonyService.CALL_STATE_CONNECTED) {
|
|
|
|
return call;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
_addCdmaChildCall: function(aClientId, aNumber) {
|
2014-08-11 06:48:00 +00:00
|
|
|
let childCall = {
|
|
|
|
callIndex: CDMA_SECOND_CALL_INDEX,
|
|
|
|
state: RIL.CALL_STATE_DIALING,
|
|
|
|
number: aNumber,
|
|
|
|
isOutgoing: true,
|
|
|
|
isEmergency: false,
|
|
|
|
isConference: false,
|
|
|
|
isSwitchable: false,
|
|
|
|
isMergeable: true,
|
2015-02-03 04:18:20 +00:00
|
|
|
parentId: CDMA_FIRST_CALL_INDEX
|
2014-08-11 06:48:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
},
|
|
|
|
|
|
|
|
dial: function(aClientId, aNumber, aIsDialEmergency, aCallback) {
|
|
|
|
if (DEBUG) debug("Dialing " + (aIsDialEmergency ? "emergency " : "") + aNumber);
|
2014-02-12 06:31:13 +00:00
|
|
|
|
2014-09-22 05:36:00 +00:00
|
|
|
// We don't try to be too clever here, as the phone is probably in the
|
|
|
|
// locked state. Let's just check if it's a number without normalizing
|
|
|
|
if (!aIsDialEmergency) {
|
|
|
|
aNumber = gPhoneNumberUtils.normalize(aNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate the number.
|
|
|
|
// Note: isPlainPhoneNumber also accepts USSD and SS numbers
|
|
|
|
if (!gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
|
|
|
|
if (DEBUG) debug("Error: Number '" + aNumber + "' is not viable. Drop.");
|
2014-10-15 06:50:00 +00:00
|
|
|
aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
|
2014-09-22 05:36:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-11 08:27:45 +00:00
|
|
|
if (this._hasCalls(aClientId)) {
|
|
|
|
// 3GPP TS 22.030 6.5.5
|
|
|
|
// Handling of supplementary services within a call.
|
|
|
|
|
|
|
|
let mmiCallback = response => {
|
|
|
|
aCallback.notifyDialMMI(RIL.MMI_KS_SC_CALL);
|
|
|
|
if (!response.success) {
|
|
|
|
aCallback.notifyDialMMIError(RIL.MMI_ERROR_KS_ERROR);
|
|
|
|
} else {
|
|
|
|
aCallback.notifyDialMMISuccess(RIL.MMI_SM_KS_CALL_CONTROL);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (aNumber === "0") {
|
|
|
|
this._sendToRilWorker(aClientId, "hangUpBackground", null, mmiCallback);
|
|
|
|
} else if (aNumber === "1") {
|
|
|
|
this._sendToRilWorker(aClientId, "hangUpForeground", null, mmiCallback);
|
|
|
|
} else if (aNumber[0] === "1" && aNumber.length === 2) {
|
2014-12-24 00:06:00 +00:00
|
|
|
this._sendToRilWorker(aClientId, "hangUpCall",
|
2014-12-11 08:27:45 +00:00
|
|
|
{ callIndex: parseInt(aNumber[1]) }, mmiCallback);
|
|
|
|
} else if (aNumber === "2") {
|
|
|
|
this._sendToRilWorker(aClientId, "switchActiveCall", null, mmiCallback);
|
|
|
|
} else if (aNumber[0] === "2" && aNumber.length === 2) {
|
|
|
|
this._sendToRilWorker(aClientId, "separateCall",
|
|
|
|
{ callIndex: parseInt(aNumber[1]) }, mmiCallback);
|
|
|
|
} else if (aNumber === "3") {
|
|
|
|
this._sendToRilWorker(aClientId, "conferenceCall", null, mmiCallback);
|
|
|
|
} else {
|
|
|
|
// Entering "Directory Number"
|
|
|
|
this._dialCall(aClientId,
|
|
|
|
{ number: aNumber,
|
|
|
|
isDialEmergency: aIsDialEmergency }, aCallback);
|
2014-12-10 07:40:44 +00:00
|
|
|
}
|
2014-12-11 08:27:45 +00:00
|
|
|
} else {
|
2015-01-23 10:34:13 +00:00
|
|
|
let mmi = gDialNumberUtils.parseMMI(aNumber);
|
2014-12-11 08:27:45 +00:00
|
|
|
if (!mmi) {
|
|
|
|
this._dialCall(aClientId,
|
|
|
|
{ number: aNumber,
|
|
|
|
isDialEmergency: aIsDialEmergency }, aCallback);
|
|
|
|
} else if (this._isTemporaryCLIR(mmi)) {
|
|
|
|
this._dialCall(aClientId,
|
|
|
|
{ number: mmi.dialNumber,
|
|
|
|
clirMode: this._getTemporaryCLIRMode(mmi.procedure),
|
|
|
|
isDialEmergency: aIsDialEmergency }, aCallback);
|
|
|
|
} else {
|
|
|
|
// Reject MMI code from dialEmergency api.
|
|
|
|
if (aIsDialEmergency) {
|
|
|
|
aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
|
|
|
|
return;
|
|
|
|
}
|
2014-12-10 12:34:11 +00:00
|
|
|
|
2014-12-21 16:35:00 +00:00
|
|
|
this._dialMMI(aClientId, mmi, aCallback);
|
2014-12-11 08:27:45 +00:00
|
|
|
}
|
2014-09-22 05:36:00 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param aOptions.number
|
|
|
|
* @param aOptions.clirMode (optional)
|
|
|
|
* @param aOptions.isDialEmergency
|
|
|
|
*/
|
|
|
|
_dialCall: function(aClientId, aOptions, aCallback) {
|
|
|
|
if (this._isDialing) {
|
2014-07-02 07:47:00 +00:00
|
|
|
if (DEBUG) debug("Error: Already has a dialing call.");
|
2014-10-15 06:50:00 +00:00
|
|
|
aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
|
2014-04-01 12:58:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-14 07:55:00 +00:00
|
|
|
// We can only have at most two calls on the same line (client).
|
|
|
|
if (this._numCallsOnLine(aClientId) >= 2) {
|
|
|
|
if (DEBUG) debug("Error: Already has more than 2 calls on line.");
|
2014-10-15 06:50:00 +00:00
|
|
|
aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
|
2014-08-14 07:55:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-16 14:26:50 +00:00
|
|
|
// For DSDS, if there is aleady a call on SIM 'aClientId', we cannot place
|
|
|
|
// any new call on other SIM.
|
2014-07-02 07:47:00 +00:00
|
|
|
if (this._hasCallsOnOtherClient(aClientId)) {
|
|
|
|
if (DEBUG) debug("Error: Already has a call on other sim.");
|
2014-10-15 06:50:00 +00:00
|
|
|
aCallback.notifyError(DIAL_ERROR_OTHER_CONNECTION_IN_USE);
|
2014-02-27 06:12:30 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-23 10:34:13 +00:00
|
|
|
aOptions.isEmergency = gDialNumberUtils.isEmergency(aOptions.number);
|
2014-09-22 05:36:00 +00:00
|
|
|
if (aOptions.isEmergency) {
|
|
|
|
// Automatically select a proper clientId for emergency call.
|
|
|
|
aClientId = gRadioInterfaceLayer.getClientIdForEmergencyCall() ;
|
|
|
|
if (aClientId === -1) {
|
|
|
|
if (DEBUG) debug("Error: No client is avaialble for emergency call.");
|
2014-10-15 06:50:00 +00:00
|
|
|
aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
|
2014-09-22 05:36:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
2014-02-12 06:31:13 +00:00
|
|
|
|
2015-01-20 02:32:00 +00:00
|
|
|
// If there is no active call, we can dial a new call directly.
|
2014-09-22 05:36:00 +00:00
|
|
|
let activeCall = this._getOneActiveCall(aClientId);
|
|
|
|
if (!activeCall) {
|
|
|
|
this._sendDialCallRequest(aClientId, aOptions, aCallback);
|
2015-01-20 02:32:00 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-08-19 04:24:00 +00:00
|
|
|
|
2015-01-20 02:32:00 +00:00
|
|
|
// Otherwise, we should hold the active call before dialing another one.
|
|
|
|
if (DEBUG) debug("There is an active call. Hold it first before dial.");
|
2014-02-21 09:46:58 +00:00
|
|
|
|
2015-01-20 02:32:00 +00:00
|
|
|
if (this._cachedDialRequest) {
|
|
|
|
if (DEBUG) debug("Error: There already is a pending dial request.");
|
|
|
|
aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let autoHoldCallback = {
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyCallback]),
|
|
|
|
|
|
|
|
notifySuccess: () => {
|
|
|
|
this._cachedDialRequest = {
|
|
|
|
clientId: aClientId,
|
|
|
|
options: aOptions,
|
|
|
|
callback: aCallback
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
notifyError: (aErrorMsg) => {
|
|
|
|
if (DEBUG) debug("Error: Fail to automatically hold the active call.");
|
|
|
|
aCallback.notifyError(aErrorMsg);
|
2014-08-22 07:26:00 +00:00
|
|
|
}
|
2015-01-20 02:32:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if (activeCall.isConference) {
|
|
|
|
this.holdConference(aClientId, autoHoldCallback);
|
|
|
|
} else {
|
|
|
|
this.holdCall(aClientId, activeCall.callIndex, autoHoldCallback);
|
2014-09-22 05:36:00 +00:00
|
|
|
}
|
2014-08-19 04:24:00 +00:00
|
|
|
},
|
2014-08-14 07:55:00 +00:00
|
|
|
|
2014-09-22 05:36:00 +00:00
|
|
|
_sendDialCallRequest: function(aClientId, aOptions, aCallback) {
|
|
|
|
this._isDialing = true;
|
2014-08-14 07:55:00 +00:00
|
|
|
|
2014-08-19 04:24:00 +00:00
|
|
|
this._sendToRilWorker(aClientId, "dial", aOptions, response => {
|
2014-09-22 05:36:00 +00:00
|
|
|
this._isDialing = false;
|
2014-08-19 04:23:00 +00:00
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
if (!response.success) {
|
2014-10-15 06:50:00 +00:00
|
|
|
aCallback.notifyError(response.errorMsg);
|
2014-08-19 04:23:00 +00:00
|
|
|
return;
|
2014-02-21 09:46:58 +00:00
|
|
|
}
|
|
|
|
|
2014-08-19 04:23:00 +00:00
|
|
|
let currentCdmaCallIndex = !response.isCdma ? null :
|
|
|
|
Object.keys(this._currentCalls[aClientId])[0];
|
|
|
|
|
|
|
|
if (currentCdmaCallIndex == null) {
|
2014-12-23 07:46:10 +00:00
|
|
|
aCallback.notifyDialCallSuccess(aClientId, response.callIndex,
|
|
|
|
response.number);
|
2014-02-21 09:46:58 +00:00
|
|
|
} else {
|
2014-08-19 04:23:00 +00:00
|
|
|
// RIL doesn't hold the 2nd call. We create one by ourselves.
|
2014-12-23 07:46:10 +00:00
|
|
|
aCallback.notifyDialCallSuccess(aClientId, CDMA_SECOND_CALL_INDEX,
|
|
|
|
response.number);
|
2015-02-03 04:18:20 +00:00
|
|
|
this._addCdmaChildCall(aClientId, response.number);
|
2014-02-12 06:31:13 +00:00
|
|
|
}
|
2014-08-19 04:23:00 +00:00
|
|
|
});
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-09-22 05:36:00 +00:00
|
|
|
/**
|
2014-10-15 06:51:00 +00:00
|
|
|
* @param aClientId
|
|
|
|
* Client id.
|
2014-09-22 05:36:00 +00:00
|
|
|
* @param aMmi
|
|
|
|
* Parsed MMI structure.
|
2014-10-15 06:51:00 +00:00
|
|
|
* @param aCallback
|
|
|
|
* A nsITelephonyDialCallback object.
|
|
|
|
* @param aStartNewSession
|
|
|
|
* True to start a new session for ussd request.
|
2014-09-22 05:36:00 +00:00
|
|
|
*/
|
2014-12-21 16:35:00 +00:00
|
|
|
_dialMMI: function(aClientId, aMmi, aCallback) {
|
2014-09-22 05:36:00 +00:00
|
|
|
let mmiServiceCode = aMmi ?
|
|
|
|
this._serviceCodeToKeyString(aMmi.serviceCode) : RIL.MMI_KS_SC_USSD;
|
|
|
|
|
|
|
|
aCallback.notifyDialMMI(mmiServiceCode);
|
|
|
|
|
2014-10-15 06:51:00 +00:00
|
|
|
this._sendToRilWorker(aClientId, "sendMMI",
|
2014-12-21 16:35:00 +00:00
|
|
|
{ mmi: aMmi }, response => {
|
2014-09-22 05:36:00 +00:00
|
|
|
if (DEBUG) debug("MMI response: " + JSON.stringify(response));
|
|
|
|
|
|
|
|
if (!response.success) {
|
|
|
|
if (response.additionalInformation != null) {
|
|
|
|
aCallback.notifyDialMMIErrorWithInfo(response.errorMsg,
|
|
|
|
response.additionalInformation);
|
|
|
|
} else {
|
|
|
|
aCallback.notifyDialMMIError(response.errorMsg);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We expect to have an IMEI at this point if the request was supposed
|
|
|
|
// to query for the IMEI, so getting a successful reply from the RIL
|
|
|
|
// without containing an actual IMEI number is considered an error.
|
|
|
|
if (mmiServiceCode === RIL.MMI_KS_SC_IMEI &&
|
|
|
|
!response.statusMessage) {
|
|
|
|
aCallback.notifyDialMMIError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// MMI query call forwarding options request returns a set of rules that
|
|
|
|
// will be exposed in the form of an array of MozCallForwardingOptions
|
|
|
|
// instances.
|
|
|
|
if (mmiServiceCode === RIL.MMI_KS_SC_CALL_FORWARDING) {
|
|
|
|
if (response.isSetCallForward) {
|
|
|
|
gGonkMobileConnectionService.notifyCFStateChanged(aClientId,
|
|
|
|
response.action,
|
|
|
|
response.reason,
|
|
|
|
response.number,
|
|
|
|
response.timeSeconds,
|
|
|
|
response.serviceClass);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (response.additionalInformation != null) {
|
2014-11-06 02:20:54 +00:00
|
|
|
let callForwardingOptions =
|
2014-09-22 05:36:00 +00:00
|
|
|
this._rulesToCallForwardingOptions(response.additionalInformation);
|
2014-11-06 02:20:54 +00:00
|
|
|
aCallback.notifyDialMMISuccessWithCallForwardingOptions(
|
|
|
|
response.statusMessage, callForwardingOptions.length, callForwardingOptions);
|
|
|
|
return;
|
2014-09-22 05:36:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-06 02:20:54 +00:00
|
|
|
// No additional information
|
2014-12-11 08:27:45 +00:00
|
|
|
if (response.additionalInformation === undefined) {
|
2014-11-06 02:20:54 +00:00
|
|
|
aCallback.notifyDialMMISuccess(response.statusMessage);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Additional information is an integer.
|
|
|
|
if (!isNaN(parseInt(response.additionalInformation, 10))) {
|
|
|
|
aCallback.notifyDialMMISuccessWithInteger(
|
|
|
|
response.statusMessage, response.additionalInformation);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Additional information is an array of strings.
|
|
|
|
let array = response.additionalInformation;
|
|
|
|
if (Array.isArray(array) && array.length > 0 && typeof array[0] === "string") {
|
|
|
|
aCallback.notifyDialMMISuccessWithStrings(response.statusMessage,
|
|
|
|
array.length, array);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aCallback.notifyDialMMISuccess(response.statusMessage);
|
2014-09-22 05:36:00 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
_serviceCodeToKeyString: function(aServiceCode) {
|
|
|
|
switch (aServiceCode) {
|
|
|
|
case RIL.MMI_SC_CFU:
|
|
|
|
case RIL.MMI_SC_CF_BUSY:
|
|
|
|
case RIL.MMI_SC_CF_NO_REPLY:
|
|
|
|
case RIL.MMI_SC_CF_NOT_REACHABLE:
|
|
|
|
case RIL.MMI_SC_CF_ALL:
|
|
|
|
case RIL.MMI_SC_CF_ALL_CONDITIONAL:
|
|
|
|
return RIL.MMI_KS_SC_CALL_FORWARDING;
|
|
|
|
case RIL.MMI_SC_PIN:
|
|
|
|
return RIL.MMI_KS_SC_PIN;
|
|
|
|
case RIL.MMI_SC_PIN2:
|
|
|
|
return RIL.MMI_KS_SC_PIN2;
|
|
|
|
case RIL.MMI_SC_PUK:
|
|
|
|
return RIL.MMI_KS_SC_PUK;
|
|
|
|
case RIL.MMI_SC_PUK2:
|
|
|
|
return RIL.MMI_KS_SC_PUK2;
|
|
|
|
case RIL.MMI_SC_IMEI:
|
|
|
|
return RIL.MMI_KS_SC_IMEI;
|
|
|
|
case RIL.MMI_SC_CLIP:
|
|
|
|
return RIL.MMI_KS_SC_CLIP;
|
|
|
|
case RIL.MMI_SC_CLIR:
|
|
|
|
return RIL.MMI_KS_SC_CLIR;
|
|
|
|
case RIL.MMI_SC_BAOC:
|
|
|
|
case RIL.MMI_SC_BAOIC:
|
|
|
|
case RIL.MMI_SC_BAOICxH:
|
|
|
|
case RIL.MMI_SC_BAIC:
|
|
|
|
case RIL.MMI_SC_BAICr:
|
|
|
|
case RIL.MMI_SC_BA_ALL:
|
|
|
|
case RIL.MMI_SC_BA_MO:
|
|
|
|
case RIL.MMI_SC_BA_MT:
|
|
|
|
return RIL.MMI_KS_SC_CALL_BARRING;
|
|
|
|
case RIL.MMI_SC_CALL_WAITING:
|
|
|
|
return RIL.MMI_KS_SC_CALL_WAITING;
|
2014-12-22 08:31:05 +00:00
|
|
|
case RIL.MMI_SC_CHANGE_PASSWORD:
|
|
|
|
return RIL.MMI_KS_SC_CHANGE_PASSWORD;
|
2014-09-22 05:36:00 +00:00
|
|
|
default:
|
|
|
|
return RIL.MMI_KS_SC_USSD;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-12-24 00:06:00 +00:00
|
|
|
/**
|
|
|
|
* The default callback handler for call operations.
|
|
|
|
*
|
|
|
|
* @param aCallback
|
|
|
|
* An callback object including notifySuccess() and notifyError(aMsg)
|
|
|
|
* @param aResponse
|
|
|
|
* The response from ril_worker.
|
|
|
|
*/
|
|
|
|
_defaultCallbackHandler: function(aCallback, aResponse) {
|
|
|
|
if (!aResponse.success) {
|
|
|
|
aCallback.notifyError(aResponse.errorMsg);
|
2014-02-21 09:46:58 +00:00
|
|
|
} else {
|
2014-12-24 00:06:00 +00:00
|
|
|
aCallback.notifySuccess();
|
2014-02-21 09:46:58 +00:00
|
|
|
}
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2015-01-07 06:37:03 +00:00
|
|
|
sendTones: function(aClientId, aDtmfChars, aPauseDuration, aToneDuration,
|
|
|
|
aCallback) {
|
|
|
|
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
|
|
let tones = aDtmfChars;
|
|
|
|
let playTone = (tone) => {
|
|
|
|
this._sendToRilWorker(aClientId, "startTone", { dtmfChar: tone }, response => {
|
|
|
|
if (!response.success) {
|
|
|
|
aCallback.notifyError(response.errorMsg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
timer.initWithCallback(() => {
|
|
|
|
this.stopTone();
|
|
|
|
timer.initWithCallback(() => {
|
|
|
|
if (tones.length === 1) {
|
|
|
|
aCallback.notifySuccess();
|
|
|
|
} else {
|
|
|
|
tones = tones.substr(1);
|
|
|
|
playTone(tones[0]);
|
|
|
|
}
|
|
|
|
}, TONES_GAP_DURATION, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
|
|
}, aToneDuration, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
timer.initWithCallback(() => {
|
|
|
|
playTone(tones[0]);
|
|
|
|
}, aPauseDuration, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
startTone: function(aClientId, aDtmfChar) {
|
2014-08-19 04:23:00 +00:00
|
|
|
this._sendToRilWorker(aClientId, "startTone", { dtmfChar: aDtmfChar });
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
stopTone: function(aClientId) {
|
2014-08-19 04:23:00 +00:00
|
|
|
this._sendToRilWorker(aClientId, "stopTone");
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-12-24 00:06:00 +00:00
|
|
|
answerCall: function(aClientId, aCallIndex, aCallback) {
|
|
|
|
this._sendToRilWorker(aClientId, "answerCall", { callIndex: aCallIndex },
|
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
|
|
|
},
|
|
|
|
|
|
|
|
rejectCall: function(aClientId, aCallIndex, aCallback) {
|
|
|
|
this._sendToRilWorker(aClientId, "rejectCall", { callIndex: aCallIndex },
|
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-12-24 00:06:00 +00:00
|
|
|
hangUpCall: function(aClientId, aCallIndex, aCallback) {
|
2015-02-03 04:18:20 +00:00
|
|
|
// Should release both, child and parent, together. Since RIL holds only
|
|
|
|
// the parent call, we send 'parentId' to RIL.
|
|
|
|
aCallIndex = this._currentCalls[aClientId][aCallIndex].parentId || aCallIndex;
|
|
|
|
|
|
|
|
this._sendToRilWorker(aClientId, "hangUpCall", { callIndex: aCallIndex },
|
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
_switchCall: function(aClientId, aCallIndex, aCallback, aRequiredState) {
|
2014-02-21 09:46:58 +00:00
|
|
|
let call = this._currentCalls[aClientId][aCallIndex];
|
2015-02-03 04:18:20 +00:00
|
|
|
if (!call) {
|
2014-12-24 00:06:00 +00:00
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
2014-02-21 09:46:58 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
if (this._isCdmaClient(aClientId)) {
|
|
|
|
this._switchCallCdma(aClientId, aCallIndex, aCallback);
|
|
|
|
} else {
|
|
|
|
this._switchCallGsm(aClientId, aCallIndex, aCallback, aRequiredState);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_switchCallGsm: function(aClientId, aCallIndex, aCallback, aRequiredState) {
|
|
|
|
let call = this._currentCalls[aClientId][aCallIndex];
|
|
|
|
if (call.state != aRequiredState) {
|
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._sendToRilWorker(aClientId, "switchActiveCall", null,
|
2014-12-24 00:06:00 +00:00
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
_switchCallCdma: function(aClientId, aCallIndex, aCallback) {
|
2014-02-21 09:46:58 +00:00
|
|
|
let call = this._currentCalls[aClientId][aCallIndex];
|
2015-02-03 04:18:20 +00:00
|
|
|
if (!call.isSwitchable) {
|
2014-12-24 00:06:00 +00:00
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
2014-02-21 09:46:58 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
this._sendToRilWorker(aClientId, "cdmaFlash", null,
|
2014-12-24 00:06:00 +00:00
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
holdCall: function(aClientId, aCallIndex, aCallback) {
|
|
|
|
this._switchCall(aClientId, aCallIndex, aCallback,
|
|
|
|
nsITelephonyService.CALL_STATE_CONNECTED);
|
|
|
|
},
|
|
|
|
|
|
|
|
resumeCall: function(aClientId, aCallIndex, aCallback) {
|
|
|
|
this._switchCall(aClientId, aCallIndex, aCallback,
|
|
|
|
nsITelephonyService.CALL_STATE_HELD);
|
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
_conferenceCallGsm: function(aClientId, aCallback) {
|
|
|
|
this._sendToRilWorker(aClientId, "conferenceCall", null, response => {
|
|
|
|
if (!response.success) {
|
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
|
|
|
// TODO: Bug 1124993. Deprecate it. Use callback response is enough.
|
|
|
|
this._notifyAllListeners("notifyConferenceError",
|
|
|
|
["addError", response.errorMsg]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aCallback.notifySuccess();
|
|
|
|
});
|
|
|
|
},
|
2014-02-21 09:46:58 +00:00
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
_conferenceCallCdma: function(aClientId, aCallback) {
|
|
|
|
for (let index in this._currentCalls[aClientId]) {
|
|
|
|
let call = this._currentCalls[aClientId][index];
|
2014-02-21 09:46:58 +00:00
|
|
|
if (!call.isMergeable) {
|
2015-01-11 08:25:00 +00:00
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
2014-02-21 09:46:58 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
this._sendToRilWorker(aClientId, "cdmaFlash", null, response => {
|
2014-02-21 09:46:58 +00:00
|
|
|
if (!response.success) {
|
2015-01-11 08:25:00 +00:00
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
2015-02-03 04:18:20 +00:00
|
|
|
// TODO: Bug 1124993. Deprecate it. Use callback response is enough.
|
|
|
|
this._notifyAllListeners("notifyConferenceError",
|
|
|
|
["addError", response.errorMsg]);
|
2014-08-19 04:23:00 +00:00
|
|
|
return;
|
2014-02-21 09:46:58 +00:00
|
|
|
}
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
for (let index in this._currentCalls[aClientId]) {
|
|
|
|
let call = this._currentCalls[aClientId][index];
|
|
|
|
call.state = RIL.CALL_STATE_ACTIVE;
|
|
|
|
call.isConference = true;
|
|
|
|
this.notifyCallStateChanged(aClientId, call);
|
2014-02-21 09:46:58 +00:00
|
|
|
}
|
2015-02-03 04:18:20 +00:00
|
|
|
this.notifyConferenceCallStateChanged(RIL.CALL_STATE_ACTIVE);
|
2015-01-11 08:25:00 +00:00
|
|
|
|
|
|
|
aCallback.notifySuccess();
|
2014-08-19 04:23:00 +00:00
|
|
|
});
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
conferenceCall: function(aClientId, aCallback) {
|
|
|
|
if (Object.keys(this._currentCalls[aClientId]).length < 2) {
|
2015-01-11 08:25:00 +00:00
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
2014-02-21 09:46:58 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
if (this._isCdmaClient(aClientId)) {
|
|
|
|
this._conferenceCallCdma(aClientId, aCallback);
|
|
|
|
} else {
|
|
|
|
this._conferenceCallGsm(aClientId, aCallback);
|
2014-02-21 09:46:58 +00:00
|
|
|
}
|
2015-02-03 04:18:20 +00:00
|
|
|
},
|
2014-02-21 09:46:58 +00:00
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
_separateCallGsm: function(aClientId, aCallIndex, aCallback) {
|
2014-08-19 04:23:00 +00:00
|
|
|
this._sendToRilWorker(aClientId, "separateCall", { callIndex: aCallIndex },
|
|
|
|
response => {
|
2014-02-21 09:46:58 +00:00
|
|
|
if (!response.success) {
|
2015-01-11 08:25:00 +00:00
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
2015-02-03 04:18:20 +00:00
|
|
|
// TODO: Bug 1124993. Deprecate it. Use callback response is enough.
|
|
|
|
this._notifyAllListeners("notifyConferenceError",
|
|
|
|
["removeError", response.errorMsg]);
|
2014-08-19 04:23:00 +00:00
|
|
|
return;
|
2014-02-21 09:46:58 +00:00
|
|
|
}
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
aCallback.notifySuccess();
|
|
|
|
});
|
|
|
|
},
|
2015-01-11 08:25:00 +00:00
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
// See 3gpp2, S.R0006-522-A v1.0. Table 4, XID 6S.
|
|
|
|
// Release the third party. Optionally apply a warning tone. Connect the
|
|
|
|
// controlling subscriber and the second party. Go to the 2-way state.
|
|
|
|
_separateCallCdma: function(aClientId, aCallIndex, aCallback) {
|
|
|
|
this._sendToRilWorker(aClientId, "cdmaFlash", null, response => {
|
|
|
|
if (!response.success) {
|
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
|
|
|
// TODO: Bug 1124993. Deprecate it. Use callback response is enough.
|
|
|
|
this._notifyAllListeners("notifyConferenceError",
|
|
|
|
["removeError", response.errorMsg]);
|
|
|
|
return;
|
2014-02-21 09:46:58 +00:00
|
|
|
}
|
2015-01-11 08:25:00 +00:00
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
let childCall = this._currentCalls[aClientId][CDMA_SECOND_CALL_INDEX];
|
|
|
|
this.notifyCallDisconnected(aClientId, childCall);
|
|
|
|
|
2015-01-11 08:25:00 +00:00
|
|
|
aCallback.notifySuccess();
|
2014-08-19 04:23:00 +00:00
|
|
|
});
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
separateCall: function(aClientId, aCallIndex, aCallback) {
|
|
|
|
let call = this._currentCalls[aClientId][aCallIndex];
|
|
|
|
if (!call || !call.isConference) {
|
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._isCdmaClient(aClientId)) {
|
|
|
|
this._separateCallCdma(aClientId, aCallIndex, aCallback);
|
|
|
|
} else {
|
|
|
|
this._separateCallGsm(aClientId, aCallIndex, aCallback);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-10-20 07:13:00 +00:00
|
|
|
hangUpConference: function(aClientId, aCallback) {
|
2015-02-03 04:18:20 +00:00
|
|
|
// In cdma, ril only maintains one call index.
|
|
|
|
if (this._isCdmaClient(aClientId)) {
|
|
|
|
this._sendToRilWorker(aClientId, "hangUpCall",
|
|
|
|
{ callIndex: CDMA_FIRST_CALL_INDEX },
|
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let foreground = this._currentConferenceState == nsITelephonyService.CALL_STATE_CONNECTED;
|
|
|
|
this._sendToRilWorker(aClientId,
|
|
|
|
foreground ? "hangUpForeground" : "hangUpBackground",
|
|
|
|
null,
|
2015-01-11 08:25:00 +00:00
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
2014-10-20 07:13:00 +00:00
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
_switchConference: function(aClientId, aCallback) {
|
|
|
|
// Cannot hold/resume a conference in cdma.
|
|
|
|
if (this._isCdmaClient(aClientId)) {
|
|
|
|
aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._sendToRilWorker(aClientId, "switchActiveCall", null,
|
2015-01-11 08:25:00 +00:00
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2015-02-03 04:18:20 +00:00
|
|
|
holdConference: function(aClientId, aCallback) {
|
|
|
|
this._switchConference(aClientId, aCallback);
|
|
|
|
},
|
|
|
|
|
2015-01-11 08:25:00 +00:00
|
|
|
resumeConference: function(aClientId, aCallback) {
|
2015-02-03 04:18:20 +00:00
|
|
|
this._switchConference(aClientId, aCallback);
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-10-15 06:51:00 +00:00
|
|
|
sendUSSD: function(aClientId, aUssd, aCallback) {
|
2015-02-03 04:18:19 +00:00
|
|
|
this._sendToRilWorker(aClientId, "sendUSSD", { ussd: aUssd },
|
2015-02-03 04:18:20 +00:00
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
2014-10-15 06:51:00 +00:00
|
|
|
},
|
|
|
|
|
2015-01-07 07:28:44 +00:00
|
|
|
cancelUSSD: function(aClientId, aCallback) {
|
2015-02-03 04:18:20 +00:00
|
|
|
this._sendToRilWorker(aClientId, "cancelUSSD", {},
|
|
|
|
this._defaultCallbackHandler.bind(this, aCallback));
|
2015-01-07 07:28:44 +00:00
|
|
|
},
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
get microphoneMuted() {
|
2014-11-14 07:20:44 +00:00
|
|
|
return gAudioService.microphoneMuted;
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
set microphoneMuted(aMuted) {
|
2014-11-14 07:20:44 +00:00
|
|
|
gAudioService.microphoneMuted = aMuted;
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
get speakerEnabled() {
|
2014-11-14 07:20:44 +00:00
|
|
|
return gAudioService.speakerEnabled;
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
set speakerEnabled(aEnabled) {
|
2014-11-14 07:20:44 +00:00
|
|
|
gAudioService.speakerEnabled = aEnabled;
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2014-06-03 14:15:25 +00:00
|
|
|
* nsIGonkTelephonyService interface.
|
2013-09-07 06:19:57 +00:00
|
|
|
*/
|
|
|
|
|
2014-10-16 21:40:00 +00:00
|
|
|
notifyAudioStateChanged: function(aClientId, aState) {
|
|
|
|
this._audioStates[aClientId] = aState;
|
|
|
|
|
|
|
|
let audioState = aState;
|
|
|
|
for (let i = 0; i < this._numClients; ++i) {
|
|
|
|
audioState = Math.max(audioState, this._audioStates[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._updateAudioState(audioState);
|
|
|
|
},
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
/**
|
|
|
|
* Handle call disconnects by updating our current state and the audio system.
|
|
|
|
*/
|
2014-01-13 02:44:40 +00:00
|
|
|
notifyCallDisconnected: function(aClientId, aCall) {
|
2013-09-07 06:19:57 +00:00
|
|
|
if (DEBUG) debug("handleCallDisconnected: " + JSON.stringify(aCall));
|
|
|
|
|
2014-06-09 06:47:00 +00:00
|
|
|
aCall.clientId = aClientId;
|
2014-06-03 14:15:25 +00:00
|
|
|
aCall.state = nsITelephonyService.CALL_STATE_DISCONNECTED;
|
2015-01-23 10:34:13 +00:00
|
|
|
aCall.isEmergency = gDialNumberUtils.isEmergency(aCall.number);
|
2013-09-07 06:19:57 +00:00
|
|
|
let duration = ("started" in aCall && typeof aCall.started == "number") ?
|
|
|
|
new Date().getTime() - aCall.started : 0;
|
2014-07-17 11:18:28 +00:00
|
|
|
|
2014-10-20 08:50:57 +00:00
|
|
|
gTelephonyMessenger.notifyCallEnded(aClientId,
|
|
|
|
aCall.number,
|
|
|
|
this._cdmaCallWaitingNumber,
|
|
|
|
aCall.isEmergency,
|
|
|
|
duration,
|
|
|
|
aCall.isOutgoing,
|
|
|
|
aCall.hangUpLocal);
|
2014-07-17 11:18:28 +00:00
|
|
|
|
2014-10-20 08:50:57 +00:00
|
|
|
// Clear cache of this._cdmaCallWaitingNumber after call disconnected.
|
|
|
|
this._cdmaCallWaitingNumber = null;
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-02 10:51:54 +00:00
|
|
|
if (!aCall.failCause ||
|
|
|
|
aCall.failCause === RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) {
|
2015-01-08 08:33:51 +00:00
|
|
|
let callInfo = new TelephonyCallInfo(aCall);
|
|
|
|
this._notifyAllListeners("callStateChanged", [callInfo]);
|
2014-02-21 09:46:58 +00:00
|
|
|
} else {
|
2014-04-22 09:55:06 +00:00
|
|
|
this._notifyAllListeners("notifyError",
|
|
|
|
[aClientId, aCall.callIndex, aCall.failCause]);
|
2013-12-02 10:51:54 +00:00
|
|
|
}
|
2014-02-21 09:46:58 +00:00
|
|
|
delete this._currentCalls[aClientId][aCall.callIndex];
|
2013-12-02 10:51:54 +00:00
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
if (manualConfStateChange) {
|
|
|
|
this.notifyConferenceCallStateChanged(RIL.CALL_STATE_UNKNOWN);
|
|
|
|
}
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle an incoming call.
|
|
|
|
*
|
|
|
|
* Not much is known about this call at this point, but it's enough
|
|
|
|
* to start bringing up the Phone app already.
|
|
|
|
*/
|
2014-01-13 02:44:40 +00:00
|
|
|
notifyCallRing: function() {
|
2013-10-28 06:07:28 +00:00
|
|
|
// We need to acquire a CPU wake lock to avoid the system falling into
|
|
|
|
// the sleep mode when the RIL handles the incoming call.
|
|
|
|
this._acquireCallRingWakeLock();
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2014-10-20 08:50:57 +00:00
|
|
|
gTelephonyMessenger.notifyNewCall();
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle call state changes by updating our current state and the audio
|
|
|
|
* system.
|
|
|
|
*/
|
2014-02-21 09:46:58 +00:00
|
|
|
notifyCallStateChanged: function(aClientId, aCall, aSkipStateConversion) {
|
2013-09-07 06:19:57 +00:00
|
|
|
if (DEBUG) debug("handleCallStateChange: " + JSON.stringify(aCall));
|
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
if (!aSkipStateConversion) {
|
|
|
|
aCall.state = this._convertRILCallState(aCall.state);
|
|
|
|
}
|
|
|
|
|
2014-06-03 14:15:25 +00:00
|
|
|
if (aCall.state == nsITelephonyService.CALL_STATE_DIALING) {
|
2014-10-20 08:50:57 +00:00
|
|
|
gTelephonyMessenger.notifyNewCall();
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
|
|
|
|
2013-12-06 08:02:28 +00:00
|
|
|
aCall.clientId = aClientId;
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2014-08-11 06:48:00 +00:00
|
|
|
function pick(arg, defaultValue) {
|
|
|
|
return typeof arg !== 'undefined' ? arg : defaultValue;
|
|
|
|
}
|
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
let call = this._currentCalls[aClientId][aCall.callIndex];
|
|
|
|
if (call) {
|
|
|
|
call.state = aCall.state;
|
2014-10-27 07:41:27 +00:00
|
|
|
call.number = aCall.number;
|
2014-02-21 09:46:58 +00:00
|
|
|
call.isConference = aCall.isConference;
|
2015-01-23 10:34:13 +00:00
|
|
|
call.isEmergency = gDialNumberUtils.isEmergency(aCall.number);
|
2014-08-11 06:48:00 +00:00
|
|
|
call.isSwitchable = pick(aCall.isSwitchable, call.isSwitchable);
|
|
|
|
call.isMergeable = pick(aCall.isMergeable, call.isMergeable);
|
2014-02-21 09:46:58 +00:00
|
|
|
} else {
|
|
|
|
call = aCall;
|
2015-01-23 10:34:13 +00:00
|
|
|
call.isEmergency = pick(aCall.isEmergency, gDialNumberUtils.isEmergency(aCall.number));
|
2014-08-11 06:48:00 +00:00
|
|
|
call.isSwitchable = pick(aCall.isSwitchable, true);
|
|
|
|
call.isMergeable = pick(aCall.isMergeable, true);
|
|
|
|
call.name = pick(aCall.name, "");
|
|
|
|
call.numberPresentaation = pick(aCall.numberPresentation, nsITelephonyService.CALL_PRESENTATION_ALLOWED);
|
|
|
|
call.namePresentaation = pick(aCall.namePresentation, nsITelephonyService.CALL_PRESENTATION_ALLOWED);
|
2014-04-16 11:43:08 +00:00
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
this._currentCalls[aClientId][aCall.callIndex] = call;
|
|
|
|
}
|
|
|
|
|
2014-08-22 07:26:00 +00:00
|
|
|
// Handle cached dial request.
|
2014-12-14 11:46:48 +00:00
|
|
|
if (this._cachedDialRequest && !this._getOneActiveCall(aClientId)) {
|
2014-08-22 07:26:00 +00:00
|
|
|
if (DEBUG) debug("All calls held. Perform the cached dial request.");
|
|
|
|
|
2014-09-22 05:36:00 +00:00
|
|
|
let request = this._cachedDialRequest;
|
|
|
|
this._sendDialCallRequest(request.clientId, request.options, request.callback);
|
|
|
|
this._cachedDialRequest = null;
|
2014-08-22 07:26:00 +00:00
|
|
|
}
|
|
|
|
|
2015-01-08 08:33:51 +00:00
|
|
|
let callInfo = new TelephonyCallInfo(call);
|
|
|
|
this._notifyAllListeners("callStateChanged", [callInfo]);
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-04-16 11:43:08 +00:00
|
|
|
notifyCdmaCallWaiting: function(aClientId, aCall) {
|
2013-10-28 06:07:28 +00:00
|
|
|
// We need to acquire a CPU wake lock to avoid the system falling into
|
|
|
|
// the sleep mode when the RIL handles the incoming call.
|
|
|
|
this._acquireCallRingWakeLock();
|
|
|
|
|
2014-02-21 09:46:58 +00:00
|
|
|
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);
|
|
|
|
}
|
2014-07-17 11:18:28 +00:00
|
|
|
|
|
|
|
this._cdmaCallWaitingNumber = aCall.number;
|
|
|
|
|
2014-04-16 11:43:08 +00:00
|
|
|
this._notifyAllListeners("notifyCdmaCallWaiting", [aClientId,
|
|
|
|
aCall.number,
|
|
|
|
aCall.numberPresentation,
|
|
|
|
aCall.name,
|
|
|
|
aCall.namePresentation]);
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
notifySupplementaryService: function(aClientId, aCallIndex, aNotification) {
|
2013-09-07 06:19:57 +00:00
|
|
|
let notification = this._convertRILSuppSvcNotification(aNotification);
|
|
|
|
this._notifyAllListeners("supplementaryServiceNotification",
|
2013-10-31 12:05:51 +00:00
|
|
|
[aClientId, aCallIndex, notification]);
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
notifyConferenceCallStateChanged: function(aState) {
|
2013-09-07 06:19:57 +00:00
|
|
|
if (DEBUG) debug("handleConferenceCallStateChanged: " + aState);
|
2015-02-03 04:18:20 +00:00
|
|
|
this._currentConferenceState = this._convertRILCallState(aState);
|
|
|
|
this._notifyAllListeners("conferenceCallStateChanged",
|
|
|
|
[this._currentConferenceState]);
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-10-15 06:51:00 +00:00
|
|
|
notifyUssdReceived: function(aClientId, aMessage, aSessionEnded) {
|
|
|
|
if (DEBUG) {
|
|
|
|
debug("notifyUssdReceived for " + aClientId + ": " +
|
|
|
|
aMessage + " (sessionEnded : " + aSessionEnded + ")");
|
|
|
|
}
|
|
|
|
|
2014-12-21 16:35:00 +00:00
|
|
|
gTelephonyMessenger.notifyUssdReceived(aClientId, aMessage, aSessionEnded);
|
2014-09-22 05:36:00 +00:00
|
|
|
},
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
/**
|
|
|
|
* nsIObserver interface.
|
|
|
|
*/
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
observe: function(aSubject, aTopic, aData) {
|
2013-09-07 06:19:57 +00:00
|
|
|
switch (aTopic) {
|
2013-10-24 08:14:59 +00:00
|
|
|
case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
|
|
|
|
if (aData === kPrefRilDebuggingEnabled) {
|
2013-09-07 06:19:57 +00:00
|
|
|
this._updateDebugFlag();
|
2014-02-21 09:46:58 +00:00
|
|
|
} else if (aData === kPrefDefaultServiceId) {
|
2013-10-24 08:15:06 +00:00
|
|
|
this.defaultServiceId = this._getDefaultServiceId();
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2013-10-24 08:14:59 +00:00
|
|
|
case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
|
2013-10-28 06:07:28 +00:00
|
|
|
// Release the CPU wake lock for handling the incoming call.
|
|
|
|
this._releaseCallRingWakeLock();
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2013-10-24 08:14:59 +00:00
|
|
|
Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
2013-09-07 06:19:57 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-10-15 06:51:00 +00:00
|
|
|
/**
|
|
|
|
* This implements nsISystemMessagesWrapper.wrapMessage(), which provides a
|
|
|
|
* plugable way to wrap a "ussd-received" type system message.
|
|
|
|
*
|
|
|
|
* Please see SystemMessageManager.js to know how it customizes the wrapper.
|
|
|
|
*/
|
|
|
|
function USSDReceivedWrapper() {
|
|
|
|
if (DEBUG) debug("USSDReceivedWrapper()");
|
|
|
|
}
|
|
|
|
USSDReceivedWrapper.prototype = {
|
|
|
|
// nsISystemMessagesWrapper implementation.
|
|
|
|
wrapMessage: function(aMessage, aWindow) {
|
|
|
|
if (DEBUG) debug("wrapMessage: " + JSON.stringify(aMessage));
|
|
|
|
|
|
|
|
let session = aMessage.sessionEnded ? null :
|
|
|
|
new aWindow.USSDSession(aMessage.serviceId);
|
|
|
|
|
|
|
|
let event = new aWindow.USSDReceivedEvent("ussdreceived", {
|
|
|
|
serviceId: aMessage.serviceId,
|
|
|
|
message: aMessage.message,
|
|
|
|
session: session
|
|
|
|
});
|
|
|
|
|
|
|
|
return event;
|
|
|
|
},
|
|
|
|
|
|
|
|
classDescription: "USSDReceivedWrapper",
|
|
|
|
classID: Components.ID("{d03684ed-ede4-4210-8206-f4f32772d9f5}"),
|
|
|
|
contractID: "@mozilla.org/dom/system-messages/wrapper/ussd-received;1",
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesWrapper])
|
|
|
|
};
|
|
|
|
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyService,
|
|
|
|
USSDReceivedWrapper]);
|