Bug 707629 - Part 1: Add call state handling and manipulation to the RIL worker. r=gal
authorPhilipp von Weitershausen <philipp@weitershausen.de>
Wed, 07 Dec 2011 14:57:05 +0800
changeset 83793 c634c79dd8293bd20d48adca0d30a59db75f0fa5
parent 83792 346e090f5a4dc361b37de2235c518d4350a25f05
child 83794 40d760a401001241d3b624306832c241515f4c12
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgal
bugs707629
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 707629 - Part 1: Add call state handling and manipulation to the RIL worker. r=gal
dom/telephony/ril_consts.js
dom/telephony/ril_worker.js
--- a/dom/telephony/ril_consts.js
+++ b/dom/telephony/ril_consts.js
@@ -31,29 +31,16 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-const CARD_MAX_APPS = 8;
-
-const RADIO_STATE_OFF = 0;
-const RADIO_STATE_UNAVAILABLE = 1;
-const RADIO_STATE_SIM_NOT_READY = 2;
-const RADIO_STATE_SIM_LOCKED_OR_ABSENT = 3;
-const RADIO_STATE_SIM_READY = 4;
-const RADIO_STATE_RUIM_NOT_READY = 5;
-const RADIO_STATE_RUIM_READY = 6;
-const RADIO_STATE_RUIM_LOCKED_OR_ABSENT = 7;
-const RADIO_STATE_NV_NOT_READY = 8;
-const RADIO_STATE_NV_READY = 9;
-
 const REQUEST_GET_SIM_STATUS = 1;
 const REQUEST_ENTER_SIM_PIN = 2;
 const REQUEST_ENTER_SIM_PUK = 3;
 const REQUEST_ENTER_SIM_PIN2 = 4;
 const REQUEST_ENTER_SIM_PUK2 = 5;
 const REQUEST_CHANGE_SIM_PIN = 6;
 const REQUEST_CHANGE_SIM_PIN2 = 7;
 const REQUEST_ENTER_NETWORK_DEPERSONALIZATION = 8;
@@ -149,16 +136,19 @@ const REQUEST_CDMA_WRITE_SMS_TO_RUIM = 9
 const REQUEST_CDMA_DELETE_SMS_ON_RUIM = 97;
 const REQUEST_DEVICE_IDENTITY = 98;
 const REQUEST_EXIT_EMERGENCY_CALLBACK_MODE = 99;
 const REQUEST_GET_SMSC_ADDRESS = 100;
 const REQUEST_SET_SMSC_ADDRESS = 101;
 const REQUEST_REPORT_SMS_MEMORY_STATUS = 102;
 const REQUEST_REPORT_STK_SERVICE_IS_RUNNING = 103;
 
+const RESPONSE_TYPE_SOLICITED = 0;
+const RESPONSE_TYPE_UNSOLICITED = 1;
+
 const UNSOLICITED_RESPONSE_BASE = 1000;
 const UNSOLICITED_RESPONSE_RADIO_STATE_CHANGED = 1000;
 const UNSOLICITED_RESPONSE_CALL_STATE_CHANGED = 1001;
 const UNSOLICITED_RESPONSE_NETWORK_STATE_CHANGED = 1002;
 const UNSOLICITED_RESPONSE_NEW_SMS = 1003;
 const UNSOLICITED_RESPONSE_NEW_SMS_STATUS_REPORT = 1004;
 const UNSOLICITED_RESPONSE_NEW_SMS_ON_SIM = 1005;
 const UNSOLICITED_ON_USSD = 1006;
@@ -182,8 +172,72 @@ const UNSOLICITED_RESTRICTED_STATE_CHANG
 const UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE = 1024;
 const UNSOLICITED_CDMA_CALL_WAITING = 1025;
 const UNSOLICITED_CDMA_OTA_PROVISION_STATUS = 1026;
 const UNSOLICITED_CDMA_INFO_REC = 1027;
 const UNSOLICITED_OEM_HOOK_RAW = 1028;
 const UNSOLICITED_RINGBACK_TONE = 1029;
 const UNSOLICITED_RESEND_INCALL_MUTE = 1030;
 
