Listen for ConnectionEvent and use the InCallToneMonitor to play the tone

BUG=25357778

Change-Id: Iad4f36f9b01670a2f188e726895a8349aeeb1502
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 7b342bc..fc124a4 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -104,6 +104,7 @@
         void onPhoneAccountChanged(Call call);
         void onConferenceableCallsChanged(Call call);
         boolean onCanceledViaNewOutgoingCallBroadcast(Call call);
+        void onHoldToneRequested(Call call);
     }
 
     public abstract static class ListenerBase implements Listener {
@@ -161,6 +162,9 @@
         public boolean onCanceledViaNewOutgoingCallBroadcast(Call call) {
             return false;
         }
+
+        @Override
+        public void onHoldToneRequested(Call call) {}
     }
 
     private final OnQueryCompleteListener mCallerInfoQueryListener =
@@ -365,6 +369,14 @@
     // Set to true once the NewOutgoingCallIntentBroadcast comes back and is processed.
     private boolean mIsNewOutgoingCallIntentBroadcastDone = false;
 
+
+    /**
+     * Indicates whether the call is remotely held.  A call is considered remotely held when
+     * {@link #onConnectionEvent(String)} receives the {@link Connection#EVENT_ON_HOLD_TONE_START}
+     * event.
+     */
+    private boolean mIsRemotelyHeld = false;
+
     /**
      * Persists the specified parameters and initializes the new instance.
      *
@@ -1921,4 +1933,36 @@
     public void setNewOutgoingCallIntentBroadcastIsDone() {
         mIsNewOutgoingCallIntentBroadcastDone = true;
     }
+
+    /**
+     * Determines if the call has been held by the remote party.
+     *
+     * @return {@code true} if the call is remotely held, {@code false} otherwise.
+     */
+    public boolean isRemotelyHeld() {
+        return mIsRemotelyHeld;
+    }
+
+    /**
+     * Handles Connection events received from a {@link ConnectionService}.
+     *
+     * @param event The event.
+     */
+    public void onConnectionEvent(String event) {
+        if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) {
+            mIsRemotelyHeld = true;
+            Log.event(this, Log.Events.REMOTELY_HELD);
+            // Inform listeners of the fact that a call hold tone was received.  This will trigger
+            // the CallAudioManager to play a tone via the InCallTonePlayer.
+            for (Listener l : mListeners) {
+                l.onHoldToneRequested(this);
+            }
+        } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
+            mIsRemotelyHeld = false;
+            Log.event(this, Log.Events.REMOTELY_UNHELD);
+            for (Listener l : mListeners) {
+                l.onHoldToneRequested(this);
+            }
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 1a5cc7b..07c1440 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -55,6 +55,7 @@
 
     private Call mForegroundCall;
     private boolean mIsTonePlaying = false;
+    private InCallTonePlayer mHoldTonePlayer;
 
     public CallAudioManager(CallAudioRouteStateMachine callAudioRouteStateMachine,
             CallsManager callsManager,
@@ -213,6 +214,19 @@
         }
     }
 
+    /**
+     * Play or stop a call hold tone for a call.  Triggered via
+     * {@link Connection#sendConnectionEvent(String)} when the
+     * {@link Connection#EVENT_ON_HOLD_TONE_START} event or
+     * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the
+     *
+     * @param call The call which requested the hold tone.
+     */
+    @Override
+    public void onHoldToneRequested(Call call) {
+        maybePlayHoldTone();
+    }
+
     @Override
     public void onIsVoipAudioModeChanged(Call call) {
         if (call != mForegroundCall) {
@@ -486,6 +500,7 @@
 
         if (mForegroundCall != oldForegroundCall) {
             mDtmfLocalTonePlayer.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
+            maybePlayHoldTone();
         }
     }
 
@@ -554,6 +569,51 @@
         mRingbackPlayer.stopRingbackForCall(call);
     }
 
