1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
8 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
11 Cu.import("resource://gre/modules/Services.jsm");
12 Cu.import("resource://gre/modules/Promise.jsm");
13 Cu.import("resource://gre/modules/systemlibs.js");
15 XPCOMUtils.defineLazyGetter(this, "RIL", function () {
17 Cu.import("resource://gre/modules/ril_consts.js", obj);
21 const GONK_TELEPHONYSERVICE_CONTRACTID =
22 "@mozilla.org/telephony/gonktelephonyservice;1";
23 const GONK_TELEPHONYSERVICE_CID =
24 Components.ID("{67d26434-d063-4d28-9f48-5b3189788155}");
26 const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
28 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
30 const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
31 const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
32 const kPrefDefaultServiceId = "dom.telephony.defaultServiceId";
34 const nsIAudioManager = Ci.nsIAudioManager;
35 const nsITelephonyService = Ci.nsITelephonyService;
37 const CALL_WAKELOCK_TIMEOUT = 5000;
39 // Index of the CDMA second call which isn't held in RIL but only in TelephoyService.
40 const CDMA_SECOND_CALL_INDEX = 2;
42 const DIAL_ERROR_INVALID_STATE_ERROR = "InvalidStateError";
43 const DIAL_ERROR_OTHER_CONNECTION_IN_USE = "OtherConnectionInUse";
44 const DIAL_ERROR_BAD_NUMBER = RIL.GECKO_CALL_ERROR_BAD_NUMBER;
46 const AUDIO_STATE_NAME = [
48 "PHONE_STATE_RINGTONE",
52 const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
56 dump("TelephonyService: " + s + "\n");
59 XPCOMUtils.defineLazyGetter(this, "gAudioManager", function getAudioManager() {
61 return Cc["@mozilla.org/telephony/audiomanager;1"]
62 .getService(nsIAudioManager);
64 //TODO on the phone this should not fall back as silently.
66 // Fake nsIAudioManager implementation so that we can run the telephony
67 // code in a non-Gonk build.
68 if (DEBUG) debug("Using fake audio manager.");
70 microphoneMuted: false,
73 phoneState: nsIAudioManager.PHONE_STATE_CURRENT,
76 setForceForUse: function(usage, force) {
77 this._forceForUse[usage] = force;
80 getForceForUse: function(usage) {
81 return this._forceForUse[usage] || nsIAudioManager.FORCE_NONE;
87 XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
89 "nsIRadioInterfaceLayer");
91 XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
92 "@mozilla.org/power/powermanagerservice;1",
93 "nsIPowerManagerService");
95 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
96 "@mozilla.org/system-message-internal;1",
97 "nsISystemMessagesInternal");
99 XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function() {
101 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns);
102 return ns.PhoneNumberUtils;
105 function TelephonyService() {
106 this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
107 this._listeners = [];
108 this._currentCalls = {};
109 this._audioStates = {};
111 this._cdmaCallWaitingNumber = null;
113 // _isActiveCall[clientId][callIndex] shows the active status of the call.
114 this._isActiveCall = {};
115 this._numActiveCall = 0;
117 this._updateDebugFlag();
118 this.defaultServiceId = this._getDefaultServiceId();
120 Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
121 Services.prefs.addObserver(kPrefDefaultServiceId, this, false);
123 Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
125 for (let i = 0; i < this._numClients; ++i) {
126 this._enumerateCallsForClient(i);
127 this._isActiveCall[i] = {};
128 this._audioStates[i] = RIL.AUDIO_STATE_NO_CALL;
131 TelephonyService.prototype = {
132 classID: GONK_TELEPHONYSERVICE_CID,
133 classInfo: XPCOMUtils.generateCI({classID: GONK_TELEPHONYSERVICE_CID,
134 contractID: GONK_TELEPHONYSERVICE_CONTRACTID,
135 classDescription: "TelephonyService",
136 interfaces: [Ci.nsITelephonyService,
137 Ci.nsIGonkTelephonyService],
138 flags: Ci.nsIClassInfo.SINGLETON}),
139 QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyService,
140 Ci.nsIGonkTelephonyService,
143 // The following attributes/functions are used for acquiring/releasing the
144 // CPU wake lock when the RIL handles the incoming call. Note that we need
145 // a timer to bound the lock's life cycle to avoid exhausting the battery.
146 _callRingWakeLock: null,
147 _callRingWakeLockTimer: null,
149 _acquireCallRingWakeLock: function() {
150 if (!this._callRingWakeLock) {
151 if (DEBUG) debug("Acquiring a CPU wake lock for handling incoming call.");
152 this._callRingWakeLock = gPowerManagerService.newWakeLock("cpu");
154 if (!this._callRingWakeLockTimer) {
155 if (DEBUG) debug("Creating a timer for releasing the CPU wake lock.");
156 this._callRingWakeLockTimer =
157 Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
159 if (DEBUG) debug("Setting the timer for releasing the CPU wake lock.");
160 this._callRingWakeLockTimer
161 .initWithCallback(this._releaseCallRingWakeLock.bind(this),
162 CALL_WAKELOCK_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
165 _releaseCallRingWakeLock: function() {
166 if (DEBUG) debug("Releasing the CPU wake lock for handling incoming call.");
167 if (this._callRingWakeLockTimer) {
168 this._callRingWakeLockTimer.cancel();
170 if (this._callRingWakeLock) {
171 this._callRingWakeLock.unlock();
172 this._callRingWakeLock = null;
176 _getClient: function(aClientId) {
177 return gRadioInterfaceLayer.getRadioInterface(aClientId);
180 _sendToRilWorker: function(aClientId, aType, aMessage, aCallback) {
181 this._getClient(aClientId).sendWorkerMessage(aType, aMessage, aCallback);
184 // An array of nsITelephonyListener instances.
186 _notifyAllListeners: function(aMethodName, aArgs) {
187 let listeners = this._listeners.slice();
188 for (let listener of listeners) {
189 if (this._listeners.indexOf(listener) == -1) {
190 // Listener has been unregistered in previous run.
194 let handler = listener[aMethodName];
196 handler.apply(listener, aArgs);
198 debug("listener for " + aMethodName + " threw an exception: " + e);
204 * Track the active call and update the audio system as its state changes.
206 _updateActiveCall: function(aCall) {
208 let incoming = false;
210 switch (aCall.state) {
211 case nsITelephonyService.CALL_STATE_DIALING: // Fall through...
212 case nsITelephonyService.CALL_STATE_ALERTING:
213 case nsITelephonyService.CALL_STATE_CONNECTED:
216 case nsITelephonyService.CALL_STATE_INCOMING:
219 case nsITelephonyService.CALL_STATE_HELD: // Fall through...
220 case nsITelephonyService.CALL_STATE_DISCONNECTED:
224 // Update active count and info.
225 let oldActive = this._isActiveCall[aCall.clientId][aCall.callIndex];
226 if (!oldActive && active) {
227 this._numActiveCall++;
228 } else if (oldActive && !active) {
229 this._numActiveCall--;
231 this._isActiveCall[aCall.clientId][aCall.callIndex] = active;
234 _updateAudioState: function(aAudioState) {
235 switch (aAudioState) {
236 case RIL.AUDIO_STATE_NO_CALL:
237 gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
240 case RIL.AUDIO_STATE_INCOMING:
241 gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE;
244 case RIL.AUDIO_STATE_IN_CALL:
245 gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
246 if (this.speakerEnabled) {
247 gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
248 nsIAudioManager.FORCE_SPEAKER);
254 debug("Put audio system into " + AUDIO_STATE_NAME[aAudioState] + ": " +
255 aAudioState + ", result is: " + gAudioManager.phoneState);
259 _convertRILCallState: function(aState) {
261 case RIL.CALL_STATE_UNKNOWN:
262 return nsITelephonyService.CALL_STATE_UNKNOWN;
263 case RIL.CALL_STATE_ACTIVE:
264 return nsITelephonyService.CALL_STATE_CONNECTED;
265 case RIL.CALL_STATE_HOLDING:
266 return nsITelephonyService.CALL_STATE_HELD;
267 case RIL.CALL_STATE_DIALING:
268 return nsITelephonyService.CALL_STATE_DIALING;
269 case RIL.CALL_STATE_ALERTING:
270 return nsITelephonyService.CALL_STATE_ALERTING;
271 case RIL.CALL_STATE_INCOMING:
272 case RIL.CALL_STATE_WAITING:
273 return nsITelephonyService.CALL_STATE_INCOMING;
275 throw new Error("Unknown rilCallState: " + aState);
279 _convertRILSuppSvcNotification: function(aNotification) {
280 switch (aNotification) {
281 case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD:
282 return nsITelephonyService.NOTIFICATION_REMOTE_HELD;
283 case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED:
284 return nsITelephonyService.NOTIFICATION_REMOTE_RESUMED;
287 debug("Unknown rilSuppSvcNotification: " + aNotification);
293 _updateDebugFlag: function() {
295 DEBUG = RIL.DEBUG_RIL ||
296 Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
300 _getDefaultServiceId: function() {
301 let id = Services.prefs.getIntPref(kPrefDefaultServiceId);
302 let numRil = Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
304 if (id >= numRil || id < 0) {
312 _enumerateCallsForClient: function(aClientId) {
313 if (DEBUG) debug("Enumeration of calls for client " + aClientId);
315 this._sendToRilWorker(aClientId, "enumerateCalls", null, response => {
316 if (!this._currentCalls[aClientId]) {
317 this._currentCalls[aClientId] = {};
319 for (let call of response.calls) {
320 call.clientId = aClientId;
321 call.state = this._convertRILCallState(call.state);
322 call.isSwitchable = true;
323 call.isMergeable = true;
325 this._currentCalls[aClientId][call.callIndex] = call;
331 * Check a given number against the list of emergency numbers provided by the
335 * The number to look up.
337 _isEmergencyNumber: function(aNumber) {
338 // Check ril provided numbers first.
339 let numbers = libcutils.property_get("ril.ecclist") ||
340 libcutils.property_get("ro.ril.ecclist");
342 numbers = numbers.split(",");
344 // No ecclist system property, so use our own list.
345 numbers = DEFAULT_EMERGENCY_NUMBERS;
347 return numbers.indexOf(aNumber) != -1;
351 * Checks whether to temporarily suppress caller id for the call.
356 _isTemporaryCLIR: function(aMmi) {
357 return (aMmi && aMmi.serviceCode === RIL.MMI_SC_CLIR) && aMmi.dialNumber;
361 * Map MMI procedure to CLIR MODE.
366 _getTemporaryCLIRMode: function(aProcedure) {
367 // In temporary mode, MMI_PROCEDURE_ACTIVATION means allowing CLI
368 // presentation, i.e. CLIR_SUPPRESSION. See TS 22.030, Annex B.
369 switch (aProcedure) {
370 case RIL.MMI_PROCEDURE_ACTIVATION:
371 return RIL.CLIR_SUPPRESSION;
372 case RIL.MMI_PROCEDURE_DEACTIVATION:
373 return RIL.CLIR_INVOCATION;
375 return RIL.CLIR_DEFAULT;
380 * nsITelephonyService interface.
385 registerListener: function(aListener) {
386 if (this._listeners.indexOf(aListener) >= 0) {
387 throw Cr.NS_ERROR_UNEXPECTED;
390 this._listeners.push(aListener);
393 unregisterListener: function(aListener) {
394 let index = this._listeners.indexOf(aListener);
396 throw Cr.NS_ERROR_UNEXPECTED;
399 this._listeners.splice(index, 1);
402 enumerateCalls: function(aListener) {
403 if (DEBUG) debug("Requesting enumeration of calls for callback");
405 for (let cid = 0; cid < this._numClients; ++cid) {
406 let calls = this._currentCalls[cid];
410 for (let i = 0, indexes = Object.keys(calls); i < indexes.length; ++i) {
411 let call = calls[indexes[i]];
412 aListener.enumerateCallState(call.clientId, call.callIndex,
413 call.state, call.number,
414 call.numberPresentation, call.name,
415 call.namePresentation, call.isOutgoing,
416 call.isEmergency, call.isConference,
417 call.isSwitchable, call.isMergeable);
420 aListener.enumerateCallStateComplete();
423 _hasCallsOnOtherClient: function(aClientId) {
424 for (let cid = 0; cid < this._numClients; ++cid) {
425 if (cid === aClientId) {
428 if (Object.keys(this._currentCalls[cid]).length !== 0) {
435 // All calls in the conference is regarded as one conference call.
436 _numCallsOnLine: function(aClientId) {
438 let hasConference = false;
440 for (let cid in this._currentCalls[aClientId]) {
441 let call = this._currentCalls[aClientId][cid];
442 if (call.isConference) {
443 hasConference = true;
449 return hasConference ? numCalls + 1 : numCalls;
453 * Get arbitrary one of active call.
455 _getOneActiveCall: function(aClientId) {
456 for (let index in this._currentCalls[aClientId]) {
457 let call = this._currentCalls[aClientId][index];
458 if (call.state === nsITelephonyService.CALL_STATE_CONNECTED) {
465 _addCdmaChildCall: function(aClientId, aNumber, aParentId) {
467 callIndex: CDMA_SECOND_CALL_INDEX,
468 state: RIL.CALL_STATE_DIALING,
478 // Manual update call state according to the request response.
479 this.notifyCallStateChanged(aClientId, childCall);
481 childCall.state = RIL.CALL_STATE_ACTIVE;
482 this.notifyCallStateChanged(aClientId, childCall);
484 let parentCall = this._currentCalls[aClientId][childCall.parentId];
485 parentCall.childId = CDMA_SECOND_CALL_INDEX;
486 parentCall.state = RIL.CALL_STATE_HOLDING;
487 parentCall.isSwitchable = false;
488 parentCall.isMergeable = true;
489 this.notifyCallStateChanged(aClientId, parentCall);
492 _composeDialRequest: function(aClientId, aNumber) {
493 return new Promise((resolve, reject) => {
494 this._sendToRilWorker(aClientId, "parseMMIFromDialNumber",
495 {number: aNumber}, response => {
497 let mmi = response.mmi;
503 } else if (this._isTemporaryCLIR(mmi)) {
505 number: mmi.dialNumber,
506 clirMode: this._getTemporaryCLIRMode(mmi.procedure)
509 reject(DIAL_ERROR_BAD_NUMBER);
515 cachedDialRequest: null,
518 dial: function(aClientId, aNumber, aIsDialEmergency, aCallback) {
519 if (DEBUG) debug("Dialing " + (aIsDialEmergency ? "emergency " : "") + aNumber);
521 if (this.isDialing) {
522 if (DEBUG) debug("Error: Already has a dialing call.");
523 aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
527 // We can only have at most two calls on the same line (client).
528 if (this._numCallsOnLine(aClientId) >= 2) {
529 if (DEBUG) debug("Error: Already has more than 2 calls on line.");
530 aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
534 // For DSDS, if there is aleady a call on SIM 'aClientId', we cannot place
535 // any new call on other SIM.
536 if (this._hasCallsOnOtherClient(aClientId)) {
537 if (DEBUG) debug("Error: Already has a call on other sim.");
538 aCallback.notifyError(DIAL_ERROR_OTHER_CONNECTION_IN_USE);
542 // We don't try to be too clever here, as the phone is probably in the
543 // locked state. Let's just check if it's a number without normalizing
544 if (!aIsDialEmergency) {
545 aNumber = gPhoneNumberUtils.normalize(aNumber);
548 // Validate the number.
549 // Note: isPlainPhoneNumber also accepts USSD and SS numbers
550 if (!gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) {
551 if (DEBUG) debug("Error: Number '" + aNumber + "' is not viable. Drop.");
552 aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
556 this._composeDialRequest(aClientId, aNumber).then(options => {
557 options.isEmergency = this._isEmergencyNumber(options.number);
558 options.isDialEmergency = aIsDialEmergency;
560 if (options.isEmergency) {
561 // Automatically select a proper clientId for emergency call.
562 aClientId = gRadioInterfaceLayer.getClientIdForEmergencyCall() ;
563 if (aClientId === -1) {
564 if (DEBUG) debug("Error: No client is avaialble for emergency call.");
565 aCallback.notifyError(DIAL_ERROR_INVALID_STATE_ERROR);
570 // Before we dial, we have to hold the active call first.
571 let activeCall = this._getOneActiveCall(aClientId);
573 this._dialInternal(aClientId, options, aCallback);
575 if (DEBUG) debug("There is an active call. Hold it first before dial.");
577 this.cachedDialRequest = {
583 if (activeCall.isConference) {
584 this.holdConference(aClientId);
586 this.holdCall(aClientId, activeCall.callIndex);
590 aCallback.notifyError(DIAL_ERROR_BAD_NUMBER);
594 _dialInternal: function(aClientId, aOptions, aCallback) {
595 this.isDialing = true;
597 this._sendToRilWorker(aClientId, "dial", aOptions, response => {
598 this.isDialing = false;
600 if (!response.success) {
601 aCallback.notifyError(response.errorMsg);
605 let currentCdmaCallIndex = !response.isCdma ? null :
606 Object.keys(this._currentCalls[aClientId])[0];
608 if (currentCdmaCallIndex == null) {
609 aCallback.notifyDialCallSuccess(aClientId, response.callIndex,
612 // RIL doesn't hold the 2nd call. We create one by ourselves.
613 aCallback.notifyDialCallSuccess(aClientId, CDMA_SECOND_CALL_INDEX,
615 this._addCdmaChildCall(aClientId, response.number, currentCdmaCallIndex);
620 hangUp: function(aClientId, aCallIndex) {
621 let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
623 // Should release both, child and parent, together. Since RIL holds only
624 // the parent call, we send 'parentId' to RIL.
625 this.hangUp(aClientId, parentId);
627 this._sendToRilWorker(aClientId, "hangUp", { callIndex: aCallIndex });
631 startTone: function(aClientId, aDtmfChar) {
632 this._sendToRilWorker(aClientId, "startTone", { dtmfChar: aDtmfChar });
635 stopTone: function(aClientId) {
636 this._sendToRilWorker(aClientId, "stopTone");
639 answerCall: function(aClientId, aCallIndex) {
640 this._sendToRilWorker(aClientId, "answerCall", { callIndex: aCallIndex });
643 rejectCall: function(aClientId, aCallIndex) {
644 this._sendToRilWorker(aClientId, "rejectCall", { callIndex: aCallIndex });
647 holdCall: function(aClientId, aCallIndex) {
648 let call = this._currentCalls[aClientId][aCallIndex];
649 if (!call || !call.isSwitchable) {
650 // TODO: Bug 975949 - [B2G] Telephony should throw exceptions when some
651 // operations aren't allowed instead of simply ignoring them.
655 this._sendToRilWorker(aClientId, "holdCall", { callIndex: aCallIndex });
658 resumeCall: function(aClientId, aCallIndex) {
659 let call = this._currentCalls[aClientId][aCallIndex];
660 if (!call || !call.isSwitchable) {
661 // TODO: Bug 975949 - [B2G] Telephony should throw exceptions when some
662 // operations aren't allowed instead of simply ignoring them.
666 this._sendToRilWorker(aClientId, "resumeCall", { callIndex: aCallIndex });
669 conferenceCall: function(aClientId) {
670 let indexes = Object.keys(this._currentCalls[aClientId]);
671 if (indexes.length < 2) {
672 // TODO: Bug 975949 - [B2G] Telephony should throw exceptions when some
673 // operations aren't allowed instead of simply ignoring them.
677 for (let i = 0; i < indexes.length; ++i) {
678 let call = this._currentCalls[aClientId][indexes[i]];
679 if (!call.isMergeable) {
684 function onCdmaConferenceCallSuccess() {
685 let indexes = Object.keys(this._currentCalls[aClientId]);
686 if (indexes.length < 2) {
690 for (let i = 0; i < indexes.length; ++i) {
691 let call = this._currentCalls[aClientId][indexes[i]];
692 call.state = RIL.CALL_STATE_ACTIVE;
693 call.isConference = true;
694 this.notifyCallStateChanged(aClientId, call);
696 this.notifyConferenceCallStateChanged(RIL.CALL_STATE_ACTIVE);
699 this._sendToRilWorker(aClientId, "conferenceCall", null, response => {
700 if (!response.success) {
701 this._notifyAllListeners("notifyConferenceError", [response.errorName,
706 if (response.isCdma) {
707 onCdmaConferenceCallSuccess.call(this);
712 separateCall: function(aClientId, aCallIndex) {
713 let call = this._currentCalls[aClientId][aCallIndex];
714 if (!call || !call.isConference) {
715 // TODO: Bug 975949 - [B2G] Telephony should throw exceptions when some
716 // operations aren't allowed instead of simply ignoring them.
720 let parentId = call.parentId;
722 this.separateCall(aClientId, parentId);
726 function onCdmaSeparateCallSuccess() {
727 // See 3gpp2, S.R0006-522-A v1.0. Table 4, XID 6S.
728 let call = this._currentCalls[aClientId][aCallIndex];
729 if (!call || !call.isConference) {
733 let childId = call.childId;
738 let childCall = this._currentCalls[aClientId][childId];
739 this.notifyCallDisconnected(aClientId, childCall);
742 this._sendToRilWorker(aClientId, "separateCall", { callIndex: aCallIndex },
744 if (!response.success) {
745 this._notifyAllListeners("notifyConferenceError", [response.errorName,
750 if (response.isCdma) {
751 onCdmaSeparateCallSuccess.call(this);
756 hangUpConference: function(aClientId, aCallback) {
757 this._sendToRilWorker(aClientId, "hangUpConference", null, response => {
758 if (!response.success) {
759 aCallback.notifyError(response.errorMsg);
761 aCallback.notifySuccess();
766 holdConference: function(aClientId) {
767 this._sendToRilWorker(aClientId, "holdConference");
770 resumeConference: function(aClientId) {
771 this._sendToRilWorker(aClientId, "resumeConference");
774 get microphoneMuted() {
775 return gAudioManager.microphoneMuted;
778 set microphoneMuted(aMuted) {
779 if (aMuted == this.microphoneMuted) {
782 gAudioManager.microphoneMuted = aMuted;
785 get speakerEnabled() {
786 let force = gAudioManager.getForceForUse(nsIAudioManager.USE_COMMUNICATION);
787 return (force == nsIAudioManager.FORCE_SPEAKER);
790 set speakerEnabled(aEnabled) {
791 if (aEnabled == this.speakerEnabled) {
794 let force = aEnabled ? nsIAudioManager.FORCE_SPEAKER :
795 nsIAudioManager.FORCE_NONE;
796 gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, force);
800 * nsIGonkTelephonyService interface.
803 notifyAudioStateChanged: function(aClientId, aState) {
804 this._audioStates[aClientId] = aState;
806 let audioState = aState;
807 for (let i = 0; i < this._numClients; ++i) {
808 audioState = Math.max(audioState, this._audioStates[i]);
811 this._updateAudioState(audioState);
815 * Handle call disconnects by updating our current state and the audio system.
817 notifyCallDisconnected: function(aClientId, aCall) {
818 if (DEBUG) debug("handleCallDisconnected: " + JSON.stringify(aCall));
820 aCall.clientId = aClientId;
821 aCall.state = nsITelephonyService.CALL_STATE_DISCONNECTED;
822 aCall.isEmergency = this._isEmergencyNumber(aCall.number);
823 let duration = ("started" in aCall && typeof aCall.started == "number") ?
824 new Date().getTime() - aCall.started : 0;
826 number: aCall.number,
827 serviceId: aClientId,
828 emergency: aCall.isEmergency,
830 direction: aCall.isOutgoing ? "outgoing" : "incoming",
831 hangUpLocal: aCall.hangUpLocal
834 if (this._cdmaCallWaitingNumber != null) {
835 data.secondNumber = this._cdmaCallWaitingNumber;
836 this._cdmaCallWaitingNumber = null;
839 gSystemMessenger.broadcastMessage("telephony-call-ended", data);
841 let manualConfStateChange = false;
842 let childId = this._currentCalls[aClientId][aCall.callIndex].childId;
844 // Child cannot live without parent.
845 let childCall = this._currentCalls[aClientId][childId];
846 this.notifyCallDisconnected(aClientId, childCall);
848 let parentId = this._currentCalls[aClientId][aCall.callIndex].parentId;
850 let parentCall = this._currentCalls[aClientId][parentId];
851 // The child is going to be released.
852 delete parentCall.childId;
853 if (parentCall.isConference) {
854 // As the child is going to be gone, the parent should be moved out
855 // of conference accordingly.
856 manualConfStateChange = true;
857 parentCall.isConference = false;
858 parentCall.isSwitchable = true;
859 parentCall.isMergeable = true;
860 aCall.isConference = false;
861 this.notifyCallStateChanged(aClientId, parentCall, true);
866 this._updateActiveCall(aCall);
868 if (!aCall.failCause ||
869 aCall.failCause === RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) {
870 this._notifyAllListeners("callStateChanged", [aClientId,
874 aCall.numberPresentation,
876 aCall.namePresentation,
883 this._notifyAllListeners("notifyError",
884 [aClientId, aCall.callIndex, aCall.failCause]);
886 delete this._currentCalls[aClientId][aCall.callIndex];
888 if (manualConfStateChange) {
889 this.notifyConferenceCallStateChanged(RIL.CALL_STATE_UNKNOWN);
894 * Handle an incoming call.
896 * Not much is known about this call at this point, but it's enough
897 * to start bringing up the Phone app already.
899 notifyCallRing: function() {
900 // We need to acquire a CPU wake lock to avoid the system falling into
901 // the sleep mode when the RIL handles the incoming call.
902 this._acquireCallRingWakeLock();
904 gSystemMessenger.broadcastMessage("telephony-new-call", {});
908 * Handle call state changes by updating our current state and the audio
911 notifyCallStateChanged: function(aClientId, aCall, aSkipStateConversion) {
912 if (DEBUG) debug("handleCallStateChange: " + JSON.stringify(aCall));
914 if (!aSkipStateConversion) {
915 aCall.state = this._convertRILCallState(aCall.state);
918 if (aCall.state == nsITelephonyService.CALL_STATE_DIALING) {
919 gSystemMessenger.broadcastMessage("telephony-new-call", {});
922 aCall.clientId = aClientId;
923 this._updateActiveCall(aCall);
925 function pick(arg, defaultValue) {
926 return typeof arg !== 'undefined' ? arg : defaultValue;
929 let call = this._currentCalls[aClientId][aCall.callIndex];
931 call.state = aCall.state;
932 call.number = aCall.number;
933 call.isEmergency = this._isEmergencyNumber(aCall.number);
934 call.isConference = aCall.isConference;
935 call.isSwitchable = pick(aCall.isSwitchable, call.isSwitchable);
936 call.isMergeable = pick(aCall.isMergeable, call.isMergeable);
939 call.isEmergency = pick(aCall.isEmergency, this._isEmergencyNumber(aCall.number));
940 call.isSwitchable = pick(aCall.isSwitchable, true);
941 call.isMergeable = pick(aCall.isMergeable, true);
942 call.name = pick(aCall.name, "");
943 call.numberPresentaation = pick(aCall.numberPresentation, nsITelephonyService.CALL_PRESENTATION_ALLOWED);
944 call.namePresentaation = pick(aCall.namePresentation, nsITelephonyService.CALL_PRESENTATION_ALLOWED);
946 this._currentCalls[aClientId][aCall.callIndex] = call;
949 // Handle cached dial request.
950 if (this.cachedDialRequest && !this._getOneActiveCall()) {
951 if (DEBUG) debug("All calls held. Perform the cached dial request.");
953 let request = this.cachedDialRequest;
954 this._dialInternal(request.clientId, request.options, request.callback);
955 this.cachedDialRequest = null;
958 this._notifyAllListeners("callStateChanged", [aClientId,
962 call.numberPresentation,
964 call.namePresentation,
972 notifyCdmaCallWaiting: function(aClientId, aCall) {
973 // We need to acquire a CPU wake lock to avoid the system falling into
974 // the sleep mode when the RIL handles the incoming call.
975 this._acquireCallRingWakeLock();
977 let call = this._currentCalls[aClientId][CDMA_SECOND_CALL_INDEX];
979 // TODO: Bug 977503 - B2G RIL: [CDMA] update callNumber when a waiting
980 // call comes after a 3way call.
981 this.notifyCallDisconnected(aClientId, call);
984 this._cdmaCallWaitingNumber = aCall.number;
986 this._notifyAllListeners("notifyCdmaCallWaiting", [aClientId,
988 aCall.numberPresentation,
990 aCall.namePresentation]);
993 notifySupplementaryService: function(aClientId, aCallIndex, aNotification) {
994 let notification = this._convertRILSuppSvcNotification(aNotification);
995 this._notifyAllListeners("supplementaryServiceNotification",
996 [aClientId, aCallIndex, notification]);
999 notifyConferenceCallStateChanged: function(aState) {
1000 if (DEBUG) debug("handleConferenceCallStateChanged: " + aState);
1001 aState = this._convertRILCallState(aState);
1002 this._notifyAllListeners("conferenceCallStateChanged", [aState]);
1006 * nsIObserver interface.
1009 observe: function(aSubject, aTopic, aData) {
1011 case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
1012 if (aData === kPrefRilDebuggingEnabled) {
1013 this._updateDebugFlag();
1014 } else if (aData === kPrefDefaultServiceId) {
1015 this.defaultServiceId = this._getDefaultServiceId();
1019 case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
1020 // Release the CPU wake lock for handling the incoming call.
1021 this._releaseCallRingWakeLock();
1023 Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
1029 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyService]);