+const RADIO_STATE_OFF = 0;
+const RADIO_STATE_UNAVAILABLE = 1;
+const RADIO_STATE_SIM_NOT_READY = 2;
+const RADIO_STATE_SIM_LOCKED_OR_ABSENT = 3;
+const RADIO_STATE_SIM_READY = 4;
+const RADIO_STATE_RUIM_NOT_READY = 5;
+const RADIO_STATE_RUIM_READY = 6;
+const RADIO_STATE_RUIM_LOCKED_OR_ABSENT = 7;
+const RADIO_STATE_NV_NOT_READY = 8;
+const RADIO_STATE_NV_READY = 9;
+
+const CARD_MAX_APPS = 8;
+
+const CALL_STATE_ACTIVE = 0;
+const CALL_STATE_HOLDING = 1;
+const CALL_STATE_DIALING = 2;
+const CALL_STATE_ALERTING = 3;
+const CALL_STATE_INCOMING = 4;
+const CALL_STATE_WAITING = 5;
+
+const TOA_INTERNATIONAL = 0x91;
+const TOA_UNKNOWN = 0x81;
+
+const CALL_PRESENTATION_ALLOWED = 0;
+const CALL_PRESENTATION_RESTRICTED = 1;
+const CALL_PRESENTATION_UNKNOWN = 2;
+const CALL_PRESENTATION_PAYPHONE = 3;
+
+
+/**
+ * DOM constants
+ */
+
+const DOM_RADIOSTATE_UNAVAILABLE   = "unavailable";
+const DOM_RADIOSTATE_OFF           = "off";
+const DOM_RADIOSTATE_READY         = "ready";
+
+const DOM_CARDSTATE_UNAVAILABLE    = "unavailable";
+const DOM_CARDSTATE_ABSENT         = "absent";
+const DOM_CARDSTATE_PIN_REQUIRED   = "pin_required";
+const DOM_CARDSTATE_PUK_REQUIRED   = "puk_required";
+const DOM_CARDSTATE_NETWORK_LOCKED = "network_locked";
+const DOM_CARDSTATE_NOT_READY      = "not_ready";
+const DOM_CARDSTATE_READY          = "ready";
+
+const DOM_CALL_READYSTATE_DIALING        = "dialing";
+const DOM_CALL_READYSTATE_RINGING        = "ringing";
+const DOM_CALL_READYSTATE_BUSY           = "busy";
+const DOM_CALL_READYSTATE_CONNECTING     = "connecting";
+const DOM_CALL_READYSTATE_CONNECTED      = "connected";
+const DOM_CALL_READYSTATE_DISCONNECTING  = "disconnecting";
+const DOM_CALL_READYSTATE_DISCONNECTED   = "disconnected";
+const DOM_CALL_READYSTATE_INCOMING       = "incoming";
+const DOM_CALL_READYSTATE_HOLDING        = "holding";
+const DOM_CALL_READYSTATE_HELD           = "held";
+
+const RIL_TO_DOM_CALL_STATE = [
+  DOM_CALL_READYSTATE_CONNECTED, // CALL_READYSTATE_ACTIVE
+  DOM_CALL_READYSTATE_HELD,      // CALL_READYSTATE_HOLDING
+  DOM_CALL_READYSTATE_DIALING,   // CALL_READYSTATE_DIALING
+  DOM_CALL_READYSTATE_RINGING,   // CALL_READYSTATE_ALERTING
+  DOM_CALL_READYSTATE_INCOMING,  // CALL_READYSTATE_INCOMING
+  DOM_CALL_READYSTATE_HELD       // CALL_READYSTATE_WAITING (XXX is this right?)
+];
--- a/dom/telephony/ril_worker.js
+++ b/dom/telephony/ril_worker.js
@@ -66,19 +66,16 @@ importScripts("ril_consts.js");
 const DEBUG = true;
 
 const INT32_MAX   = 2147483647;
 const UINT8_SIZE  = 1;
 const UINT16_SIZE = 2;
 const UINT32_SIZE = 4;
 const PARCEL_SIZE_SIZE = UINT32_SIZE;
 
