WebAPI/WebTelephony

From MozillaWiki
Jump to: navigation, search

Goals

The aim of WebTelephony is to establish a DOM API, which allows web content to dial out and mediate calls, i.e. answer, reject, hold or resume a call.

Status

This has been implemented for B2G in the following bugs:

Implementation Specifics

Telephony call states (GSM)

The diagram below shows the current design of B2G telephony call states.


 Some examples of state transition in detail:

  • Scenario #1: There is no other call on-line (current design)
    When a remote party dials, a new call is generated with its call index (no. 1), and the call state now is CALL_STATE_INCOMING.
    When user answers/hangs up the call, the call state is eventually pushed to CALL_STATE_CONNECTED/CALL_STATE_DISCONNECTED according to user's decision.
  • Scenario #2: There is already a call on-line
    When the third party dials, a new call is generated with the state of CALL_STATE_INCOMING. Since there is already a call on-line, the new call's index is no. 2.
    When user answers the new call (call no. 2), its state is going to be transferred to CALL_STATE_CONNECTED.
    In the meanwhile, the state of the originally connected call (call no. 1) should be forced to CALL_STATE_HELD.
    • Note: The served mobile subscriber can only have one call on hold at a time, according to 3GPP Specification "TS 22.083 section 2.2.1."
  • Scenario #3: User wants to hold a call when there's no waiting call
    User can |Hold()| to change the call state from CALL_STATE_CONNECTED to CALL_STATE_HELD.
    User can |Resume()| to make a call from CALL_STATE_HELD back to CALL_STATE_CONNECTED.


  B2G telephony call states

DOM API

We can access the phone functionality simply through navigator.mozTelephony. Once we have a reference to that object, we can start placing and recieving calls by the API below.

 interface Telephony : EventTarget {
   /**
    * There are multiple telephony services in multi-sim architecture. We use
    * |serviceId| to indicate the target telephony service. If not specified,
    * the implementation MUST use the default service.
    *
    * Possible values of |serviceId| are 0 ~ (number of services - 1), which is
    * simply the index of a service. Get number of services by acquiring
    * |navigator.mozMobileConnections.length|.
    */
 
   // Promise<TelephonyCall>
   Promise dial(DOMString number, optional unsigned long serviceId);
 
   // Promise<TelephonyCall>
   Promise dialEmergency(DOMString number, optional unsigned long serviceId);
 
   [Throws]
   void startTone(DOMString tone, optional unsigned long serviceId);
 
   [Throws]
   void stopTone(optional unsigned long serviceId);
 
   [Throws]
   attribute boolean muted;
 
   [Throws]
   attribute boolean speakerEnabled;
 
   readonly attribute (TelephonyCall or TelephonyCallGroup)? active;
 
   // A call is contained either in Telephony or in TelephonyCallGroup.
   readonly attribute CallsList calls;
   readonly attribute TelephonyCallGroup conferenceGroup;
 
   attribute EventHandler onincoming;
   attribute EventHandler oncallschanged;
   attribute EventHandler onremoteheld;
   attribute EventHandler onremoteresumed;
 };
 
 interface TelephonyCall : EventTarget {
   // Indicate which service the call comes from.
   readonly attribute unsigned long serviceId;
 
   readonly attribute DOMString number;
 
   // In CDMA networks, the 2nd waiting call shares the connection with the 1st
   // call. We need an additional attribute for the 2nd number.
   readonly attribute DOMString? secondNumber;
 
   readonly attribute DOMString state;
 
   // The property "emergency" indicates whether the call number is an emergency
   // number. Only the outgoing call could have a value with true and it is
   // available after dialing state.
   readonly attribute boolean emergency;
 
   // Indicate whether the call state can be switched between "connected" and
   // "held".
   readonly attribute boolean switchable;
 
   // Indicate whether the call can be added into TelephonyCallGroup.
   readonly attribute boolean mergeable;
 
   readonly attribute DOMError? error;
 
   readonly attribute TelephonyCallGroup? group;
 
   [Throws]
   void answer();
   [Throws]
   void hangUp();
   [Throws]
   void hold();
   [Throws]
   void resume();
 
   attribute EventHandler onstatechange;
   attribute EventHandler ondialing;
   attribute EventHandler onalerting;
   attribute EventHandler onconnecting;
   attribute EventHandler onconnected;
   attribute EventHandler ondisconnecting;
   attribute EventHandler ondisconnected;
   attribute EventHandler onholding;
   attribute EventHandler onheld;
   attribute EventHandler onresuming;
   attribute EventHandler onerror;
 
   // Fired whenever the group attribute changes.
   attribute EventHandler ongroupchange;
 };
 
 interface TelephonyCallGroup : EventTarget {
   readonly attribute CallsList calls;
 
   [Throws]
   void add(TelephonyCall call);
 
   [Throws]
   void add(TelephonyCall call, TelephonyCall secondCall);
 
   [Throws]
   void remove(TelephonyCall call);
 
   [Throws]
   void hold();
 
   [Throws]
   void resume();
 
   readonly attribute DOMString state;
 
   attribute EventHandler onstatechange;
   attribute EventHandler onconnected;
   attribute EventHandler onholding;
   attribute EventHandler onheld;
   attribute EventHandler onresuming;
   attribute EventHandler oncallschanged;
   attribute EventHandler onerror;
 };

