DO NOT MERGE 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 cee5332..d4eda16 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -95,6 +95,7 @@
         void onPhoneAccountChanged(Call call);
         void onConferenceableCallsChanged(Call call);
         boolean onCanceledViaNewOutgoingCallBroadcast(Call call);
+        void onHoldToneRequested(Call call);
     }
 
     public abstract static class ListenerBase implements Listener {
@@ -152,6 +153,9 @@
         public boolean onCanceledViaNewOutgoingCallBroadcast(Call call) {
             return false;
         }
+
+        @Override
+        public void onHoldToneRequested(Call call) {}
     }
 
     private final OnQueryCompleteListener mCallerInfoQueryListener =
@@ -332,6 +336,13 @@
     private boolean mIsLocallyDisconnecting = 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.
      *
      * @param context The context.
@@ -1694,4 +1705,34 @@
     public boolean isDisconnected() {
         return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
     }
+
+    /**
+     * 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;
+            // Inform listeners of the fact that a call hold tone was received.  This will trigger
+            // the CallsManager to inform the InCallToneMonitor of the request.
+            for (Listener l : mListeners) {
+                l.onHoldToneRequested(this);
+            }
+        } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
+            mIsRemotelyHeld = false;
+            for (Listener l : mListeners) {
+                l.onHoldToneRequested(this);
+            }
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 2044a2c..d1833af 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -82,6 +82,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";
@@ -415,6 +416,21 @@
         }
     }
 
+    /**
+     * 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);
+        }
+    }
+
     Collection<Call> getCalls() {
         return Collections.unmodifiableCollection(mCalls);
     }
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index 58085a0..ce433a4 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -84,4 +84,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 8e58f22..1e46e07 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -588,6 +588,21 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public void onConnectionEvent(String callId, String event) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.onConnectionEvent(event);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 
     private final Adapter mAdapter = new Adapter();
diff --git a/src/com/android/server/telecom/InCallToneMonitor.java b/src/com/android/server/telecom/InCallToneMonitor.java
index afe0f06..207b19a 100644
--- a/src/com/android/server/telecom/InCallToneMonitor.java
+++ b/src/com/android/server/telecom/InCallToneMonitor.java
@@ -20,14 +20,13 @@
 import android.telecom.Connection;
 import android.telecom.VideoProfile;
 
-import java.util.Collection;
-
 /**
  * Monitors events from CallsManager and plays in-call tones for events which require them, such as
  * different type of call disconnections (busy tone, congestion tone, etc).
  */
 public final class InCallToneMonitor extends CallsManagerListenerBase {
     private final InCallTonePlayer.Factory mPlayerFactory;
+    private InCallTonePlayer mHoldTonePlayer = null;
 
     private final CallsManager mCallsManager;
 
@@ -77,6 +76,9 @@
             if (toneToPlay != InCallTonePlayer.TONE_INVALID) {
                 mPlayerFactory.createPlayer(toneToPlay).startTone();
             }
+        } else {
+            // If foreground call changes state, we may need to start or stop the hold tone.
+            maybePlayHoldTone();
         }
     }
 
@@ -111,4 +113,78 @@
             mPlayerFactory.createPlayer(InCallTonePlayer.TONE_VIDEO_UPGRADE).startTone();
         }
     }
+
+    /**
+     * 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 onCallAdded(Call call) {
+        maybePlayHoldTone();
+    }
+
+    @Override
+    public void onCallRemoved(Call call) {
+        maybePlayHoldTone();
+    }
+
+    @Override
+    public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
+        maybePlayHoldTone();
+    }
+
+    /**
+     * 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 = mCallsManager.getForegroundCall();
+        // If there is no foreground call, no hold tone should play.
+        if (foregroundCall == null) {
+            // Clean up mPlayHoldToneId
+            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();
+    }
 }