-const RESPONSE_TYPE_SOLICITED = 0;
-const RESPONSE_TYPE_UNSOLICITED = 1;
-
 /**
  * This object contains helpers buffering incoming data & deconstructing it
  * into parcels as well as buffering outgoing data & constructing parcels.
  * For that it maintains two buffers and corresponding uint8 views, indexes.
  *
  * The incoming buffer is a circular buffer where we store incoming data.
  * As soon as a complete parcel is received, it is processed right away, so
  * the buffer only needs to be large enough to hold one parcel.
@@ -429,34 +426,36 @@ let Buf = {
   },
 
   /**
    * Process one parcel.
    */
 
   processParcel: function processParcel() {
     let response_type = this.readUint32();
-    let length = this.readIncoming - 2 * UINT32_SIZE;
+    let length = this.readIncoming - UINT32_SIZE;
 
     let request_type;
     if (response_type == RESPONSE_TYPE_SOLICITED) {
       let token = this.readUint32();
       let error = this.readUint32();
+      length -= 2 * UINT32_SIZE;
       request_type = this.tokenRequestMap[token];
       if (error) {
         //TODO
         debug("Received error " + error + " for solicited parcel type " +
               request_type);
         return;
       }
       debug("Solicited response for request type " + request_type +
             ", token " + token);
       delete this.tokenRequestMap[token];
     } else if (response_type == RESPONSE_TYPE_UNSOLICITED) {
       request_type = this.readUint32();
+      length -= UINT32_SIZE;
       debug("Unsolicited response for request type " + request_type);
     } else {
       debug("Unknown response type: " + response_type);
       return;
     }
 
     RIL.handleParcel(request_type, length);
   },
@@ -621,16 +620,43 @@ let RIL = {
     Buf.writeUint32(uusInfo || 0);
 	// TODO Why do we need this extra 0? It was put it in to make this
 	// match the format of the binary message.
 	Buf.writeUint32(0);
     Buf.sendParcel();
   },
 
   /**
+   * Hang up the phone.
+   *
+   * @param index
+   *        Call index (1-based) as reported by REQUEST_GET_CURRENT_CALLS.
+   */
+  hangUp: function hangUp(index) {
+    Buf.newParcel(REQUEST_HANGUP);
+    Buf.writeUint32(1);
+    Buf.writeUint32(index);
+    Buf.sendParcel();
+  },
+
+  /**
+   * Answer an incoming call.
+   */
+  answerCall: function answerCall() {
+    Buf.simpleRequest(REQUEST_ANSWER);
+  },
+
+  /**
+   * Reject an incoming call.
+   */
+  rejectCall: function rejectCall() {
+    Buf.simpleRequest(REQUEST_UDUB);
+  },
+
+  /**
    * Send an SMS.
    *
    * @param smscPDU
    *        String containing the SMSC PDU in hex format.
    * @param pdu
    *        String containing the PDU in hex format.
    */
   sendSMS: function sendSMS(smscPDU, pdu) {
@@ -693,67 +719,83 @@ RIL[REQUEST_ENTER_SIM_PIN] = function RE
   Phone.onEnterICCPIN(response);
 };
 RIL[REQUEST_ENTER_SIM_PUK] = null;
 RIL[REQUEST_ENTER_SIM_PIN2] = null;
 RIL[REQUEST_ENTER_SIM_PUK2] = null;
 RIL[REQUEST_CHANGE_SIM_PIN] = null;
 RIL[REQUEST_CHANGE_SIM_PIN2] = null;
 RIL[REQUEST_ENTER_NETWORK_DEPERSONALIZATION] = null;
-RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS() {
-  let calls = [];
-  let calls_length = Buf.readUint32();
+RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS(length) {
+  let calls_length = 0;
+  // The RIL won't even send us the length integer if there are no active calls.
+  // So only read this integer if the parcel actually has it.
+  if (length) {
+    calls_length = Buf.readUint32();
+  }
+  if (!calls_length) {
+    Phone.onCurrentCalls(null);
+    return;
+  }
 
+  let calls = {};
   for (let i = 0; i < calls_length; i++) {
-    let dc = {
-      state:              Buf.readUint32(), // CALLSTATE_* constants
-      index:              Buf.readUint32(),
+    let call = {
+      state:              Buf.readUint32(), // CALL_STATE_*
+      index:              Buf.readUint32(), // GSM index (1-based)
       toa:                Buf.readUint32(),
       isMpty:             Boolean(Buf.readUint32()),
       isMT:               Boolean(Buf.readUint32()),
       als:                Buf.readUint32(),
       isVoice:            Boolean(Buf.readUint32()),
       isVoicePrivacy:     Boolean(Buf.readUint32()),
       somethingOrOther:   Buf.readUint32(), //XXX TODO whatziz? not in ril.h, but it's in the output...
       number:             Buf.readString(), //TODO munge with TOA
-      numberPresentation: Buf.readUint32(), // Connection.PRESENTATION XXX TODO
+      numberPresentation: Buf.readUint32(), // CALL_PRESENTATION_*
       name:               Buf.readString(),
       namePresentation:   Buf.readUint32(),
       uusInfo:            null
     };
     let uusInfoPresent = Buf.readUint32();
     if (uusInfoPresent == 1) {
-      dc.uusInfo = {
+      call.uusInfo = {
         type:     Buf.readUint32(),
         dcs:      Buf.readUint32(),
         userData: null //XXX TODO byte array?!?
       };
     }
-    calls.push(dc);
+    calls[call.index] = call;
   }
-
   Phone.onCurrentCalls(calls);
 };
-RIL[REQUEST_DIAL] = null;
+RIL[REQUEST_DIAL] = function REQUEST_DIAL(length) {
+  Phone.onDial();
+};
 RIL[REQUEST_GET_IMSI] = function REQUEST_GET_IMSI(length) {
-  let imsi = Buf.readString(length);
+  let imsi = Buf.readString();
   Phone.onIMSI(imsi);
 };
-RIL[REQUEST_HANGUP] = null;
+RIL[REQUEST_HANGUP] = function REQUEST_HANGUP(length) {
+  Phone.onHangUp();
+};
 RIL[REQUEST_HANGUP_WAITING_OR_BACKGROUND] = null;
 RIL[REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND] = null;
 RIL[REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE] = null;
 RIL[REQUEST_SWITCH_HOLDING_AND_ACTIVE] = null;
 RIL[REQUEST_CONFERENCE] = null;
-RIL[REQUEST_UDUB] = null;
+RIL[REQUEST_UDUB] = function REQUEST_UDUB(length) {
+  Phone.onRejectCall();
+};
 RIL[REQUEST_LAST_CALL_FAIL_CAUSE] = null;
 RIL[REQUEST_SIGNAL_STRENGTH] = function REQUEST_SIGNAL_STRENGTH() {
   let strength = {
     // Valid values are (0-31, 99) as defined in TS 27.007 8.5.
-    gsmSignalStrength: Buf.readUint32(),
+    // For some reason we're getting int32s like [99, 4, 0, 0] and [99, 3, 0, 0]
+    // here, so let's strip of anything beyond the first byte.
+    gsmSignalStrength: Buf.readUint32() & 0xff,
     // GSM bit error rate (0-7, 99) as defined in TS 27.007 8.5.
     gsmBitErrorRate:   Buf.readUint32(),
     // The CDMA RSSI value.
     cdmaDBM:           Buf.readUint32(),
     // The CDMA EC/IO.
     cdmaECIO:          Buf.readUint32(),
     // The EVDO RSSI value.
     evdoDBM:           Buf.readUint32(),
@@ -799,17 +841,19 @@ RIL[REQUEST_SMS_ACKNOWLEDGE] = null;
 RIL[REQUEST_GET_IMEI] = function REQUEST_GET_IMEI() {
   let imei = Buf.readString();
   Phone.onIMEI(imei);
 };
 RIL[REQUEST_GET_IMEISV] = function REQUEST_GET_IMEISV() {
   let imeiSV = Buf.readString();
   Phone.onIMEISV(imeiSV);
 };
-RIL[REQUEST_ANSWER] = null;
+RIL[REQUEST_ANSWER] = function REQUEST_ANSWER(length) {
+  Phone.onAnswerCall();
+};
 RIL[REQUEST_DEACTIVATE_DATA_CALL] = null;
 RIL[REQUEST_QUERY_FACILITY_LOCK] = null;
 RIL[REQUEST_SET_FACILITY_LOCK] = null;
 RIL[REQUEST_CHANGE_BARRING_PASSWORD] = null;
 RIL[REQUEST_QUERY_NETWORK_SELECTION_MODE] = function REQUEST_QUERY_NETWORK_SELECTION_MODE() {
   let response = Buf.readUint32List();
   Phone.onNetworkSelectionMode(response);
 };
@@ -896,17 +940,25 @@ RIL[UNSOLICITED_SIGNAL_STRENGTH] = funct
 RIL[UNSOLICITED_DATA_CALL_LIST_CHANGED] = null;
 RIL[UNSOLICITED_SUPP_SVC_NOTIFICATION] = null;
 RIL[UNSOLICITED_STK_SESSION_END] = null;
 RIL[UNSOLICITED_STK_PROACTIVE_COMMAND] = null;
 RIL[UNSOLICITED_STK_EVENT_NOTIFY] = null;
 RIL[UNSOLICITED_STK_CALL_SETUP] = null;
 RIL[UNSOLICITED_SIM_SMS_STORAGE_FULL] = null;
 RIL[UNSOLICITED_SIM_REFRESH] = null;
-RIL[UNSOLICITED_CALL_RING] = null;
+RIL[UNSOLICITED_CALL_RING] = function UNSOLICITED_CALL_RING() {
+  let info = {
+    isPresent:  Buf.readUint32(),
+    signalType: Buf.readUint32(),
+    alertPitch: Buf.readUint32(),
+    signal:     Buf.readUint32()
+  };
+  Phone.onCallRing(info);
+};
 RIL[UNSOLICITED_RESPONSE_SIM_STATUS_CHANGED] = null;
 RIL[UNSOLICITED_RESPONSE_CDMA_NEW_SMS] = null;
 RIL[UNSOLICITED_RESPONSE_NEW_BROADCAST_SMS] = null;
 RIL[UNSOLICITED_CDMA_RUIM_SMS_STORAGE_FULL] = null;
 RIL[UNSOLICITED_RESTRICTED_STATE_CHANGED] = null;
 RIL[UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE] = null;
 RIL[UNSOLICITED_CDMA_CALL_WAITING] = null;
 RIL[UNSOLICITED_CDMA_OTA_PROVISION_STATUS] = null;
@@ -956,17 +1008,17 @@ let Phone = {
   /**
    * ICC card status
    */
   iccStatus: null,
 
   /**
    * Active calls
    */
-  calls: null,
+  currentCalls: {},
 
   /**
    * Handlers for messages from the RIL. They all begin with on* and are called
    * from RIL object.
    */
 
   onRadioStateChanged: function onRadioStateChanged(newState) {
     debug("Radio state changed from " + this.radioState + " to " + newState);
@@ -996,78 +1048,131 @@ let Phone = {
       }
       if (cdma) {
         RIL.getDeviceIdentity();
       }
       Buf.simpleRequest(REQUEST_BASEBAND_VERSION);
       RIL.setScreenState(true);
       this.sendDOMMessage({
         type: "radiostatechange",
-        radioState: (newState == RADIO_STATE_OFF) ? "off" : "ready"
+        radioState: (newState == RADIO_STATE_OFF) ?
+                     DOM_RADIOSTATE_OFF : DOM_RADIOSTATE_READY
       });
 
       //XXX TODO For now, just turn the radio on if it's off. for the real
       // deal we probably want to do the opposite: start with a known state
       // when we boot up and let the UI layer control the radio power.
       if (newState == RADIO_STATE_OFF) {
         RIL.setRadioPower(true);
       }
     }
 
     if (newState == RADIO_STATE_UNAVAILABLE) {
       // The radio is no longer available, we need to deal with any
       // remaining pending requests.
       //TODO do that
 
       this.sendDOMMessage({type: "radiostatechange",
-                           radioState: "unavailable"});
+                           radioState: DOM_RADIOSTATE_UNAVAILABLE});
     }
 
     if (newState == RADIO_STATE_SIM_READY  ||
         newState == RADIO_STATE_RUIM_READY ||
         newState == RADIO_STATE_NV_READY) {
       // The ICC card has become available. Get all the things.
       RIL.getICCStatus();
       this.requestNetworkInfo();
       RIL.getSignalStrength();
       this.sendDOMMessage({type: "cardstatechange",
-                           cardState: "ready"});
+                           cardState: DOM_CARDSTATE_READY});
     }
     if (newState == RADIO_STATE_SIM_LOCKED_OR_ABSENT  ||
         newState == RADIO_STATE_RUIM_LOCKED_OR_ABSENT) {
       RIL.getICCStatus();
       this.sendDOMMessage({type: "cardstatechange",
-                           cardState: "unavailable"});
+                           cardState: DOM_CARDSTATE_UNAVAILABLE});
     }
 
     let wasOn = this.radioState != RADIO_STATE_OFF &&
                 this.radioState != RADIO_STATE_UNAVAILABLE;
     let isOn = newState != RADIO_STATE_OFF &&
                newState != RADIO_STATE_UNAVAILABLE;
     if (!wasOn && isOn) {
       //TODO
     }
     if (wasOn && !isOn) {
       //TODO
     }
 
     this.radioState = newState;
   },
 
