Play DTMF tones when sending DTMF tones during post dial wait.

Regression from KK -- when adding additional digits to a number limited
with a wait character (";"), the local DTMF player should make those
sounds audible to the caller.

Bug: 18644688
Change-Id: Ib04658316f725146a65b399d3f477791260c3f5a
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index d3eddb5..82d2d78 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -73,6 +73,7 @@
         void onFailedUnknownCall(Call call);
         void onRingbackRequested(Call call, boolean ringbackRequested);
         void onPostDialWait(Call call, String remaining);
+        void onPostDialChar(Call call, char nextChar);
         void onConnectionCapabilitiesChanged(Call call);
         void onParentChanged(Call call);
         void onChildrenChanged(Call call);
@@ -109,6 +110,8 @@
         @Override
         public void onPostDialWait(Call call, String remaining) {}
         @Override
+        public void onPostDialChar(Call call, char nextChar) {}
+        @Override
         public void onConnectionCapabilitiesChanged(Call call) {}
         @Override
         public void onParentChanged(Call call) {}
@@ -980,6 +983,12 @@
         }
     }
 
+    void onPostDialChar(char nextChar) {
+        for (Listener l : mListeners) {
+            l.onPostDialChar(this, nextChar);
+        }
+    }
+
     void postDialContinue(boolean proceed) {
         mConnectionService.onPostDialContinue(this, proceed);
     }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index dc65c9e..cfd4641 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -33,6 +33,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
 
 import com.android.internal.util.IndentingPrintWriter;
@@ -136,6 +137,8 @@
      */
     private Call mForegroundCall;
 
+    private Runnable mStopTone;
+
     /** Singleton accessor. */
     static CallsManager getInstance() {
         return sInstance;
@@ -264,6 +267,40 @@
     }
 
     @Override
+    public void onPostDialChar(final Call call, char nextChar) {
+        if (PhoneNumberUtils.is12Key(nextChar)) {
+            // Play tone if it is one of the dialpad digits, canceling out the previously queued
+            // up stopTone runnable since playing a new tone automatically stops the previous tone.
+            if (mStopTone != null) {
+                mHandler.removeCallbacks(mStopTone);
+            }
+
+            mDtmfLocalTonePlayer.playTone(call, nextChar);
+
+            mStopTone = new Runnable() {
+                @Override
+                public void run() {
+                    // Set a timeout to stop the tone in case there isn't another tone to follow.
+                    mDtmfLocalTonePlayer.stopTone(call);
+                }
+            };
+            mHandler.postDelayed(
+                    mStopTone,
+                    Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver()));
+        } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT ||
+                nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) {
+            // Stop the tone if a tone is playing, removing any other stopTone callbacks since
+            // the previous tone is being stopped anyway.
+            if (mStopTone != null) {
+                mHandler.removeCallbacks(mStopTone);
+            }
+            mDtmfLocalTonePlayer.stopTone(call);
+        } else {
+            Log.w(this, "onPostDialChar: invalid value %d", nextChar);
+        }
+    }
+
+    @Override
     public void onParentChanged(Call call) {
         // parent-child relationship affects which call should be foreground, so do an update.
         updateCallsManagerState();
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index c346272..3f16108 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -82,6 +82,7 @@
     private static final int MSG_SET_VIDEO_STATE = 19;
     private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20;
     private static final int MSG_ADD_EXISTING_CONNECTION = 21;
+    private static final int MSG_ON_POST_DIAL_CHAR = 22;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -248,6 +249,21 @@
                     }
                     break;
                 }
+                case MSG_ON_POST_DIAL_CHAR: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        call = mCallIdMapper.getCall(args.arg1);
+                        if (call != null) {
+                            char nextChar = (char) args.argi1;
+                            call.onPostDialChar(nextChar);
+                        } else {
+                            //Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
+                        }
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                }
                 case MSG_QUERY_REMOTE_CALL_SERVICES: {
                     queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
                     break;
@@ -488,6 +504,17 @@
         }
 
         @Override
+        public void onPostDialChar(String callId, char nextChar) throws RemoteException {
+            logIncoming("onPostDialChar %s %s", callId, nextChar);
+            if (mCallIdMapper.isValidCallId(callId)) {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.argi1 = nextChar;
+                mHandler.obtainMessage(MSG_ON_POST_DIAL_CHAR, args).sendToTarget();
+            }
+        }
+
+        @Override
         public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
             logIncoming("queryRemoteCSs");
             mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
diff --git a/src/com/android/server/telecom/Timeouts.java b/src/com/android/server/telecom/Timeouts.java
index 47c4224..75b94c2 100644
--- a/src/com/android/server/telecom/Timeouts.java
+++ b/src/com/android/server/telecom/Timeouts.java
@@ -62,4 +62,14 @@
     public static long getNewOutgoingCallCancelMillis(ContentResolver contentResolver) {
         return get(contentResolver, "new_outgoing_call_cancel_ms", 300L);
     }
+
+    /**
+     * Returns the amount of time to play each DTMF tone after post dial continue.
+     * This timeout allows the current tone to play for a certain amount of time before either being
+     * interrupted by the next tone or terminated.
+     */
+    public static long getDelayBetweenDtmfTonesMillis(ContentResolver contentResolver) {
+        return get(contentResolver, "delay_between_dtmf_tones_ms", 300L);
+    }
+
 }