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) {