Merge "Telecom: Refactor to move DEVICE_ID_DEFAULT from vdm to Context" into udc-dev
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 5847960..02dff13 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -2701,7 +2701,8 @@
             // hangup, not reject.
             setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
             if (mTransactionalService != null) {
-                mTransactionalService.onReject(this, DisconnectCause.REJECTED);
+                mTransactionalService.onDisconnect(this,
+                        new DisconnectCause(DisconnectCause.REJECTED));
             } else if (mConnectionService != null) {
                 mConnectionService.disconnect(this);
             } else {
@@ -2714,7 +2715,8 @@
             mVideoStateHistory |= mVideoState;
 
             if (mTransactionalService != null) {
-                mTransactionalService.onReject(this, DisconnectCause.REJECTED);
+                mTransactionalService.onDisconnect(this,
+                        new DisconnectCause(DisconnectCause.REJECTED));
             } else if (mConnectionService != null) {
                 mConnectionService.reject(this, rejectWithMessage, textMessage);
             } else {
@@ -2737,7 +2739,8 @@
             // Since its simulated reason we can't pass along the reject reason.
             setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
             if (mTransactionalService != null) {
-                mTransactionalService.onReject(this, DisconnectCause.REJECTED);
+                mTransactionalService.onDisconnect(this,
+                        new DisconnectCause(DisconnectCause.REJECTED));
             } else if (mConnectionService != null) {
                 mConnectionService.disconnect(this);
             } else {
@@ -2749,7 +2752,8 @@
             // Ensure video state history tracks video state at time of rejection.
             mVideoStateHistory |= mVideoState;
             if (mTransactionalService != null) {
-                mTransactionalService.onReject(this, rejectReason);
+                mTransactionalService.onDisconnect(this,
+                        new DisconnectCause(DisconnectCause.REJECTED));
             } else if (mConnectionService != null) {
                 mConnectionService.rejectWithReason(this, rejectReason);
             } else {
@@ -3205,9 +3209,7 @@
      * @param extras Associated extras.
      */
     public void sendCallEvent(String event, int targetSdkVer, Bundle extras) {
-        if (mTransactionalService != null) {
-            Log.i(this, "sendCallEvent: called on TransactionalService. doing nothing");
-        } else if (mConnectionService != null) {
+        if (mConnectionService != null || mTransactionalService != null) {
             if (android.telecom.Call.EVENT_REQUEST_HANDOVER.equals(event)) {
                 if (targetSdkVer > Build.VERSION_CODES.P) {
                     Log.e(this, new Exception(), "sendCallEvent failed. Use public api handoverTo" +
@@ -3230,8 +3232,8 @@
                 if (extras == null) {
                     Log.w(this, "sendCallEvent: %s event received with null extras.",
                             android.telecom.Call.EVENT_REQUEST_HANDOVER);
-                    mConnectionService.sendCallEvent(this,
-                            android.telecom.Call.EVENT_HANDOVER_FAILED, null);
+                    sendEventToService(this, android.telecom.Call.EVENT_HANDOVER_FAILED,
+                            null);
                     return;
                 }
                 Parcelable parcelable = extras.getParcelable(
@@ -3239,8 +3241,7 @@
                 if (!(parcelable instanceof PhoneAccountHandle) || parcelable == null) {
                     Log.w(this, "sendCallEvent: %s event received with invalid handover acct.",
                             android.telecom.Call.EVENT_REQUEST_HANDOVER);
-                    mConnectionService.sendCallEvent(this,
-                            android.telecom.Call.EVENT_HANDOVER_FAILED, null);
+                    sendEventToService(this, android.telecom.Call.EVENT_HANDOVER_FAILED, null);
                     return;
                 }
                 PhoneAccountHandle phoneAccountHandle = (PhoneAccountHandle) parcelable;
@@ -3263,7 +3264,7 @@
                     ));
                 }
                 Log.addEvent(this, LogUtils.Events.CALL_EVENT, event);
-                mConnectionService.sendCallEvent(this, event, extras);
+                sendEventToService(this, event, extras);
             }
         } else {
             Log.e(this, new NullPointerException(),
@@ -3272,6 +3273,17 @@
     }
 
     /**
+     *  This method should only be called from sendCallEvent(String, int, Bundle).
+     */
+    private void sendEventToService(Call call, String event, Bundle extras) {
+        if (mConnectionService != null) {
+            mConnectionService.sendCallEvent(call, event, extras);
+        } else if (mTransactionalService != null) {
+            mTransactionalService.onEvent(call, event, extras);
+        }
+    }
+
+    /**
      * Notifies listeners when a bluetooth quality report is received.
      * @param report The bluetooth quality report.
      */
diff --git a/src/com/android/server/telecom/CallStreamingController.java b/src/com/android/server/telecom/CallStreamingController.java
index 56ce17b..cd8a690 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -111,11 +111,11 @@
 
             if (mCallsManager.getCallStreamingController().isStreaming()) {
                 future.complete(new VoipCallTransactionResult(
-                        VoipCallTransactionResult.RESULT_SUCCEED, null));
-            } else {
-                future.complete(new VoipCallTransactionResult(
                         VoipCallTransactionResult.RESULT_FAILED,
                         "STREAMING_FAILED_ALREADY_STREAMING"));
+            } else {
+                future.complete(new VoipCallTransactionResult(
+                        VoipCallTransactionResult.RESULT_SUCCEED, null));
             }
 
             return future;
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 48aa9e6..09d8787 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -154,6 +154,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
@@ -455,6 +456,9 @@
 
     private LinkedList<HandlerThread> mGraphHandlerThreads;
 
+    // An executor that can be used to fire off async tasks that do not block Telecom in any manner.
+    private final Executor mAsyncTaskExecutor;
+
     private boolean mHasActiveRttCall = false;
 
     private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
@@ -553,7 +557,9 @@
             RoleManagerAdapter roleManagerAdapter,
             ToastFactory toastFactory,
             CallEndpointControllerFactory callEndpointControllerFactory,
-            CallAnomalyWatchdog callAnomalyWatchdog) {
+            CallAnomalyWatchdog callAnomalyWatchdog,
+            Executor asyncTaskExecutor,
+            Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter) {
         mContext = context;
         mLock = lock;
         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -614,7 +620,7 @@
                 ringtoneFactory, systemVibrator,
                 new Ringer.VibrationEffectProxy(), mInCallController,
                 mContext.getSystemService(NotificationManager.class),
-                mContext.getSystemService(AccessibilityManager.class));
+                accessibilityManagerAdapter);
         mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext, audioManager,
                 mTimeoutsAdapter, mLock);
         mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
@@ -670,6 +676,7 @@
         mGraphHandlerThreads = new LinkedList<>();
 
         mCallAnomalyWatchdog = callAnomalyWatchdog;
+        mAsyncTaskExecutor = asyncTaskExecutor;
     }
 
     public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
@@ -3327,7 +3334,8 @@
         setCallState(call, CallState.RINGING, "ringing set explicitly");
     }
 
-    void markCallAsDialing(Call call) {
+    @VisibleForTesting
+    public void markCallAsDialing(Call call) {
         setCallState(call, CallState.DIALING, "dialing set explicitly");
         maybeMoveToSpeakerPhone(call);
         maybeTurnOffMute(call);
@@ -4893,17 +4901,28 @@
         }
     }
 
+    /**
+     * Ensures that the call will be audible to the user by checking if the voice call stream is
+     * audible, and if not increasing the volume to the default value.
+     */
     private void ensureCallAudible() {
-        AudioManager am = mContext.getSystemService(AudioManager.class);
-        if (am == null) {
-            Log.w(this, "ensureCallAudible: audio manager is null");
-            return;
-        }
-        if (am.getStreamVolume(AudioManager.STREAM_VOICE_CALL) == 0) {
-            Log.i(this, "ensureCallAudible: voice call stream has volume 0. Adjusting to default.");
-            am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
-                    AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_VOICE_CALL), 0);
-        }
+        // Audio manager APIs can be somewhat slow.  To prevent a potential ANR we will fire off
+        // this opreation on the async task executor.  Note that this operation does not have any
+        // dependency on any Telecom state, so we can safely launch this on a different thread
+        // without worrying that it is in the Telecom sync lock.
+        mAsyncTaskExecutor.execute(() -> {
+            AudioManager am = mContext.getSystemService(AudioManager.class);
+            if (am == null) {
+                Log.w(this, "ensureCallAudible: audio manager is null");
+                return;
+            }
+            if (am.getStreamVolume(AudioManager.STREAM_VOICE_CALL) == 0) {
+                Log.i(this,
+                        "ensureCallAudible: voice call stream has volume 0. Adjusting to default.");
+                am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+                        AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_VOICE_CALL), 0);
+            }
+        });
     }
 
     /**
@@ -5941,18 +5960,24 @@
         }
     }
 
-    // driver method to create and execute a new TransactionalFocusRequestCallback
-    public void transactionRequestNewFocusCall(Call call,
+    /**
+     * Intended for ongoing or new calls that would like to go active/answered and need to
+     * update the mConnectionSvrFocusMgr before setting the state
+     */
+    public void transactionRequestNewFocusCall(Call call, int newCallState,
             OutcomeReceiver<Boolean, CallException> callback) {
         Log.d(this, "transactionRequestNewFocusCall");
-        PendingAction pendingAction = new ActionSetCallState(call, CallState.ACTIVE,
+        PendingAction pendingAction = new ActionSetCallState(call, newCallState,
                 "transactional ActionSetCallState");
         mConnectionSvrFocusMgr
                 .requestFocus(call,
                         new TransactionalFocusRequestCallback(pendingAction, call, callback));
     }
 
-    // request a new call focus and ensure the request was successful
+    /**
+     * Request a new call focus and ensure the request was successful via an OutcomeReceiver. Also,
+     * include a PendingAction that will execute if the call focus change is successful.
+     */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public class TransactionalFocusRequestCallback implements
             ConnectionServiceFocusManager.RequestFocusCallback {
@@ -5971,26 +5996,18 @@
         @Override
         public void onRequestFocusDone(ConnectionServiceFocusManager.CallFocus call) {
             Call currentCallFocus = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
-
-            if (currentCallFocus == null) {
-                Log.i(this, "TransactionalFocusRequestCallback: "
-                        + "currentCallFocus is null.");
-                mCallback.onError(new CallException("currentCallFocus is null",
+            // verify the update was successful before updating the state
+            Log.i(this, "tFRC: currentCallFocus=[%s], targetFocus=[%s]",
+                    mTargetCallFocus, currentCallFocus);
+            if (currentCallFocus == null ||
+                    !currentCallFocus.getId().equals(mTargetCallFocus.getId())) {
+                mCallback.onError(new CallException("failed to switch focus to requested call",
                         CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE));
                 return;
             }
-
-            Log.i(this, "TransactionalFocusRequestCallback: targetId=[%s], "
-                    + "currentId=[%s]", mTargetCallFocus.getId(), currentCallFocus.getId());
-            if (!currentCallFocus.getId().equals(mTargetCallFocus.getId())) {
-                Log.i(this, "TransactionalFocusRequestCallback: "
-                        + "currentCallFocus is not equal to targetCallFocus.");
-                mCallback.onError(new CallException("current focus is not target focus",
-                        CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE));
-                return;
-            }
-            mPendingAction.performAction();
-            mCallback.onResult(true);
+            // at this point, we know the FocusManager is able to update successfully
+            mPendingAction.performAction(); // set the call state
+            mCallback.onResult(true); // complete the transaction
         }
     }
 
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 171745f..c04cf6b 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -21,6 +21,7 @@
 import static android.provider.CallLog.Calls.USER_MISSED_NO_VIBRATE;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
 
+import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.Person;
@@ -54,20 +55,25 @@
  */
 @VisibleForTesting
 public class Ringer {
-  /**
-   * Flag only for local debugging. Do not submit enabled.
-   */
-  private static final boolean DEBUG_RINGER = false;
-
-  public static class VibrationEffectProxy {
-    public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
-      return VibrationEffect.createWaveform(timings, amplitudes, repeat);
+    public interface AccessibilityManagerAdapter {
+        boolean startFlashNotificationSequence(@NonNull Context context,
+                @AccessibilityManager.FlashNotificationReason int reason);
+        boolean stopFlashNotificationSequence(@NonNull Context context);
     }
+    /**
+     * Flag only for local debugging. Do not submit enabled.
+     */
+    private static final boolean DEBUG_RINGER = false;
 
-    public VibrationEffect get(Uri ringtoneUri, Context context) {
-      return VibrationEffect.get(ringtoneUri, context);
+    public static class VibrationEffectProxy {
+        public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
+            return VibrationEffect.createWaveform(timings, amplitudes, repeat);
+        }
+
+        public VibrationEffect get(Uri ringtoneUri, Context context) {
+            return VibrationEffect.get(ringtoneUri, context);
+        }
     }
-  }
     @VisibleForTesting
     public VibrationEffect mDefaultVibrationEffect;
 
@@ -162,7 +168,7 @@
     private RingtoneFactory mRingtoneFactory;
     private AudioManager mAudioManager;
     private NotificationManager mNotificationManager;
-    private AccessibilityManager mAccessibilityManager;
+    private AccessibilityManagerAdapter mAccessibilityManagerAdapter;
 
     /**
      * Call objects that are ringing, vibrating or call-waiting. These are used only for logging
@@ -197,7 +203,7 @@
             VibrationEffectProxy vibrationEffectProxy,
             InCallController inCallController,
             NotificationManager notificationManager,
-            AccessibilityManager accessibilityManager) {
+            AccessibilityManagerAdapter accessibilityManagerAdapter) {
 
         mLock = new Object();
         mSystemSettingsUtil = systemSettingsUtil;
@@ -211,7 +217,7 @@
         mInCallController = inCallController;
         mVibrationEffectProxy = vibrationEffectProxy;
         mNotificationManager = notificationManager;
-        mAccessibilityManager = accessibilityManager;
+        mAccessibilityManagerAdapter = accessibilityManagerAdapter;
 
         if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
             mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN,
@@ -295,10 +301,11 @@
         stopCallWaiting();
 
         final boolean shouldFlash = attributes.shouldRingForContact();
-        if (mAccessibilityManager != null && shouldFlash) {
+        if (mAccessibilityManagerAdapter != null && shouldFlash) {
             Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_START);
-            getHandler().post(() -> mAccessibilityManager.startFlashNotificationSequence(mContext,
-                    AccessibilityManager.FLASH_REASON_CALL));
+            getHandler().post(() ->
+                    mAccessibilityManagerAdapter.startFlashNotificationSequence(mContext,
+                        AccessibilityManager.FLASH_REASON_CALL));
         }
 
         // Determine if the settings and DND mode indicate that the vibrator can be used right now.
@@ -512,9 +519,10 @@
 
     public void stopRinging() {
         final Call foregroundCall = mRingingCall != null ? mRingingCall : mVibratingCall;
-        if (mAccessibilityManager != null) {
+        if (mAccessibilityManagerAdapter != null) {
             Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_STOP);
-            getHandler().post(() -> mAccessibilityManager.stopFlashNotificationSequence(mContext));
+            getHandler().post(() ->
+                    mAccessibilityManagerAdapter.stopFlashNotificationSequence(mContext));
         }
 
         synchronized (mLock) {
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 63852f3..f9c2508 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -31,6 +31,7 @@
 import android.telecom.Log;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.AnomalyReporter;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
@@ -208,7 +209,8 @@
             ClockProxy clockProxy,
             RoleManagerAdapter roleManagerAdapter,
             ContactsAsyncHelper.Factory contactsAsyncHelperFactory,
-            DeviceIdleControllerAdapter deviceIdleControllerAdapter) {
+            DeviceIdleControllerAdapter deviceIdleControllerAdapter,
+            Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter) {
         mContext = context.getApplicationContext();
         LogUtils.initLogging(mContext);
         AnomalyReporter.initialize(mContext);
@@ -332,6 +334,7 @@
             CallAnomalyWatchdog callAnomalyWatchdog = new CallAnomalyWatchdog(
                     Executors.newSingleThreadScheduledExecutor(),
                     mLock, timeoutsAdapter, clockProxy);
+
             mCallsManager = new CallsManager(
                     mContext,
                     mLock,
@@ -363,7 +366,9 @@
                     roleManagerAdapter,
                     toastFactory,
                     callEndpointControllerFactory,
-                    callAnomalyWatchdog);
+                    callAnomalyWatchdog,
+                    Executors.newSingleThreadExecutor(),
+                    accessibilityManagerAdapter);
 
             mIncomingCallNotifier = incomingCallNotifier;
             incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index 6931996..d97d192 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -38,6 +38,7 @@
 
 import com.android.internal.telecom.ICallControl;
 import com.android.internal.telecom.ICallEventCallback;
+import com.android.server.telecom.voip.AnswerCallTransaction;
 import com.android.server.telecom.voip.CallEventCallbackAckTransaction;
 import com.android.server.telecom.voip.EndpointChangeTransaction;
 import com.android.server.telecom.voip.HoldCallTransaction;
@@ -67,7 +68,7 @@
     // CallControl : Client (ex. voip app) --> Telecom
     public static final String SET_ACTIVE = "SetActive";
     public static final String SET_INACTIVE = "SetInactive";
-    public static final String REJECT = "Reject";
+    public static final String ANSWER = "Answer";
     public static final String DISCONNECT = "Disconnect";
     public static final String START_STREAMING = "StartStreaming";
 
@@ -75,7 +76,6 @@
     public static final String ON_SET_ACTIVE = "onSetActive";
     public static final String ON_SET_INACTIVE = "onSetInactive";
     public static final String ON_ANSWER = "onAnswer";
-    public static final String ON_REJECT = "onReject";
     public static final String ON_DISCONNECT = "onDisconnect";
     public static final String ON_STREAMING_STARTED = "onStreamingStarted";
 
@@ -196,6 +196,17 @@
         }
 
         @Override
+        public void answer(int videoState, String callId, android.os.ResultReceiver callback)
+                throws RemoteException {
+            try {
+                Log.startSession("TSW.a");
+                createTransactions(callId, callback, ANSWER, videoState);
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void setInactive(String callId, android.os.ResultReceiver callback)
                 throws RemoteException {
             try {
@@ -238,6 +249,10 @@
                     case SET_ACTIVE:
                         addTransactionsToManager(createSetActiveTransactions(call), callback);
                         break;
+                    case ANSWER:
+                        addTransactionsToManager(createSetAnswerTransactions(call,
+                                (int) objects[0]), callback);
+                        break;
                     case DISCONNECT:
                         addTransactionsToManager(new EndCallTransaction(mCallsManager,
                                 (DisconnectCause) objects[0], call), callback);
@@ -273,6 +288,28 @@
                 Log.endSession();
             }
         }
+
+        /**
+         * Application would like to inform InCallServices of an event
+         */
+        @Override
+        public void sendEvent(String callId, String event, Bundle extras) {
+            try {
+                Log.startSession("TSW.sE");
+                Call call = mTrackedCalls.get(callId);
+                if (call != null) {
+                    call.onConnectionEvent(event, extras);
+                }
+                else{
+                    Log.i(TAG,
+                            "sendEvent: was called but there is no call with id=[%s] cannot be "
+                                    + "found. Most likely the call has been disconnected");
+                }
+            }
+            finally {
+                Log.endSession();
+            }
+        }
     };
 
     private void addTransactionsToManager(VoipCallTransaction transaction,
@@ -366,7 +403,7 @@
             Log.i(TAG, String.format(Locale.US, "onSetInactive: callId=[%s]", call.getId()));
             mTransactionManager.addTransaction(
                     new CallEventCallbackAckTransaction(mICallEventCallback,
-                            ON_SET_INACTIVE, call.getId(), 0), new OutcomeReceiver<>() {
+                            ON_SET_INACTIVE, call.getId()), new OutcomeReceiver<>() {
                         @Override
                         public void onResult(VoipCallTransactionResult result) {
                             mCallsManager.markCallAsOnHold(call);
@@ -389,7 +426,7 @@
 
             mTransactionManager.addTransaction(
                     new CallEventCallbackAckTransaction(mICallEventCallback, ON_DISCONNECT,
-                            call.getId(), 0), new OutcomeReceiver<>() {
+                            call.getId(), cause), new OutcomeReceiver<>() {
                         @Override
                         public void onResult(VoipCallTransactionResult result) {
                             removeCallFromCallsManager(call, cause);
@@ -406,32 +443,6 @@
         }
     }
 
-    public void onReject(Call call, @android.telecom.Call.RejectReason int rejectReason) {
-        try {
-            Log.startSession("TSW.oR");
-            Log.d(TAG, String.format(Locale.US, "onReject: callId=[%s]", call.getId()));
-
-            mTransactionManager.addTransaction(
-                    new CallEventCallbackAckTransaction(mICallEventCallback, ON_REJECT,
-                            call.getId(), 0), new OutcomeReceiver<>() {
-                        @Override
-                        public void onResult(VoipCallTransactionResult result) {
-                            removeCallFromCallsManager(call,
-                                    new DisconnectCause(DisconnectCause.REJECTED));
-                        }
-
-                        @Override
-                        public void onError(CallException exception) {
-                            removeCallFromCallsManager(call,
-                                    new DisconnectCause(DisconnectCause.REJECTED));
-                        }
-                    }
-            );
-        } finally {
-            Log.endSession();
-        }
-    }
-
     public void onCallStreamingStarted(Call call) {
         try {
             Log.startSession("TSW.oCSS");
@@ -440,7 +451,7 @@
 
             mTransactionManager.addTransaction(
                     new CallEventCallbackAckTransaction(mICallEventCallback, ON_STREAMING_STARTED,
-                            call.getId(), 0), new OutcomeReceiver<>() {
+                            call.getId()), new OutcomeReceiver<>() {
                         @Override
                         public void onResult(VoipCallTransactionResult result) {
                         }
@@ -508,6 +519,15 @@
         }
     }
 
+    public void onEvent(Call call, String event, Bundle extras){
+        if (call != null) {
+            try {
+                mICallEventCallback.onEvent(call.getId(), event, extras);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /***
      *********************************************************************************************
      **                                Helpers                                                  **
@@ -536,13 +556,27 @@
         // add t1. hold potential active call
         transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call));
 
-        // add t2. answer current call
+        // add t2. send request to set the current call active
         transactions.add(new RequestFocusTransaction(mCallsManager, call));
 
         // send off to Transaction Manager to process
         return new SerialTransaction(transactions);
     }
 
+    private SerialTransaction createSetAnswerTransactions(Call call, int videoState) {
+        // create list for multiple transactions
+        List<VoipCallTransaction> transactions = new ArrayList<>();
+
+        // add t1. hold potential active call
+        transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call));
+
+        // add t2. answer current call
+        transactions.add(new AnswerCallTransaction(mCallsManager, call, videoState));
+
+        // send off to Transaction Manager to process
+        return new SerialTransaction(transactions);
+    }
+
     /***
      *********************************************************************************************
      **                    FocusManager                                                       **
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 9ad0da4..3881154 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -29,6 +29,7 @@
 import android.telecom.Log;
 
 import android.telecom.CallerInfoAsyncQuery;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.telecom.IInternalServiceRetriever;
 import com.android.internal.telecom.ITelecomLoader;
@@ -53,6 +54,7 @@
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.ProximitySensorManager;
+import com.android.server.telecom.Ringer;
 import com.android.server.telecom.RoleManagerAdapterImpl;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.TelecomWakeLock;
@@ -191,7 +193,22 @@
                             new RoleManagerAdapterImpl(context,
                                     (RoleManager) context.getSystemService(Context.ROLE_SERVICE)),
                             new ContactsAsyncHelper.Factory(),
-                            internalServiceRetriever.getDeviceIdleController()));
+                            internalServiceRetriever.getDeviceIdleController(),
+                            new Ringer.AccessibilityManagerAdapter() {
+                                @Override
+                                public boolean startFlashNotificationSequence(
+                                        @androidx.annotation.NonNull Context context, int reason) {
+                                    return context.getSystemService(AccessibilityManager.class)
+                                            .startFlashNotificationSequence(context, reason);
+                                }
+
+                                @Override
+                                public boolean stopFlashNotificationSequence(
+                                        @androidx.annotation.NonNull Context context) {
+                                    return context.getSystemService(AccessibilityManager.class)
+                                            .stopFlashNotificationSequence(context);
+                                }
+                            }));
         }
     }
 
diff --git a/src/com/android/server/telecom/voip/AnswerCallTransaction.java b/src/com/android/server/telecom/voip/AnswerCallTransaction.java
new file mode 100644
index 0000000..1eb34c4
--- /dev/null
+++ b/src/com/android/server/telecom/voip/AnswerCallTransaction.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.voip;
+
+import android.os.OutcomeReceiver;
+import android.telecom.CallException;
+import android.util.Log;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallsManager;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+/**
+ * This transaction should be created for new incoming calls that request to go from
+ * CallState.Ringing to CallState.Answered.  Before changing the CallState, the focus manager must
+ * be updated. Once the focus manager updates, the call state will be set.  If there is an issue
+ * answering the call, the transaction will fail.
+ */
+public class AnswerCallTransaction extends VoipCallTransaction {
+
+    private static final String TAG = AnswerCallTransaction.class.getSimpleName();
+    private final CallsManager mCallsManager;
+    private final Call mCall;
+    private final int mVideoState;
+
+    public AnswerCallTransaction(CallsManager callsManager, Call call, int videoState) {
+        mCallsManager = callsManager;
+        mCall = call;
+        mVideoState = videoState;
+    }
+
+    @Override
+    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+        Log.d(TAG, "processTransaction");
+        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+
+        mCall.setVideoState(mVideoState);
+
+        mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ANSWERED,
+                new OutcomeReceiver<>() {
+            @Override
+            public void onResult(Boolean result) {
+                Log.d(TAG, "processTransaction: onResult");
+                future.complete(new VoipCallTransactionResult(
+                        VoipCallTransactionResult.RESULT_SUCCEED, null));
+            }
+
+            @Override
+            public void onError(CallException exception) {
+                Log.d(TAG, "processTransaction: onError");
+                future.complete(new VoipCallTransactionResult(
+                        exception.getCode(), exception.getMessage()));
+            }
+        });
+
+        return future;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
index 327694e..f47e4c5 100644
--- a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
+++ b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
@@ -22,6 +22,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.telecom.DisconnectCause;
 import android.util.Log;
 
 import com.android.internal.telecom.ICallEventCallback;
@@ -41,7 +42,10 @@
     private final ICallEventCallback mICallEventCallback;
     private final String mAction;
     private final String mCallId;
-    private final int mVideoState;
+    // optional values
+    private int mVideoState = 0;
+    private DisconnectCause mDisconnectCause = null;
+
     private final VoipCallTransactionResult TRANSACTION_FAILED = new VoipCallTransactionResult(
             CODE_OPERATION_TIMED_OUT, "failed to complete the operation before timeout");
 
@@ -61,6 +65,14 @@
         }
     }
 
+    public CallEventCallbackAckTransaction(ICallEventCallback service, String action,
+            String callId) {
+        mICallEventCallback = service;
+        mAction = action;
+        mCallId = callId;
+    }
+
+
     public CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId,
             int videoState) {
         mICallEventCallback = service;
@@ -69,6 +81,14 @@
         mVideoState = videoState;
     }
 
+    public CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId,
+            DisconnectCause cause) {
+        mICallEventCallback = service;
+        mAction = action;
+        mCallId = callId;
+        mDisconnectCause = cause;
+    }
+
 
     @Override
     public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
@@ -82,10 +102,7 @@
                     mICallEventCallback.onSetInactive(mCallId, receiver);
                     break;
                 case TransactionalServiceWrapper.ON_DISCONNECT:
-                    mICallEventCallback.onDisconnect(mCallId, receiver);
-                    break;
-                case TransactionalServiceWrapper.ON_REJECT:
-                    mICallEventCallback.onReject(mCallId, receiver);
+                    mICallEventCallback.onDisconnect(mCallId, mDisconnectCause, receiver);
                     break;
                 case TransactionalServiceWrapper.ON_SET_ACTIVE:
                     mICallEventCallback.onSetActive(mCallId, receiver);
diff --git a/src/com/android/server/telecom/voip/RequestFocusTransaction.java b/src/com/android/server/telecom/voip/RequestFocusTransaction.java
index f13525f..5dedbda 100644
--- a/src/com/android/server/telecom/voip/RequestFocusTransaction.java
+++ b/src/com/android/server/telecom/voip/RequestFocusTransaction.java
@@ -21,6 +21,7 @@
 import android.util.Log;
 
 import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
 import com.android.server.telecom.CallsManager;
 
 import java.util.concurrent.CompletableFuture;
@@ -42,7 +43,8 @@
         Log.d(TAG, "processTransaction");
         CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
 
-        mCallsManager.transactionRequestNewFocusCall(mCall, new OutcomeReceiver<>() {
+        mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ACTIVE,
+                new OutcomeReceiver<>() {
             @Override
             public void onResult(Boolean result) {
                 Log.d(TAG, "processTransaction: onResult");
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
index cdf2efa..690311e 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
@@ -16,11 +16,14 @@
 
 package com.android.server.telecom.transactionalVoipApp;
 
+import android.os.Bundle;
 import android.telecom.CallControlCallback;
 import android.telecom.CallEndpoint;
 import android.telecom.CallControl;
 import android.telecom.CallEventCallback;
+import android.telecom.DisconnectCause;
 import android.util.Log;
+
 import java.util.List;
 
 import androidx.annotation.NonNull;
@@ -60,13 +63,8 @@
     }
 
     @Override
-    public void onReject(@NonNull Consumer<Boolean> wasCompleted) {
-        Log.i(TAG, String.format("onReject: callId=[%s]", mCallId));
-        wasCompleted.accept(Boolean.TRUE);
-    }
-
-    @Override
-    public void onDisconnect(@NonNull Consumer<Boolean> wasCompleted) {
+    public void onDisconnect(@NonNull DisconnectCause cause,
+            @NonNull Consumer<Boolean> wasCompleted) {
         Log.i(TAG, String.format("onDisconnect: callId=[%s]", mCallId));
         wasCompleted.accept(Boolean.TRUE);
     }
@@ -83,6 +81,12 @@
     }
 
     @Override
+    public void onEvent(String event, Bundle extras) {
+        Log.i(TAG, String.format("onEvent: id=[%s], event=[%s], extras=[%s]",
+                mCallId, event, extras));
+    }
+
+    @Override
     public void onCallEndpointChanged(@NonNull CallEndpoint newCallEndpoint) {
         Log.i(TAG, String.format("onCallEndpointChanged: endpoint=[%s]", newCallEndpoint));
     }
diff --git a/tests/src/com/android/server/telecom/tests/CallTest.java b/tests/src/com/android/server/telecom/tests/CallTest.java
index fc78810..6b817d8 100644
--- a/tests/src/com/android/server/telecom/tests/CallTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -426,11 +427,6 @@
         call.answer(0);
         verify(mMockTransactionalService, times(1)).onAnswer(call, 0);
 
-        // assert CallEventCallback#onReject is called
-        call.setState(CallState.RINGING, "test");
-        call.reject(0);
-        verify(mMockTransactionalService, times(1)).onReject(call, 0);
-
         // assert CallEventCallback#onDisconnect is called
         call.setState(CallState.ACTIVE, "test");
         call.disconnect();
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 75a9a96..7544dc9 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -48,6 +48,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -105,6 +106,7 @@
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
 import com.android.server.telecom.ProximitySensorManager;
 import com.android.server.telecom.ProximitySensorManagerFactory;
+import com.android.server.telecom.Ringer;
 import com.android.server.telecom.RoleManagerAdapter;
 import com.android.server.telecom.SystemStateHelper;
 import com.android.server.telecom.TelecomSystem;
@@ -136,6 +138,7 @@
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -233,6 +236,7 @@
     @Mock private Toast mToast;
     @Mock private CallAnomalyWatchdog mCallAnomalyWatchdog;
     @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
+    @Mock private Ringer.AccessibilityManagerAdapter mAccessibilityManagerAdapter;
 
     private CallsManager mCallsManager;
 
@@ -294,7 +298,10 @@
                 mRoleManagerAdapter,
                 mToastFactory,
                 mCallEndpointControllerFactory,
-                mCallAnomalyWatchdog);
+                mCallAnomalyWatchdog,
+                // Just do async tasks synchronously to support testing.
+                command -> command.run(),
+                mAccessibilityManagerAdapter);
 
         when(mPhoneAccountRegistrar.getPhoneAccount(
                 eq(SELF_MANAGED_HANDLE), any())).thenReturn(SELF_MANAGED_ACCOUNT);
@@ -2415,6 +2422,50 @@
 
     @MediumTest
     @Test
+    public void testSetCallDialingAndDontIncreaseVolume() {
+        // Start with a non zero volume.
+        mComponentContextFixture.getAudioManager().setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+                4, 0 /* flags */);
+
+        Call call = mock(Call.class);
+        mCallsManager.markCallAsDialing(call);
+
+        // We set the volume to non-zero above, so expect 1
+        verify(mComponentContextFixture.getAudioManager(), times(1)).setStreamVolume(
+                eq(AudioManager.STREAM_VOICE_CALL), anyInt(), anyInt());
+    }
+    @MediumTest
+    @Test
+    public void testSetCallDialingAndIncreaseVolume() {
+        // Start with a zero volume stream.
+        mComponentContextFixture.getAudioManager().setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+                0, 0 /* flags */);
+
+        Call call = mock(Call.class);
+        mCallsManager.markCallAsDialing(call);
+
+        // We set the volume to zero above, so expect 2
+        verify(mComponentContextFixture.getAudioManager(), times(2)).setStreamVolume(
+                eq(AudioManager.STREAM_VOICE_CALL), anyInt(), anyInt());
+    }
+
+    @MediumTest
+    @Test
+    public void testSetCallActiveAndDontIncreaseVolume() {
+        // Start with a non-zero volume.
+        mComponentContextFixture.getAudioManager().setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+                4, 0 /* flags */);
+
+        Call call = mock(Call.class);
+        mCallsManager.markCallAsActive(call);
+
+        // We set the volume to non-zero above, so expect 1 only.
+        verify(mComponentContextFixture.getAudioManager(), times(1)).setStreamVolume(
+                eq(AudioManager.STREAM_VOICE_CALL), anyInt(), anyInt());
+    }
+
+    @MediumTest
+    @Test
     public void testHandoverToIsAccepted() {
         Call sourceCall = addSpyCall(CONNECTION_MGR_1_HANDLE, CallState.NEW);
         Call call = addSpyCall(CONNECTION_MGR_1_HANDLE, CallState.NEW);
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index 17bf85e..e3a1471 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -783,6 +783,10 @@
         return mTelephonyManager;
     }
 
+    public AudioManager getAudioManager() {
+        return mAudioManager;
+    }
+
     public CarrierConfigManager getCarrierConfigManager() {
         return mCarrierConfigManager;
     }
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index 35eda43..d397910 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -150,7 +150,7 @@
     @Mock Vibrator mockVibrator;
     @Mock InCallController mockInCallController;
     @Mock NotificationManager mockNotificationManager;
-    @Mock AccessibilityManager mockAccessibilityManager;
+    @Mock Ringer.AccessibilityManagerAdapter mockAccessibilityManagerAdapter;
 
     @Spy Ringer.VibrationEffectProxy spyVibrationEffectProxy;
 
@@ -180,13 +180,12 @@
         when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mockSystemSettingsUtil.isHapticPlaybackSupported(any(Context.class))).thenReturn(true);
         mockNotificationManager =mContext.getSystemService(NotificationManager.class);
-        mockAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         when(mockTonePlayer.startTone()).thenReturn(true);
         when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
         when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false);
         mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
                 mockRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy,
-                mockInCallController, mockNotificationManager, mockAccessibilityManager);
+                mockInCallController, mockNotificationManager, mockAccessibilityManagerAdapter);
         when(mockCall1.getState()).thenReturn(CallState.RINGING);
         when(mockCall2.getState()).thenReturn(CallState.RINGING);
         when(mockCall1.getUserHandleFromTargetPhoneAccount()).thenReturn(PA_HANDLE.getUserHandle());
@@ -558,7 +557,7 @@
 
         assertFalse(mRingerUnderTest.shouldRingForContact(mockCall2));
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
-        verify(mockAccessibilityManager, never())
+        verify(mockAccessibilityManagerAdapter, never())
                 .startFlashNotificationSequence(any(Context.class), anyInt());
     }
 
@@ -573,7 +572,7 @@
 
         assertTrue(mRingerUnderTest.shouldRingForContact(mockCall2));
         assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
-        verify(mockAccessibilityManager, atLeastOnce())
+        verify(mockAccessibilityManagerAdapter, atLeastOnce())
                 .startFlashNotificationSequence(any(Context.class), anyInt());
     }
 
@@ -589,7 +588,7 @@
         assertTrue(mRingerUnderTest.shouldRingForContact(mockCall2));
         assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
         mRingerUnderTest.stopRinging();
-        verify(mockAccessibilityManager, atLeastOnce())
+        verify(mockAccessibilityManagerAdapter, atLeastOnce())
                 .stopFlashNotificationSequence(any(Context.class));
 
     }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 2ab3160..7e7235d 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -89,6 +89,7 @@
 import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
 import com.android.server.telecom.ProximitySensorManager;
 import com.android.server.telecom.ProximitySensorManagerFactory;
+import com.android.server.telecom.Ringer;
 import com.android.server.telecom.RoleManagerAdapter;
 import com.android.server.telecom.StatusBarNotifier;
 import com.android.server.telecom.SystemStateHelper;
@@ -209,6 +210,8 @@
     @Mock ToneGenerator mToneGenerator;
     @Mock DeviceIdleControllerAdapter mDeviceIdleControllerAdapter;
 
+    @Mock Ringer.AccessibilityManagerAdapter mAccessibilityManagerAdapter;
+
     final ComponentName mInCallServiceComponentNameX =
             new ComponentName(
                     "incall-service-package-X",
@@ -537,7 +540,7 @@
                             ContactsAsyncHelper.ContentResolverAdapter adapter) {
                         return new ContactsAsyncHelper(adapter, mHandlerThread.getLooper());
                     }
-                }, mDeviceIdleControllerAdapter);
+                }, mDeviceIdleControllerAdapter, mAccessibilityManagerAdapter);
 
         mComponentContextFixture.setTelecomManager(new TelecomManager(
                 mComponentContextFixture.getTestDouble(),
diff --git a/tests/src/com/android/server/telecom/tests/TransactionTests.java b/tests/src/com/android/server/telecom/tests/TransactionTests.java
index a82fa78..1e6734b 100644
--- a/tests/src/com/android/server/telecom/tests/TransactionTests.java
+++ b/tests/src/com/android/server/telecom/tests/TransactionTests.java
@@ -47,6 +47,7 @@
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.ui.ToastFactory;
+import com.android.server.telecom.voip.AnswerCallTransaction;
 import com.android.server.telecom.voip.EndCallTransaction;
 import com.android.server.telecom.voip.HoldCallTransaction;
 import com.android.server.telecom.voip.IncomingCallTransaction;
@@ -152,7 +153,23 @@
 
         // THEN
         verify(mCallsManager, times(1))
-                .transactionRequestNewFocusCall(eq(mMockCall1), isA(OutcomeReceiver.class));
+                .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ACTIVE),
+                        isA(OutcomeReceiver.class));
+    }
+
+    @Test
+    public void testAnswerCallTransaction() throws Exception {
+        // GIVEN
+        AnswerCallTransaction transaction =
+                new AnswerCallTransaction(mCallsManager, mMockCall1, 0);
+
+        // WHEN
+        transaction.processTransaction(null);
+
+        // THEN
+        verify(mCallsManager, times(1))
+                .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ANSWERED),
+                        isA(OutcomeReceiver.class));
     }
 
     @Test