-  onCurrentCalls: function onCurrentCalls(calls) {
-    debug("onCurrentCalls");
-    debug(calls);
-    //TODO 
-    this.sendDOMMessage({type: "callstatechange", callState: calls});
+  onCurrentCalls: function onCurrentCalls(newCalls) {
+    // Go through the calls we currently have on file and see if any of them
+    // changed state. Remove them from the newCalls map as we deal with them
+    // so that only new calls remain in the map after we're done.
+    for each (let currentCall in this.currentCalls) {
+      let callIndex = currentCall.index;
+      let newCall;
+      if (newCalls) {
+        newCall = newCalls[callIndex];
+        delete newCalls[callIndex];
+      }
+
+      if (!newCall) {
+        // Call is no longer reported by the radio. Send disconnected
+        // state change.
+        this.sendDOMMessage({type:      "callstatechange",
+                             callState:  DOM_CALL_READYSTATE_DISCONNECTED,
+                             callIndex:  callIndex,
+                             number:     currentCall.number,
+                             name:       currentCall.name});
+        delete this.currentCalls[currentCall];
+        continue;
+      }
+
+      if (newCall.state == currentCall.state) {
+        continue;
+      }
+
+      this._handleChangedCallState(newCall);
+    }
+
+    // Go through any remaining calls that are new to us.
+    for each (let newCall in newCalls) {
+      if (newCall.isVoice) {
+        this._handleChangedCallState(newCall);
+      }
+    }
+  },
+
+  _handleChangedCallState: function handleChangedCallState(newCall) {
+    // Format international numbers appropriately.
+    if (newCall.number &&
+        newCall.toa == TOA_INTERNATIONAL &&
+        newCall.number[0] != "+") {
+      newCall.number = "+" + newCall.number;
+    }
+    this.currentCalls[newCall.index] = newCall;
+    this.sendDOMMessage({type:      "callstatechange",
+                         callState: RIL_TO_DOM_CALL_STATE[newCall.state],
+                         callIndex: newCall.index,
+                         number:    newCall.number,
+                         name:      newCall.name});
   },
 
   onCallStateChanged: function onCallStateChanged() {
     RIL.getCurrentCalls();
   },
 
+  onCallRing: function onCallRing(info) {
+    debug("onCallRing " + JSON.stringify(info)); //DEBUG
+    RIL.getCurrentCalls();
+  },
+
   onNetworkStateChanged: function onNetworkStateChanged() {
     debug("Network state changed, re-requesting phone state.");
     this.requestNetworkInfo();
   },
 
   onICCStatus: function onICCStatus(iccStatus) {
     debug("SIM card state is " + iccStatus.cardState);
     debug("Universal PIN state is " + iccStatus.universalPINState);
@@ -1126,16 +1231,28 @@ let Phone = {
   },
 
   onSignalStrength: function onSignalStrength(strength) {
     debug("Signal strength " + JSON.stringify(strength));
     this.sendDOMMessage({type: "signalstrengthchange",
                          signalStrength: strength});
   },
 
+  onDial: function onDial() {
+  },
+
+  onHangUp: function onHangUp() {
+  },
+
+  onAnswerCall: function onAnswerCall() {
+  },
+
+  onRejectCall: function onRejectCall() {
+  },
+
   onSendSMS: function onSendSMS(messageRef, ackPDU, errorCode) {
     //TODO
   },
 
 
   /**
    * Outgoing requests to the RIL. These can be triggered from the
    * main thread via messages that look like this:
@@ -1166,16 +1283,42 @@ let Phone = {
    * @param number
    *        String containing the number to dial.
    */
   dial: function dial(options) {
     RIL.dial(options.number, 0, 0);
   },
 
   /**
+   * Hang up a call.
+   *
+   * @param callIndex
+   *        Call index of the call to hang up.
+   */
+  hangUp: function hangUp(options) {
+    //TODO need to check whether call is holding/waiting/background
+    // and then use REQUEST_HANGUP_WAITING_OR_BACKGROUND
+    RIL.hangUp(options.callIndex);
+  },
+
+  /**
+   * Answer an incoming call.
+   */
+  answerCall: function answerCall() {
+    RIL.answerCall();
+  },
+
+  /**
+   * Reject an incoming call.
+   */
+  rejectCall: function rejectCall() {
+    RIL.rejectCall();
+  },
+
+  /**
    * Send an SMS.
    *
    * @param number
    *        String containing the recipient number.
    * @param message
    *        String containing the message text.
    */
   sendSMS: function sendSMS(options) {