IMS-VT: Support QTI audio routing policy for modify/merge calls
- Audio policy is to turn SPEAKER on if the user makes any action that
indicates a transition to video calls or route to EARPICE for voice
calls. For all other non-user initiated transitions, no changes are
made in the audio routing path.
- Handle all user initiated (MO) upgrade/downgrade requests, merge calls
, user accepted (MT) upgrade requests and incoming call in this change.
Other use cases like outgoing calls, add call, call waiting are
handled in Telecomm layer.
- Only make changes to audio route if Bluetooth or wired headset is
not connected.
IMS-VT: Route to speaker if current route is not BT, headset or speaker
- Route audio to speaker(/earpiece) only if current audio route is
not bluetooth, wired headset or speaker(/earpiece)
Change-Id: Id5b1910487d8f365ca5c449c3e62ef24d6f3b530
CRs-Fixed: 879183 932268
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index dae84e7..05c49fe 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -327,7 +327,7 @@
InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context);
} else {
Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState);
- TelecomAdapter.getInstance().answerCall(mCall[phoneId].getId(), videoState);
+ InCallPresenter.getInstance().answerIncomingCall(context, videoState);
}
}
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index a81d88f..59e1ce2 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -279,6 +279,7 @@
}
}
TelecomAdapter.getInstance().merge(mCall.getId());
+ InCallAudioManager.getInstance().onMergeClicked();
}
public void addParticipantClicked() {
diff --git a/InCallUI/src/com/android/incallui/InCallAudioManager.java b/InCallUI/src/com/android/incallui/InCallAudioManager.java
new file mode 100644
index 0000000..760784c
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallAudioManager.java
@@ -0,0 +1,194 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.incallui;
+
+import android.telecom.CallAudioState;
+import android.telecom.VideoProfile;
+
+/**
+ * This class implements QTI audio routing policy for upgrade/downgrade/merge call use cases
+ * based on the audio policy below.
+ *
+ * Audio policy for any user initiated action like making a video call, accepting an upgrade
+ * request or sending an upgrade/downgrade request is as follows : If any of the above user actions
+ * indicate a transition to a video call, we route audio to SPEAKER. If any of the above user
+ * actions indicate a voice call, we route to EARPIECE. For all other non-user initiated actions
+ * like an incoming downgrade request, we don't change the audio path and respect user's choice.
+ * For merge calls, we route to SPEAKER only if one of the calls being merged is a video call.
+ *
+ * Audio path routing for outgoing calls, incoming calls, add call and call waiting are handled
+ * in Telecom layer. Same audio policy is followed for those as well. Any user initiated action
+ * indicating a origin/acceptance of video call routes audio to SPEAKER or to EARPIECE for voice
+ * calls.
+ */
+public class InCallAudioManager {
+
+ private static InCallAudioManager sInCallAudioManager;
+
+ private final static String LOG_TAG = "InCallAudioManager";
+
+ /**
+ * Private constructor. Must use getInstance() to get this singleton.
+ */
+ private InCallAudioManager() {
+ }
+
+ /**
+ * This method returns a singleton instance of {@class InCallAudioManager}
+ */
+ public static synchronized InCallAudioManager getInstance() {
+ if (sInCallAudioManager == null) {
+ sInCallAudioManager = new InCallAudioManager();
+ }
+ return sInCallAudioManager;
+ }
+
+ /**
+ * Called when user sends an upgrade/downgrade request. Route audio to speaker if the user
+ * sends an upgrade request to Video (bidirectional, transmit or receive) otherwise route
+ * audio to earpiece if it's a downgrade request.
+ */
+ public void onModifyCallClicked(final Call call,final int newVideoState) {
+ Log.v(this, "onModifyCallClicked: Call = " + call + "new video state = " +
+ newVideoState);
+
+ if (!VideoProfile.isVideo(newVideoState)) {
+ enableEarpiece();
+ } else if (canEnableSpeaker(call.getVideoState(),newVideoState)) {
+ enableSpeaker();
+ }
+ }
+
+ /**
+ * Called when user accepts an upgrade request. Route audio to speaker if the user accepts an
+ * upgrade request to Video (bidirectional, transmit or receive)
+ */
+ public void onAcceptUpgradeRequest(final Call call, final int videoState) {
+ Log.v(this, "onAcceptUpgradeRequest: Call = " + call + "video state = " +
+ videoState);
+ if (canEnableSpeaker(call.getVideoState(), videoState)) {
+ enableSpeaker();
+ }
+ }
+
+ /**
+ * Called when user accepts an incoming call. Route audio to speaker if the user accepts an
+ * incoming call as Video (bidirectional, transmit or receive) or to earpiece is the user
+ * accepts the call as Voice
+ */
+ public void onAnswerIncomingCall(final Call call, final int videoState) {
+ Log.v(this, "onAnswerIncomingCall: Call = " + call + "video state = " +
+ videoState);
+ if (!VideoProfile.isVideo(videoState)) {
+ enableEarpiece();
+ } else {
+ enableSpeaker();
+ }
+ }
+
+ /**
+ * Called when user clicks on merge calls from the UI. Route audio to speaker if one of the
+ * calls being merged is a video call.
+ */
+ public void onMergeClicked() {
+ Log.v(this, "onMergeClicked");
+
+ if (VideoUtils.isVideoCall(CallList.getInstance().getBackgroundCall()) ||
+ VideoUtils.isVideoCall(CallList.getInstance().getActiveCall())) {
+ enableSpeaker();
+ }
+ }
+
+ /**
+ * Determines if the speakerphone should be automatically enabled for the call. Speakerphone
+ * should be enabled if the call is a video call and if the adb property
+ * "persist.radio.call.audio.output" is true.
+ *
+ * @param videoState The video state of the call.
+ * @return {@code true} if the speakerphone should be enabled.
+ */
+ private static boolean canEnableSpeaker(int oldVideoState, int newVideoState) {
+ return !VideoProfile.isVideo(oldVideoState) &&
+ VideoProfile.isVideo(newVideoState) && isSpeakerEnabledForVideoCalls();
+ }
+
+ /**
+ * Determines if the speakerphone should be automatically enabled for video calls.
+ *
+ * @return {@code true} if the speakerphone should automatically be enabled.
+ */
+ private static boolean isSpeakerEnabledForVideoCalls() {
+ // TODO: we can't access adb properties from InCallUI. Need to refactor this.
+ return true;
+ }
+
+ /**
+ * Routes the call to the earpiece if audio is not being already routed to Earpiece and if
+ * bluetooth or wired headset is not connected.
+ */
+ private static void enableEarpiece() {
+ final TelecomAdapter telecomAdapter = TelecomAdapter.getInstance();
+ if (telecomAdapter == null) {
+ Log.e(LOG_TAG, "enableEarpiece: TelecomAdapter is null");
+ return;
+ }
+
+ final int currentAudioMode = AudioModeProvider.getInstance().getAudioMode();
+ Log.v(LOG_TAG, "enableEarpiece: Current audio mode is - " + currentAudioMode);
+
+ if (QtiCallUtils.isNotEnabled(CallAudioState.ROUTE_EARPIECE |
+ CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET,
+ currentAudioMode)) {
+ Log.v(LOG_TAG, "enableEarpiece: Set audio route to earpiece");
+ telecomAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
+ }
+ }
+
+ /**
+ * Routes the call to the speaker if audio is not being already routed to Speaker and if
+ * bluetooth or wired headset is not connected.
+ */
+ private static void enableSpeaker() {
+ final TelecomAdapter telecomAdapter = TelecomAdapter.getInstance();
+ if (telecomAdapter == null) {
+ Log.e(LOG_TAG, "enableSpeaker: TelecomAdapter is null");
+ return;
+ }
+
+ final int currentAudioMode = AudioModeProvider.getInstance().getAudioMode();
+ Log.v(LOG_TAG, "enableSpeaker: Current audio mode is - " + currentAudioMode);
+
+ if(QtiCallUtils.isNotEnabled(CallAudioState.ROUTE_SPEAKER |
+ CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET,
+ currentAudioMode)) {
+ Log.v(LOG_TAG, "enableSpeaker: Set audio route to speaker");
+ telecomAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
+ }
+ }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 14f143a..0a7db2e 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -928,6 +928,7 @@
if (VideoUtils.isVideoCall(videoState)) {
showInCall(false, false/* newOutgoingCall */);
}
+ InCallAudioManager.getInstance().onAnswerIncomingCall(call, videoState);
}
}
@@ -975,6 +976,7 @@
VideoProfile videoProfile = new VideoProfile(videoState);
call.getVideoCall().sendSessionModifyResponse(videoProfile);
call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
+ InCallAudioManager.getInstance().onAcceptUpgradeRequest(call, videoState);
}
}
diff --git a/InCallUI/src/com/android/incallui/QtiCallUtils.java b/InCallUI/src/com/android/incallui/QtiCallUtils.java
index f6016b6..81d5105 100644
--- a/InCallUI/src/com/android/incallui/QtiCallUtils.java
+++ b/InCallUI/src/com/android/incallui/QtiCallUtils.java
@@ -214,6 +214,7 @@
}
videoCall.sendSessionModifyRequest(videoProfile);
call.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
+ InCallAudioManager.getInstance().onModifyCallClicked(call, videoProfile.getVideoState());
}
/**
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
index 7d71078..e42c90c 100644
--- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
@@ -725,11 +725,6 @@
maybeAutoEnterFullscreen(call);
}
- private static boolean isSpeakerEnabledForVideoCalls() {
- // TODO: Make this a carrier configurable setting. For now this is always true. b/20090407
- return true;
- }
-
private void enableCamera(VideoCall videoCall, boolean isCameraRequired) {
Log.d(this, "enableCamera: VideoCall=" + videoCall + " enabling=" + isCameraRequired);
if (videoCall == null) {