WebAPI/WebTelephony: Difference between revisions

m (→‎Discussions: correct link)
 
(6 intermediate revisions by 5 users not shown)
Line 5: Line 5:
== Status ==
== Status ==


Basic functionality is done with some work remaining.
This has been implemented for B2G in the following bugs:
 
For details about the development, refer to 


* WebTelephony meta bug: {{bug|674726}}
* WebTelephony meta bug: {{bug|674726}}
Line 14: Line 12:
== Implementation Specifics ==
== Implementation Specifics ==


=== Telephony call states ===
=== Telephony call states (GSM) ===


The diagram below shows the current design of B2G telephony call states.  
The diagram below shows the current design of B2G telephony call states.  
Line 24: Line 22:
*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 #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>  
*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.
*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.


Line 31: Line 30:


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.
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 nsIDOMTelephony: nsIDOMEventTarget
{
  nsIDOMTelephonyCall dial(in DOMString number);
  attribute boolean muted;
  attribute boolean speakerEnabled;
  // The call that is "active", i.e. receives microphone input and tones
  // generated via startTone.
  readonly attribute jsval active;
  // Array of all calls that are currently connected.
  readonly attribute jsval calls;
  readonly attribute nsIDOMTelephonyCallGroup conferanceGroup;
  void startTone(in DOMString tone);
  void stopTone();
  attribute nsIDOMEventListener onincoming;
  attribute nsIDOMEventListener oncallschanged;
};
interface nsIDOMTelephonyCall: nsIDOMEventTarget
{
  readonly attribute DOMString number;
  // "dialing", "alerting", "busy", "connecting", "connected", "disconnecting",
  // "disconnected", "incoming", "holding", "held", "resuming"
  readonly attribute DOMString state;
  readonly attribute nsIDOMTelephonyCallGroup group;
  // functions to mediate a call.
  void answer(); 
  void hangUp();
  void hold();
  // Resuming a group automatically holds any other groups/calls
  void resume();
  attribute nsIDOMEventListener onstatechange;
  attribute nsIDOMEventListener ondialing;
  attribute nsIDOMEventListener onalerting;
  attribute nsIDOMEventListener onbusy;
  attribute nsIDOMEventListener onconnecting;
  attribute nsIDOMEventListener onconnected;
  attribute nsIDOMEventListener ondisconnecting;
  attribute nsIDOMEventListener ondisconnected;
  attribute nsIDOMEventListener onincoming;
  attribute nsIDOMEventListener onholding;
  attribute nsIDOMEventListener onheld;
  attribute nsIDOMEventListener onresuming;
  // Fired whenever the .group attribute changes
  attribute nsIDOMEventListener ongroupchange;
};
interface nsIDOMTelephonyCallGroup : nsIDOMEventTarget
{
  // Array of all calls that are currently in this group. The length
  // of this array is never 1.
  readonly attribute jsval calls;
  // Add a call to the callgroup. call2 must not be specified if the
  // callgroup isn't empty.
  // If the callgroup is empty both call and call2 must be specified,
  // and one of them must be in 'held' state and the other in
  // 'connected' state.
  // Neither call or call2 can be in 'disconnected' state.
  void add(nsIDOMTelephonyCall call, optional nsIDOMTelephonyCall call2);
  // Removes a call from the callgroup. If this leaves the callgroup with
  // just one call, then that last call is also removed from the callgroup.
  void remove(nsIDOMTelephonyCall call);
  void hold();
  // Resuming a group automatically holds any other groups/calls
  void resume();
  // When this changes, the state of all contained calls changes at the same time
  readonly attribute DOMString state;
  attribute nsIDOMEventListener onstatechange;
  attribute nsIDOMEventListener onconnected;
  attribute nsIDOMEventListener onholding;
  attribute nsIDOMEventListener onheld;
  attribute nsIDOMEventListener onresuming;
   
   
  // Fires when the array in the 'calls' property changes.
  interface Telephony : EventTarget {
  attribute nsIDOMEventListener oncallschanged;
    /**
};
    * 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 ===
=== Example ===
Line 165: Line 193:
   incoming.answer();   
   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 ==  
== Links ==  
Line 170: Line 225:
* Source code: http://mxr.mozilla.org/mozilla-central/source/dom/telephony/
* 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]
* 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