+    /**
+     * Determines if a hold tone should be played and then starts or stops it accordingly.
+     */
+    private void maybePlayHoldTone() {
+        if (shouldPlayHoldTone()) {
+            if (mHoldTonePlayer == null) {
+                mHoldTonePlayer = mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
+                mHoldTonePlayer.start();
+            }
+        } else {
+            if (mHoldTonePlayer != null) {
+                mHoldTonePlayer.stopTone();
+                mHoldTonePlayer = null;
+            }
+        }
+    }
+
+    /**
+     * Determines if a hold tone should be played.
+     * A hold tone should be played only if foreground call is equals with call which is
+     * remotely held.
+     *
+     * @return {@code true} if the the hold tone should be played, {@code false} otherwise.
+     */
+    private boolean shouldPlayHoldTone() {
+        Call foregroundCall = getForegroundCall();
+        // If there is no foreground call, no hold tone should play.
+        if (foregroundCall == null) {
+            return false;
+        }
+
+        // If another call is ringing, no hold tone should play.
+        if (mCallsManager.hasRingingCall()) {
+            return false;
+        }
+
+        // If the foreground call isn't active, no hold tone should play. This might happen, for
+        // example, if the user puts a remotely held call on hold itself.
+        if (!foregroundCall.isActive()) {
+            return false;
+        }
+
+        return foregroundCall.isRemotelyHeld();
+    }
+
     private void dumpCallsInCollection(IndentingPrintWriter pw, Collection<Call> calls) {
         for (Call call : calls) {
             if (call != null) pw.println(call.getId());
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index fbb35ba..ce18f8a 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -91,6 +91,7 @@
         void onVideoStateChanged(Call call);
         void onCanAddCallChanged(boolean canAddCall);
         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
+        void onHoldToneRequested(Call call);
     }
 
     private static final String TAG = "CallsManager";
@@ -522,11 +523,25 @@
         }
     }
 
-    @VisibleForTesting
     public Collection<Call> getCalls() {
         return Collections.unmodifiableCollection(mCalls);
     }
 
+    /**
+     * Play or stop a call hold tone for a call.  Triggered via
+     * {@link Connection#sendConnectionEvent(String)} when the
+     * {@link Connection#EVENT_ON_HOLD_TONE_START} event or
+     * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the
+     *
+     * @param call The call which requested the hold tone.
+     */
+    @Override
+    public void onHoldToneRequested(Call call) {
+        for (CallsManagerListener listener : mListeners) {
+            listener.onHoldToneRequested(call);
+        }
+    }
+
     @VisibleForTesting
     public Call getForegroundCall() {
         if (mCallAudioManager == null) {
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index 50716d5..71d6c53 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -80,4 +80,8 @@
     public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) {
 
     }
+
+    @Override
+    public void onHoldToneRequested(Call call) {
+    }
 }
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 56fbd5c..1feb356 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -587,6 +587,23 @@
                 Log.endSession();
             }
         }
+
+        @Override
+        public void onConnectionEvent(String callId, String event) {
+            Log.startSession("CSW.oCE");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.onConnectionEvent(event);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+                Log.endSession();
+            }
+        }
     }
 
     private final Adapter mAdapter = new Adapter();
diff --git a/src/com/android/server/telecom/Log.java b/src/com/android/server/telecom/Log.java
index 5e2dc9d..8daf6c1 100644
--- a/src/com/android/server/telecom/Log.java
+++ b/src/com/android/server/telecom/Log.java
@@ -101,6 +101,8 @@
         public static final String BLOCK_CHECK_INITIATED = "BLOCK_CHECK_INITIATED";
         public static final String BLOCK_CHECK_TIMED_OUT = "BLOCK_CHECK_TIMED_OUT";
         public static final String BLOCK_CHECK_FINISHED = "BLOCK_CHECK_FINISHED";
+        public static final String REMOTELY_HELD = "REMOTELY_HELD";
+        public static final String REMOTELY_UNHELD = "REMOTELY_UNHELD";
 
         /**
          * Maps from a request to a response.  The same event could be listed as the