Implement the short-burst DTMF tones for CDMA calls.

Changes:
- Add code to respect "burst" mode dtmf
  - includes adding code to read when radio has completed the dtmf tone
    and adding a queue of incoming dtmf presses while a dtmf tone is in
    progress.
- Add a config overlay for mcc311/mnc480 to enable the setting in
  dialer.

Bug: 17431108
Change-Id: Icf1ec86ab54783e04b37104f6f3c327b959c4c0b
diff --git a/res/values-mcc311-mnc480/config.xml b/res/values-mcc311-mnc480/config.xml
new file mode 100644
index 0000000..7c269e0
--- /dev/null
+++ b/res/values-mcc311-mnc480/config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Phone app resources that may need to be customized
+     for different hardware or product builds. -->
+<resources>
+    <!-- Flag indicating if dtmf tone type is enabled -->
+    <bool name="dtmf_type_enabled">true</bool>
+</resources>
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index 12ccbce..cb99b55 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -18,13 +18,18 @@
 
 import android.os.Handler;
 import android.os.Message;
-import android.telecomm.PhoneCapabilities;
 
+import android.provider.Settings;
+import android.telecomm.PhoneCapabilities;
 import android.telephony.DisconnectCause;
 
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
+import com.android.phone.Constants;
+
+import java.util.LinkedList;
+import java.util.Queue;
 
 /**
  * Manages a single phone call handled by CDMA.
@@ -32,6 +37,7 @@
 final class CdmaConnection extends TelephonyConnection {
 
     private static final int MSG_CALL_WAITING_MISSED = 1;
+    private static final int MSG_DTMF_SEND_CONFIRMATION = 2;
     private static final int TIMEOUT_CALL_WAITING_MILLIS = 20 * 1000;
 
     private final Handler mHandler = new Handler() {
@@ -43,6 +49,9 @@
                 case MSG_CALL_WAITING_MISSED:
                     hangupCallWaiting(DisconnectCause.INCOMING_MISSED);
                     break;
+                case MSG_DTMF_SEND_CONFIRMATION:
+                    handleBurstDtmfConfirmation();
+                    break;
                 default:
                     break;
             }
@@ -55,7 +64,11 @@
      */
     private final boolean mAllowMute;
     private final boolean mIsOutgoing;
+    // Queue of pending short-DTMF characters.
+    private final Queue<Character> mDtmfQueue = new LinkedList<>();
 
+    // Indicates that the DTMF confirmation from telephony is pending.
+    private boolean mDtmfBurstConfirmationPending = false;
     private boolean mIsCallWaiting;
 
     CdmaConnection(Connection connection, boolean allowMute, boolean isOutgoing) {
@@ -71,20 +84,21 @@
     /** {@inheritDoc} */
     @Override
     public void onPlayDtmfTone(char digit) {
-        // TODO: There are conditions where we should play dtmf tones with different
-        // timeouts.
-        // TODO: We get explicit response from the phone via a Message when the burst
-        // tone has completed. During this time we can get subsequent requests. We need to stop
-        // passing in null as the message and start handling it to implement a queue.
-        if (getPhone() != null) {
-            getPhone().sendBurstDtmf(Character.toString(digit), 0, 0, null);
+        if (useBurstDtmf()) {
+            Log.i(this, "sending dtmf digit as burst");
+            sendShortDtmfToNetwork(digit);
+        } else {
+            Log.i(this, "sending dtmf digit directly");
+            getPhone().startDtmf(digit);
         }
     }
 
     /** {@inheritDoc} */
     @Override
     public void onStopDtmfTone() {
-        // no-op, we only play timed dtmf tones for cdma.
+        if (!useBurstDtmf()) {
+            getPhone().stopDtmf();
+        }
     }
 
     @Override
@@ -168,4 +182,53 @@
             setDisconnected(disconnectCause, null);
         }
     }
+
+    /**
+     * Read the settings to determine which type of DTMF method this CDMA phone calls.
+     */
+    private boolean useBurstDtmf() {
+        int dtmfTypeSetting = Settings.System.getInt(
+                getPhone().getContext().getContentResolver(),
+                Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
+                Constants.DTMF_TONE_TYPE_NORMAL);
+        return dtmfTypeSetting == Constants.DTMF_TONE_TYPE_NORMAL;
+    }
+
+    private void sendShortDtmfToNetwork(char digit) {
+        synchronized(mDtmfQueue) {
+            if (mDtmfBurstConfirmationPending) {
+                mDtmfQueue.add(new Character(digit));
+            } else {
+                sendBurstDtmfStringLocked(Character.toString(digit));
+            }
+        }
+    }
+
+    private void sendBurstDtmfStringLocked(String dtmfString) {
+        getPhone().sendBurstDtmf(
+                dtmfString, 0, 0, mHandler.obtainMessage(MSG_DTMF_SEND_CONFIRMATION));
+        mDtmfBurstConfirmationPending = true;
+    }
+
+    private void handleBurstDtmfConfirmation() {
+        String dtmfDigits = null;
+        synchronized(mDtmfQueue) {
+            mDtmfBurstConfirmationPending = false;
+            if (!mDtmfQueue.isEmpty()) {
+                StringBuilder builder = new StringBuilder(mDtmfQueue.size());
+                while (!mDtmfQueue.isEmpty()) {
+                    builder.append(mDtmfQueue.poll());
+                }
+                dtmfDigits = builder.toString();
+
+                // It would be nice to log the digit, but since DTMF digits can be passwords
+                // to things, or other secure account numbers, we want to keep it away from
+                // the logs.
+                Log.i(this, "%d dtmf character[s] removed from the queue", dtmfDigits.length());
+            }
+            if (dtmfDigits != null) {
+                sendBurstDtmfStringLocked(dtmfDigits);
+            }
+        }
+    }
 }