WebAPI/WebTelephony: Difference between revisions

m (→‎Discussions: correct link)
 
(60 intermediate revisions by 10 users not shown)
Line 1: Line 1:
== Goals ==
== Goals ==


Our first goal is to create a low-level API for "plain" telephony. I.e. not SIP or other VoIP calls. This is because plain telephony is still the main use case for most users. While we long term will want to have APIs for things like SIP, it's likely that those APIs will look substantially different since the capabilities there are much more advanced.
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.


It's possible that we will want to have a higher level API which abstracts over POTS and VoIP, but that's something we should focus on once we have the lower level pieces in place. See also the discussion [[WebAPI#Low_Level_vs._High_Level|here]].
== Status ==
 
This has been implemented for B2G in the following bugs:
 
* WebTelephony meta bug: {{bug|674726}}
* B2G telephony meta bug: {{bug|699235}}


== Status ==
== Implementation Specifics ==
 
=== Telephony call states (GSM) ===
 
The diagram below shows the current design of B2G telephony call states.
 
<br>
 
&nbsp;Some examples of state transition in detail:


There are some rudamentory patches in {{bug|674726}}, but they don't implement much of what we need yet.
*Scenario #1: There is no other call on-line (current design)<br>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.<br>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<br>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. <br>When user answers the new call (call no. 2), its state is going to be transferred to CALL_STATE_CONNECTED. <br>In the meanwhile, the state of the originally connected call (call no. 1) should be forced to CALL_STATE_HELD. <br>
**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<br>User can |Hold()| to change the call state from CALL_STATE_CONNECTED to CALL_STATE_HELD. <br>User can |Resume()| to make a call from CALL_STATE_HELD back to CALL_STATE_CONNECTED.


== Proposed API ==
<br>&nbsp; [[Image:B2GTelephonyCallStates.png|699x781px|B2G telephony call states]]<br>


navigator.telephony would return an object with the following interface
=== 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 {
   interface Telephony : EventTarget {
     TelephonySession connect(DOMString number); // Should this make the session the active one?
     /**
    * 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 boolean mute; // Should these live on the session/group?
     attribute EventHandler onstatechange;
     attribute boolean speaker;
     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;
    
    
     attribute any active; // Either a session or a group
     // Fired whenever the group attribute changes.
    attribute EventHandler ongroupchange;
  };
    
    
    attribute sequence<TelephonySession> sessions;
  interface TelephonyCallGroup : EventTarget {
     attribute sequence<TelephonySessionGroup> groups;
     readonly attribute CallsList calls;
    
    
     attribute Function onincoming;
     [Throws]
  }
    void add(TelephonyCall call);
    
    
  interface TelephonySession {
    [Throws]
     readonly DOMString number;
     void add(TelephonyCall call, TelephonyCall secondCall);
    
    
     readonly attribute DOMString readyState; // "calling", "incomming", "connected", "closed"
     [Throws]
     // Can we get info on when a call goes from "trying to place call" to "calling"?
     void remove(TelephonyCall call);
    attribute Function onconnect;
    attribute Function ondisconnect;
    
    
     attribute TelephonySessionGroup? group;
     [Throws]
    void hold();
    
    
     answer(); // Should this make the session the active one?
     [Throws]
    hangUp();
     void resume();
     void sendTone(DOMString dtmfTones);
  }
    
    
  [Constructor()]
     readonly attribute DOMString state;
  interface TelephonySessionGroup {
    getter TelephonySession item(unsigned long index);
     readonly attribute unsigned long length;
  }
    
    
   interface IncommingCallEvent {
    attribute EventHandler onstatechange;
    attribute TelephonySession session;
    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:
# 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
# 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.
# 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: [https://bugzilla.mozilla.org/show_bug.cgi?id=1036305 Bug 1036305]
 
== Links ==
 
* Source code: http://mxr.mozilla.org/mozilla-central/source/dom/telephony/
* Security discussion: [http://groups.google.com/group/mozilla.dev.b2g/browse_thread/thread/3b128baaa3519dc3/64213b894586061e?lnk=gst&q=webtelephony#64213b894586061e  WebTelephony security discussion] [https://wiki.mozilla.org/Security/WebAPI/Web_Telephony MozillaWiki/WebTelephony Security]
 
[[Category:Web APIs]]

Latest revision as of 11:21, 24 November 2014

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.


   

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