WebAPI/WebTelephony

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

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.


   

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();   
};

Links