2013-09-07 06:19:57 +00:00
|
|
|
/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* 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
|
|
|
|
|
|
|
var RIL = {};
|
|
|
|
Cu.import("resource://gre/modules/ril_consts.js", RIL);
|
|
|
|
|
|
|
|
const GONK_TELEPHONYPROVIDER_CONTRACTID =
|
|
|
|
"@mozilla.org/telephony/gonktelephonyprovider;1";
|
|
|
|
const GONK_TELEPHONYPROVIDER_CID =
|
|
|
|
Components.ID("{67d26434-d063-4d28-9f48-5b3189788155}");
|
|
|
|
|
2013-10-24 08:14:59 +00:00
|
|
|
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
const nsIAudioManager = Ci.nsIAudioManager;
|
|
|
|
const nsITelephonyProvider = Ci.nsITelephonyProvider;
|
|
|
|
|
|
|
|
const CALL_WAKELOCK_TIMEOUT = 5000;
|
|
|
|
|
|
|
|
let DEBUG;
|
|
|
|
function debug(s) {
|
|
|
|
dump("TelephonyProvider: " + s + "\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "gAudioManager", function getAudioManager() {
|
|
|
|
try {
|
|
|
|
return Cc["@mozilla.org/telephony/audiomanager;1"]
|
|
|
|
.getService(nsIAudioManager);
|
|
|
|
} catch (ex) {
|
|
|
|
//TODO on the phone this should not fall back as silently.
|
|
|
|
|
|
|
|
// Fake nsIAudioManager implementation so that we can run the telephony
|
|
|
|
// code in a non-Gonk build.
|
|
|
|
if (DEBUG) debug("Using fake audio manager.");
|
|
|
|
return {
|
|
|
|
microphoneMuted: false,
|
|
|
|
masterVolume: 1.0,
|
|
|
|
masterMuted: false,
|
|
|
|
phoneState: nsIAudioManager.PHONE_STATE_CURRENT,
|
|
|
|
_forceForUse: {},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
setForceForUse: function(usage, force) {
|
2013-09-07 06:19:57 +00:00
|
|
|
this._forceForUse[usage] = force;
|
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
getForceForUse: function(usage) {
|
2013-09-07 06:19:57 +00:00
|
|
|
return this._forceForUse[usage] || nsIAudioManager.FORCE_NONE;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
|
|
|
|
"@mozilla.org/system-message-internal;1",
|
|
|
|
"nsISystemMessagesInternal");
|
|
|
|
|
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;
|
|
|
|
});
|
|
|
|
|
2013-12-06 08:02:28 +00:00
|
|
|
function SingleCall(options){
|
|
|
|
this.clientId = options.clientId;
|
|
|
|
this.callIndex = options.callIndex;
|
|
|
|
this.state = options.state;
|
|
|
|
this.number = options.number;
|
|
|
|
this.isOutgoing = options.isOutgoing;
|
|
|
|
this.isEmergency = options.isEmergency;
|
|
|
|
this.isConference = options.isConference;
|
|
|
|
}
|
|
|
|
SingleCall.prototype = {
|
|
|
|
clientId: null,
|
|
|
|
callIndex: null,
|
|
|
|
state: null,
|
|
|
|
number: null,
|
|
|
|
isOutgoing: false,
|
|
|
|
isEmergency: false,
|
|
|
|
isConference: false
|
|
|
|
};
|
|
|
|
|
|
|
|
function ConferenceCall(state){
|
|
|
|
this.state = state;
|
|
|
|
}
|
|
|
|
ConferenceCall.prototype = {
|
|
|
|
state: null
|
|
|
|
};
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
function TelephonyProvider() {
|
2013-10-31 12:05:51 +00:00
|
|
|
this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
|
2013-09-07 06:19:57 +00:00
|
|
|
this._listeners = [];
|
|
|
|
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);
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
|
|
|
TelephonyProvider.prototype = {
|
|
|
|
classID: GONK_TELEPHONYPROVIDER_CID,
|
|
|
|
classInfo: XPCOMUtils.generateCI({classID: GONK_TELEPHONYPROVIDER_CID,
|
|
|
|
contractID: GONK_TELEPHONYPROVIDER_CONTRACTID,
|
|
|
|
classDescription: "TelephonyProvider",
|
|
|
|
interfaces: [Ci.nsITelephonyProvider,
|
|
|
|
Ci.nsIGonkTelephonyProvider],
|
|
|
|
flags: Ci.nsIClassInfo.SINGLETON}),
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyProvider,
|
|
|
|
Ci.nsIGonkTelephonyProvider,
|
|
|
|
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);
|
|
|
|
},
|
|
|
|
|
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-01-13 02:44:40 +00:00
|
|
|
_matchActiveSingleCall: function(aCall) {
|
2013-12-06 08:02:28 +00:00
|
|
|
return this._activeCall &&
|
|
|
|
this._activeCall instanceof SingleCall &&
|
|
|
|
this._activeCall.clientId === aCall.clientId &&
|
|
|
|
this._activeCall.callIndex === aCall.callIndex;
|
2013-10-31 12:05:51 +00:00
|
|
|
},
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
/**
|
|
|
|
* Track the active call and update the audio system as its state changes.
|
|
|
|
*/
|
|
|
|
_activeCall: null,
|
2014-01-13 02:44:44 +00:00
|
|
|
_updateCallAudioState: function(aCall, aConferenceState) {
|
2013-09-07 06:19:57 +00:00
|
|
|
if (aConferenceState === nsITelephonyProvider.CALL_STATE_CONNECTED) {
|
2013-12-06 08:02:28 +00:00
|
|
|
this._activeCall = new ConferenceCall(aConferenceState);
|
2013-09-07 06:19:57 +00:00
|
|
|
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
|
|
|
|
if (this.speakerEnabled) {
|
|
|
|
gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
|
|
|
|
nsIAudioManager.FORCE_SPEAKER);
|
|
|
|
}
|
2013-12-06 08:02:28 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
debug("Active call, put audio system into PHONE_STATE_IN_CALL: " +
|
|
|
|
gAudioManager.phoneState);
|
|
|
|
}
|
2013-09-07 06:19:57 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-12-06 08:02:28 +00:00
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
if (aConferenceState === nsITelephonyProvider.CALL_STATE_UNKNOWN ||
|
|
|
|
aConferenceState === nsITelephonyProvider.CALL_STATE_HELD) {
|
2013-12-06 08:02:28 +00:00
|
|
|
if (this._activeCall instanceof ConferenceCall) {
|
|
|
|
this._activeCall = null;
|
2013-09-07 06:19:57 +00:00
|
|
|
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
|
2013-12-06 08:02:28 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
debug("No active call, put audio system into PHONE_STATE_NORMAL: " +
|
|
|
|
gAudioManager.phoneState);
|
|
|
|
}
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aCall) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aCall.isConference) {
|
2013-12-06 08:02:28 +00:00
|
|
|
if (this._matchActiveSingleCall(aCall)) {
|
2013-09-07 06:19:57 +00:00
|
|
|
this._activeCall = null;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (aCall.state) {
|
|
|
|
case nsITelephonyProvider.CALL_STATE_DIALING: // Fall through...
|
|
|
|
case nsITelephonyProvider.CALL_STATE_ALERTING:
|
|
|
|
case nsITelephonyProvider.CALL_STATE_CONNECTED:
|
|
|
|
aCall.isActive = true;
|
2013-12-06 08:02:28 +00:00
|
|
|
this._activeCall = new SingleCall(aCall);
|
2013-09-07 06:19:57 +00:00
|
|
|
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
|
|
|
|
if (this.speakerEnabled) {
|
|
|
|
gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
|
|
|
|
nsIAudioManager.FORCE_SPEAKER);
|
|
|
|
}
|
|
|
|
if (DEBUG) {
|
|
|
|
debug("Active call, put audio system into PHONE_STATE_IN_CALL: " +
|
|
|
|
gAudioManager.phoneState);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nsITelephonyProvider.CALL_STATE_INCOMING:
|
|
|
|
aCall.isActive = false;
|
|
|
|
if (!this._activeCall) {
|
|
|
|
// We can change the phone state into RINGTONE only when there's
|
|
|
|
// no active call.
|
|
|
|
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE;
|
|
|
|
if (DEBUG) {
|
|
|
|
debug("Incoming call, put audio system into PHONE_STATE_RINGTONE: " +
|
|
|
|
gAudioManager.phoneState);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case nsITelephonyProvider.CALL_STATE_HELD: // Fall through...
|
|
|
|
case nsITelephonyProvider.CALL_STATE_DISCONNECTED:
|
|
|
|
aCall.isActive = false;
|
2013-12-06 08:02:28 +00:00
|
|
|
if (this._matchActiveSingleCall(aCall)) {
|
2013-09-07 06:19:57 +00:00
|
|
|
// Previously active call is not active now.
|
|
|
|
this._activeCall = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this._activeCall) {
|
|
|
|
// No active call. Disable the audio.
|
|
|
|
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
|
|
|
|
if (DEBUG) {
|
|
|
|
debug("No active call, put audio system into PHONE_STATE_NORMAL: " +
|
|
|
|
gAudioManager.phoneState);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
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:
|
|
|
|
return nsITelephonyProvider.CALL_STATE_UNKNOWN;
|
2013-09-07 06:19:57 +00:00
|
|
|
case RIL.CALL_STATE_ACTIVE:
|
|
|
|
return nsITelephonyProvider.CALL_STATE_CONNECTED;
|
|
|
|
case RIL.CALL_STATE_HOLDING:
|
|
|
|
return nsITelephonyProvider.CALL_STATE_HELD;
|
|
|
|
case RIL.CALL_STATE_DIALING:
|
|
|
|
return nsITelephonyProvider.CALL_STATE_DIALING;
|
|
|
|
case RIL.CALL_STATE_ALERTING:
|
|
|
|
return nsITelephonyProvider.CALL_STATE_ALERTING;
|
|
|
|
case RIL.CALL_STATE_INCOMING:
|
|
|
|
case RIL.CALL_STATE_WAITING:
|
|
|
|
return nsITelephonyProvider.CALL_STATE_INCOMING;
|
|
|
|
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:
|
|
|
|
return nsITelephonyProvider.NOTIFICATION_REMOTE_HELD;
|
|
|
|
case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED:
|
|
|
|
return nsITelephonyProvider.NOTIFICATION_REMOTE_RESUMED;
|
|
|
|
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-01-13 02:44:40 +00:00
|
|
|
_validateNumber: function(aNumber) {
|
2013-09-07 06:19:57 +00:00
|
|
|
// note: isPlainPhoneNumber also accepts USSD and SS numbers
|
|
|
|
if (gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
let errorMsg = RIL.RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[RIL.CALL_FAIL_UNOBTAINABLE_NUMBER];
|
|
|
|
let currentThread = Services.tm.currentThread;
|
|
|
|
currentThread.dispatch(this.notifyCallError.bind(this, -1, errorMsg),
|
|
|
|
Ci.nsIThread.DISPATCH_NORMAL);
|
|
|
|
if (DEBUG) {
|
|
|
|
debug("Number '" + aNumber + "' doesn't seem to be a viable number. Drop.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
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;
|
|
|
|
},
|
|
|
|
|
2013-09-07 06:19:57 +00:00
|
|
|
/**
|
|
|
|
* nsITelephonyProvider interface.
|
|
|
|
*/
|
|
|
|
|
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-01-13 02:44:44 +00:00
|
|
|
_enumerateCallsForClient: function(aClientId, aListener) {
|
2013-10-31 12:05:51 +00:00
|
|
|
if (DEBUG) debug("Enumeration of calls for client " + aClientId);
|
|
|
|
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
|
|
|
|
this._getClient(aClientId).sendWorkerMessage("enumerateCalls", null,
|
|
|
|
(function(response) {
|
2013-09-07 06:19:57 +00:00
|
|
|
for (let call of response.calls) {
|
2013-11-19 07:39:40 +00:00
|
|
|
call.clientId = aClientId;
|
2013-09-07 06:19:57 +00:00
|
|
|
call.state = this._convertRILCallState(call.state);
|
2013-12-06 08:02:28 +00:00
|
|
|
call.isActive = this._matchActiveSingleCall(call);
|
2013-09-07 06:19:57 +00:00
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
aListener.enumerateCallState(call.clientId, call.callIndex,
|
|
|
|
call.state, call.number,
|
2013-09-07 06:19:57 +00:00
|
|
|
call.isActive, call.isOutgoing,
|
|
|
|
call.isEmergency, call.isConference);
|
|
|
|
}
|
2013-10-31 12:05:51 +00:00
|
|
|
deferred.resolve();
|
2013-09-07 06:19:57 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}).bind(this));
|
2013-10-31 12:05:51 +00:00
|
|
|
|
|
|
|
return deferred.promise;
|
|
|
|
},
|
|
|
|
|
|
|
|
enumerateCalls: function(aListener) {
|
|
|
|
if (DEBUG) debug("Requesting enumeration of calls for callback");
|
|
|
|
|
|
|
|
let promise = Promise.resolve();
|
|
|
|
for (let i = 0; i < this._numClients; ++i) {
|
|
|
|
promise = promise.then(this._enumerateCallsForClient.bind(this, i, aListener));
|
|
|
|
}
|
|
|
|
promise.then(function() {
|
|
|
|
aListener.enumerateCallStateComplete();
|
|
|
|
});
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
dial: function(aClientId, aNumber, aIsEmergency) {
|
2013-09-07 06:19:57 +00:00
|
|
|
if (DEBUG) debug("Dialing " + (aIsEmergency ? "emergency " : "") + aNumber);
|
|
|
|
// 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 (!aIsEmergency) {
|
|
|
|
aNumber = gPhoneNumberUtils.normalize(aNumber);
|
|
|
|
}
|
|
|
|
if (this._validateNumber(aNumber)) {
|
2013-10-31 12:05:51 +00:00
|
|
|
this._getClient(aClientId).sendWorkerMessage("dial", {
|
|
|
|
number: aNumber,
|
|
|
|
isDialEmergency: aIsEmergency
|
|
|
|
});
|
2013-09-07 06:19:57 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
hangUp: function(aClientId, aCallIndex) {
|
|
|
|
this._getClient(aClientId).sendWorkerMessage("hangUp", { callIndex: aCallIndex });
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
startTone: function(aClientId, aDtmfChar) {
|
|
|
|
this._getClient(aClientId).sendWorkerMessage("startTone", { dtmfChar: aDtmfChar });
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
stopTone: function(aClientId) {
|
|
|
|
this._getClient(aClientId).sendWorkerMessage("stopTone");
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
answerCall: function(aClientId, aCallIndex) {
|
|
|
|
this._getClient(aClientId).sendWorkerMessage("answerCall", { callIndex: aCallIndex });
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
rejectCall: function(aClientId, aCallIndex) {
|
|
|
|
this._getClient(aClientId).sendWorkerMessage("rejectCall", { callIndex: aCallIndex });
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
holdCall: function(aClientId, aCallIndex) {
|
|
|
|
this._getClient(aClientId).sendWorkerMessage("holdCall", { callIndex: aCallIndex });
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
resumeCall: function(aClientId, aCallIndex) {
|
|
|
|
this._getClient(aClientId).sendWorkerMessage("resumeCall", { callIndex: aCallIndex });
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
conferenceCall: function(aClientId) {
|
2013-10-31 12:05:51 +00:00
|
|
|
this._getClient(aClientId).sendWorkerMessage("conferenceCall");
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
separateCall: function(aClientId, aCallIndex) {
|
2013-10-31 12:05:51 +00:00
|
|
|
this._getClient(aClientId).sendWorkerMessage("separateCall", { callIndex: aCallIndex });
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
holdConference: function(aClientId) {
|
2013-10-31 12:05:51 +00:00
|
|
|
this._getClient(aClientId).sendWorkerMessage("holdConference");
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
resumeConference: function(aClientId) {
|
2013-10-31 12:05:51 +00:00
|
|
|
this._getClient(aClientId).sendWorkerMessage("resumeConference");
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
get microphoneMuted() {
|
|
|
|
return gAudioManager.microphoneMuted;
|
|
|
|
},
|
|
|
|
|
|
|
|
set microphoneMuted(aMuted) {
|
|
|
|
if (aMuted == this.microphoneMuted) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gAudioManager.microphoneMuted = aMuted;
|
|
|
|
|
|
|
|
if (!this._activeCall) {
|
|
|
|
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
get speakerEnabled() {
|
|
|
|
let force = gAudioManager.getForceForUse(nsIAudioManager.USE_COMMUNICATION);
|
|
|
|
return (force == nsIAudioManager.FORCE_SPEAKER);
|
|
|
|
},
|
|
|
|
|
|
|
|
set speakerEnabled(aEnabled) {
|
|
|
|
if (aEnabled == this.speakerEnabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let force = aEnabled ? nsIAudioManager.FORCE_SPEAKER :
|
|
|
|
nsIAudioManager.FORCE_NONE;
|
|
|
|
gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, force);
|
|
|
|
|
|
|
|
if (!this._activeCall) {
|
|
|
|
gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* nsIGonkTelephonyProvider interface.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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));
|
|
|
|
|
|
|
|
aCall.state = nsITelephonyProvider.CALL_STATE_DISCONNECTED;
|
|
|
|
let duration = ("started" in aCall && typeof aCall.started == "number") ?
|
|
|
|
new Date().getTime() - aCall.started : 0;
|
|
|
|
let data = {
|
|
|
|
number: aCall.number,
|
|
|
|
duration: duration,
|
|
|
|
direction: aCall.isOutgoing ? "outgoing" : "incoming"
|
|
|
|
};
|
|
|
|
gSystemMessenger.broadcastMessage("telephony-call-ended", data);
|
|
|
|
|
2013-12-06 08:02:28 +00:00
|
|
|
aCall.clientId = aClientId;
|
2013-09-07 06:19:57 +00:00
|
|
|
this._updateCallAudioState(aCall, null);
|
|
|
|
|
2013-12-02 10:51:54 +00:00
|
|
|
if (!aCall.failCause ||
|
|
|
|
aCall.failCause === RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) {
|
|
|
|
this._notifyAllListeners("callStateChanged", [aClientId,
|
|
|
|
aCall.callIndex,
|
|
|
|
aCall.state,
|
|
|
|
aCall.number,
|
|
|
|
aCall.isActive,
|
|
|
|
aCall.isOutgoing,
|
|
|
|
aCall.isEmergency,
|
|
|
|
aCall.isConference]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.notifyCallError(aClientId, aCall.callIndex, aCall.failCause);
|
2013-09-07 06:19:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle call error.
|
|
|
|
*/
|
2014-01-13 02:44:40 +00:00
|
|
|
notifyCallError: function(aClientId, aCallIndex, aErrorMsg) {
|
2013-10-31 12:05:51 +00:00
|
|
|
this._notifyAllListeners("notifyError", [aClientId, aCallIndex, aErrorMsg]);
|
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
|
|
|
|
|
|
|
gSystemMessenger.broadcastMessage("telephony-new-call", {});
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle call state changes by updating our current state and the audio
|
|
|
|
* system.
|
|
|
|
*/
|
2014-01-13 02:44:40 +00:00
|
|
|
notifyCallStateChanged: function(aClientId, aCall) {
|
2013-09-07 06:19:57 +00:00
|
|
|
if (DEBUG) debug("handleCallStateChange: " + JSON.stringify(aCall));
|
|
|
|
|
|
|
|
aCall.state = this._convertRILCallState(aCall.state);
|
|
|
|
if (aCall.state == nsITelephonyProvider.CALL_STATE_DIALING) {
|
|
|
|
gSystemMessenger.broadcastMessage("telephony-new-call", {});
|
|
|
|
}
|
|
|
|
|
2013-12-06 08:02:28 +00:00
|
|
|
aCall.clientId = aClientId;
|
2013-09-07 06:19:57 +00:00
|
|
|
this._updateCallAudioState(aCall, null);
|
|
|
|
|
2013-10-31 12:05:51 +00:00
|
|
|
this._notifyAllListeners("callStateChanged", [aClientId,
|
|
|
|
aCall.callIndex,
|
2013-09-07 06:19:57 +00:00
|
|
|
aCall.state,
|
|
|
|
aCall.number,
|
|
|
|
aCall.isActive,
|
|
|
|
aCall.isOutgoing,
|
|
|
|
aCall.isEmergency,
|
|
|
|
aCall.isConference]);
|
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
notifyCdmaCallWaiting: function(aClientId, aNumber) {
|
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-10-31 12:05:51 +00:00
|
|
|
this._notifyAllListeners("notifyCdmaCallWaiting", [aClientId, aNumber]);
|
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);
|
2013-09-12 13:00:18 +00:00
|
|
|
aState = this._convertRILCallState(aState);
|
2013-09-07 06:19:57 +00:00
|
|
|
this._updateCallAudioState(null, aState);
|
|
|
|
|
|
|
|
this._notifyAllListeners("conferenceCallStateChanged", [aState]);
|
|
|
|
},
|
|
|
|
|
2014-01-13 02:44:40 +00:00
|
|
|
notifyConferenceError: function(aName, aMessage) {
|
2013-10-28 06:46:54 +00:00
|
|
|
if (DEBUG) debug("handleConferenceError: " + aName + "." +
|
|
|
|
" Error details: " + aMessage);
|
|
|
|
this._notifyAllListeners("notifyConferenceError", [aName, aMessage]);
|
|
|
|
},
|
|
|
|
|
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();
|
2013-10-24 08:15:06 +00:00
|
|
|
} else if (aData === kPrefDefaultServiceId) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyProvider]);
|