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();
+ }
}