Example

Here are few examples about how to use WebTelephony API.

Place a call

// First, obtain a telephony object.
var telephony = navigator.mozTelephony;

// Check if the speaker is enabled.
concole.log(telephony.speakerEnabled);
// Then, we dial out.
var outgoing = telephony.dial(phoneNumber);
// Event handlers for the call.
outgoing.onconnected = function onconnected(event) {
  /* Do something when the callee picks up the call. */
};

outgoing.ondisconnected = function ondisconnected(event) {
  /* Do something when the call finishes. */
};

// Hang up the call.
outgoing.hangUp();

Receive a call

// First, obtain a telephony object.
var telephony = navigator.mozTelephony;
// Receive an incoming call.
telephony.onincoming = function onincoming(event) {
  var incoming = event.call;
  
  // Answer the call. 
  incoming.answer();   
};

CDMA Multiparty Call Design

Call Waiting Scenario

When a CDMA call waiting notification is received on a device, there's only one connection with carrier. The API tries to reflect the behaviour - keep only one TelephonyCall object.

API design in detailed:

  1. The waiting call number is assigned to the only call's second number, i.e. navigator.telephony.calls[0].secondId, and navigator.telephony.oncallschanged is fired
  2. To accept the waiting call or to switch between two calls, simply call |navigator.telephony.calls[0].hold()|. As the only network connection remains "connected" the API follows the situation, so |.calls[0].state| remains "connected" even |hold()| succeeds.
  3. No specific API for rejecting the waiting call as there's no way for that. As a result, it relies on application's implementation to hide the UI of the waiting call when user wants to ignore the call.

Pros:

  • Keep it as simple as the network itself

Cons:

  • When user rejects the second call, the API and the backend TelephonyService have no idea. That also leads other components monitoring TelephonyService, e.g. BT, to some trouble. Currently, Mozilla BT webAPI has to expose a sort of hacky interface for Dialer app to call when user ignores the waiting call.

3-way Call Scenario

With vendor support, it's likely to detect fine call state changes. To provide a possibility for customization, a heavy abstraction has been introduced. As a result, the CDMA 3-way call API is the same as the GSM conference call API.

Pros:

  • WebAPI allows possible customization in ril to provide more call state changes.
    • Note: in reference design, for instance, it's no way to know if a second call is successfully established or accepted by the remote party.

Cons:

  • Over abstraction that requires much maintance effort on "fake" call state transition
  • The API hides the CDMA behaviour and causes confusions

Discussions

To resolve the inconsistency in the CDMA multiparty call API: Bug 1036305

Links