Merge "Add field to store who is initiating the call"
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 7a17dde..56f0963 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -1514,7 +1514,7 @@
      * MMI codes which can be dialed when one or more calls are in progress.
      * <P>
      * Checks for numbers formatted similar to the MMI codes defined in:
-     * {@link com.android.internal.telephony.gsm.GSMPhone#handleInCallMmiCommands(String)}
+     * {@link com.android.internal.telephony.GsmCdmaPhone#handleInCallMmiCommands(String)}
      * and
      * {@link com.android.internal.telephony.imsphone.ImsPhone#handleInCallMmiCommands(String)}
      *
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 28ea05d..91056d2 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -41,6 +41,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -62,6 +63,7 @@
 public class TelecomServiceImpl {
     private static final String PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION =
             "android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION";
+    private static final int DEFAULT_VIDEO_STATE = -1;
 
     private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
         @Override
@@ -589,7 +591,25 @@
 
                 long token = Binder.clearCallingIdentity();
                 try {
-                    acceptRingingCallInternal();
+                    acceptRingingCallInternal(DEFAULT_VIDEO_STATE);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        /**
+         * @see android.telecom.TelecomManager#acceptRingingCall(int)
+         *
+         */
+        @Override
+        public void acceptRingingCallWithVideoState(int videoState) {
+            synchronized (mLock) {
+                enforceModifyPermission();
+
+                long token = Binder.clearCallingIdentity();
+                try {
+                    acceptRingingCallInternal(videoState);
                 } finally {
                     Binder.restoreCallingIdentity(token);
                 }
@@ -1038,10 +1058,13 @@
         return false;
     }
 
-    private void acceptRingingCallInternal() {
+    private void acceptRingingCallInternal(int videoState) {
         Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
         if (call != null) {
-            call.answer(call.getVideoState());
+            if (videoState == DEFAULT_VIDEO_STATE || !isValidAcceptVideoState(videoState)) {
+                videoState = call.getVideoState();
+            }
+            call.answer(videoState);
         }
     }
 
@@ -1204,4 +1227,23 @@
     private TelephonyManager getTelephonyManager() {
         return (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
     }
+
+    /**
+     * Determines if a video state is valid for accepting an incoming call.
+     * For the purpose of accepting a call, states {@link VideoProfile#STATE_AUDIO_ONLY}, and
+     * any combination of {@link VideoProfile#STATE_RX_ENABLED} and
+     * {@link VideoProfile#STATE_TX_ENABLED} are considered valid.
+     *
+     * @param videoState The video state.
+     * @return {@code true} if the video state is valid, {@code false} otherwise.
+     */
+    private boolean isValidAcceptVideoState(int videoState) {
+        // Given a video state input, turn off TX and RX so that we can determine if those were the
+        // only bits set.
+        int remainingState = videoState & ~VideoProfile.STATE_TX_ENABLED;
+        remainingState = remainingState & ~VideoProfile.STATE_RX_ENABLED;
+
+        // If only TX or RX were set (or neither), the video state is valid.
+        return remainingState == 0;
+    }
 }
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index a637470..4576690 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -31,6 +31,9 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
 
+    <!-- Used to access TelephonyManager APIs -->
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+
     <application android:label="@string/app_name"
                  android:debuggable="true">
         <uses-library android:name="android.test.runner" />
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index dfaeed1..f3f4e5b 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -420,6 +420,15 @@
             String number,
             PhoneAccountHandle phoneAccountHandle,
             final ConnectionServiceFixture connectionServiceFixture) throws Exception {
+        return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
+                connectionServiceFixture);
+    }
+
+    private IdPair startIncomingPhoneCall(
+            String number,
+            PhoneAccountHandle phoneAccountHandle,
+            int videoState,
+            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
         reset(
                 connectionServiceFixture.getTestDouble(),
                 mInCallServiceFixtureX.getTestDouble(),
@@ -450,10 +459,15 @@
                 eq(true),
                 eq(false));
 
+        mConnectionServiceFixtureA.mConnectionById.get(
+                connectionServiceFixture.mLatestConnectionId).videoState = videoState;
+
         connectionServiceFixture.sendHandleCreateConnectionComplete(
                 connectionServiceFixture.mLatestConnectionId);
         connectionServiceFixture.sendSetRinging(
                 connectionServiceFixture.mLatestConnectionId);
+        connectionServiceFixture.sendSetVideoState(
+                connectionServiceFixture.mLatestConnectionId);
 
         // For the case of incoming calls, Telecom connecting the InCall services and adding the
         // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
@@ -607,6 +621,99 @@
                 mInCallServiceFixtureY.getCall(ids.mCallId).getState());
     }
 
+    /**
+     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
+     * audio-only call.
+     *
+     * @throws Exception
+     */
+    public void testTelecomManagerAcceptRingingCall() throws Exception {
+        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+        // Use TelecomManager API to answer the ringing call.
+        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
+                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
+        telecomManager.acceptRingingCall();
+
+        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
+                .answer(ids.mCallId);
+    }
+
+    /**
+     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
+     * video call, which should be answered as video.
+     *
+     * @throws Exception
+     */
+    public void testTelecomManagerAcceptRingingVideoCall() throws Exception {
+        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
+                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+        // Use TelecomManager API to answer the ringing call; the default expected behavior is to
+        // answer using whatever video state the ringing call requests.
+        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
+                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
+        telecomManager.acceptRingingCall();
+
+        // Answer video API should be called
+        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
+                .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
+    }
+
+    /**
+     * Tests the {@link TelecomManager#acceptRingingCall(int)} API.  Tests answering a video call
+     * as an audio call.
+     *
+     * @throws Exception
+     */
+    public void testTelecomManagerAcceptRingingVideoCallAsAudio() throws Exception {
+        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
+                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+        // Use TelecomManager API to answer the ringing call.
+        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
+                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
+        telecomManager.acceptRingingCall(VideoProfile.STATE_AUDIO_ONLY);
+
+        // The generic answer method on the ConnectionService is used to answer audio-only calls.
+        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
+                .answer(eq(ids.mCallId));
+    }
+
+    /**
+     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
+     * video call, where an attempt is made to answer with an invalid video state.
+     *
+     * @throws Exception
+     */
+    public void testTelecomManagerAcceptRingingInvalidVideoState() throws Exception {
+        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
+                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+        // Use TelecomManager API to answer the ringing call; the default expected behavior is to
+        // answer using whatever video state the ringing call requests.
+        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
+                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
+        telecomManager.acceptRingingCall(999 /* invalid videostate */);
+
+        // Answer video API should be called
+        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
+                .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
+    }
+
     // A simple incoming call, similar in scope to the previous test
     private IdPair startAndMakeActiveIncomingCall(
             String number,