Call Sequencing initial changes/refactor

This CL refactors the existing transactional logic to be retrofitted to
a more general case that handles regular connection services as well.
This CL mainly just renames existing classes and moves classes around to
different directories for better clarity.

This also inserts the boilerplate code for implementing the call
sequencing related changes. Those classes are as follows:
CallSequencingController, CallsManagerSequencingAdapter,
TransactionalCallSequencingAdapter.

A separate CL will be put up that will be focused around the
actual implementation.

Bug: 327038818
Flag: com.android.server.telecom.flags.enable_call_sequencing
Test: m Telecom
Change-Id: I75bd7954f9c84affcfeff29e0f36fa09e0c56607
diff --git a/src/com/android/server/telecom/CachedVideoStateChange.java b/src/com/android/server/telecom/CachedVideoStateChange.java
index cefb92b..8aa6d40 100644
--- a/src/com/android/server/telecom/CachedVideoStateChange.java
+++ b/src/com/android/server/telecom/CachedVideoStateChange.java
@@ -16,7 +16,8 @@
 
 package com.android.server.telecom;
 
-import static com.android.server.telecom.voip.VideoStateTranslation.TransactionalVideoStateToString;
+import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
+        .TransactionalVideoStateToString;
 
 import android.telecom.Log;
 
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index c391641..df31e02 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -21,8 +21,10 @@
 
 import static com.android.server.telecom.CachedCallback.TYPE_QUEUE;
 import static com.android.server.telecom.CachedCallback.TYPE_STATE;
-import static com.android.server.telecom.voip.VideoStateTranslation.TransactionalVideoStateToString;
-import static com.android.server.telecom.voip.VideoStateTranslation.VideoProfileStateToTransactionalVideoState;
+import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
+        .TransactionalVideoStateToString;
+import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
+        .VideoProfileStateToTransactionalVideoState;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -78,9 +80,9 @@
 import com.android.server.telecom.stats.CallFailureCause;
 import com.android.server.telecom.stats.CallStateChangedAtomWriter;
 import com.android.server.telecom.ui.ToastFactory;
-import com.android.server.telecom.voip.TransactionManager;
-import com.android.server.telecom.voip.VerifyCallStateChangeTransaction;
-import com.android.server.telecom.voip.VoipCallTransactionResult;
+import com.android.server.telecom.callsequencing.TransactionManager;
+import com.android.server.telecom.callsequencing.VerifyCallStateChangeTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.io.IOException;
 import java.text.SimpleDateFormat;
@@ -3185,7 +3187,7 @@
         tm.addTransaction(new VerifyCallStateChangeTransaction(mCallsManager.getLock(),
                 this, targetCallState), new OutcomeReceiver<>() {
             @Override
-            public void onResult(VoipCallTransactionResult result) {
+            public void onResult(CallTransactionResult result) {
                 Log.i(this, "awaitCallStateChangeAndMaybeDisconnectCall: %s: onResult:"
                         + " due to CallException=[%s]", callingMethod, result);
             }
diff --git a/src/com/android/server/telecom/CallStreamingController.java b/src/com/android/server/telecom/CallStreamingController.java
index efd458e..d14a553 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -39,10 +39,10 @@
 import android.telecom.Log;
 
 import com.android.internal.telecom.ICallStreamingService;
-import com.android.server.telecom.voip.ParallelTransaction;
-import com.android.server.telecom.voip.SerialTransaction;
-import com.android.server.telecom.voip.VoipCallTransaction;
-import com.android.server.telecom.voip.VoipCallTransactionResult;
+import com.android.server.telecom.callsequencing.voip.ParallelTransaction;
+import com.android.server.telecom.callsequencing.voip.SerialTransaction;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -112,7 +112,7 @@
         }
     }
 
-    public static class QueryCallStreamingTransaction extends VoipCallTransaction {
+    public static class QueryCallStreamingTransaction extends CallTransaction {
         private final CallsManager mCallsManager;
 
         public QueryCallStreamingTransaction(CallsManager callsManager) {
@@ -121,24 +121,24 @@
         }
 
         @Override
-        public CompletableFuture<VoipCallTransactionResult> processTransaction(Void v) {
+        public CompletableFuture<CallTransactionResult> processTransaction(Void v) {
             Log.i(this, "processTransaction");
-            CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+            CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
 
             if (mCallsManager.getCallStreamingController().isStreaming()) {
-                future.complete(new VoipCallTransactionResult(
+                future.complete(new CallTransactionResult(
                         CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */,
                         "STREAMING_FAILED_ALREADY_STREAMING"));
             } else {
-                future.complete(new VoipCallTransactionResult(
-                        VoipCallTransactionResult.RESULT_SUCCEED, null));
+                future.complete(new CallTransactionResult(
+                        CallTransactionResult.RESULT_SUCCEED, null));
             }
 
             return future;
         }
     }
 
-    public static class AudioInterceptionTransaction extends VoipCallTransaction {
+    public static class AudioInterceptionTransaction extends CallTransaction {
         private Call mCall;
         private boolean mEnterInterception;
 
@@ -150,16 +150,16 @@
         }
 
         @Override
-        public CompletableFuture<VoipCallTransactionResult> processTransaction(Void v) {
+        public CompletableFuture<CallTransactionResult> processTransaction(Void v) {
             Log.i(this, "processTransaction");
-            CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+            CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
 
             if (mEnterInterception) {
                 mCall.startStreaming();
             } else {
                 mCall.stopStreaming();
             }
-            future.complete(new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED,
+            future.complete(new CallTransactionResult(CallTransactionResult.RESULT_SUCCEED,
                     null));
             return future;
         }
@@ -170,7 +170,7 @@
         return new StreamingServiceTransaction(context, wrapper, call);
     }
 
-    public class StreamingServiceTransaction extends VoipCallTransaction {
+    public class StreamingServiceTransaction extends CallTransaction {
         public static final String MESSAGE = "STREAMING_FAILED_NO_SENDER";
         private final TransactionalServiceWrapper mWrapper;
         private final Context mContext;
@@ -188,14 +188,14 @@
 
         @SuppressLint("LongLogTag")
         @Override
-        public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+        public CompletionStage<CallTransactionResult> processTransaction(Void v) {
             Log.i(this, "processTransaction");
-            CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+            CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
             RoleManager roleManager = mContext.getSystemService(RoleManager.class);
             PackageManager packageManager = mContext.getPackageManager();
             if (roleManager == null || packageManager == null) {
                 Log.w(this, "processTransaction: Can't find system service");
-                future.complete(new VoipCallTransactionResult(
+                future.complete(new CallTransactionResult(
                         CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */,
                         MESSAGE));
                 return future;
@@ -205,7 +205,7 @@
                     RoleManager.ROLE_SYSTEM_CALL_STREAMING, mUserHandle);
             if (holders.isEmpty()) {
                 Log.w(this, "processTransaction: Can't find streaming app");
-                future.complete(new VoipCallTransactionResult(
+                future.complete(new CallTransactionResult(
                         CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */,
                         MESSAGE));
                 return future;
@@ -217,7 +217,7 @@
                     PackageManager.GET_META_DATA, mUserHandle);
             if (infos.isEmpty()) {
                 Log.w(this, "processTransaction: Can't find streaming service");
-                future.complete(new VoipCallTransactionResult(
+                future.complete(new CallTransactionResult(
                         CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */,
                         MESSAGE));
                 return future;
@@ -229,7 +229,7 @@
                     Manifest.permission.BIND_CALL_STREAMING_SERVICE)) {
                 Log.w(this, "Must require BIND_CALL_STREAMING_SERVICE: " +
                         serviceInfo.packageName);
-                future.complete(new VoipCallTransactionResult(
+                future.complete(new CallTransactionResult(
                         CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */,
                         MESSAGE));
                 return future;
@@ -242,7 +242,7 @@
                     | Context.BIND_FOREGROUND_SERVICE
                     | Context.BIND_SCHEDULE_LIKE_TOP_APP, mUserHandle)) {
                 Log.w(this, "Can't bind to streaming service");
-                future.complete(new VoipCallTransactionResult(
+                future.complete(new CallTransactionResult(
                         CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */,
                         "STREAMING_FAILED_SENDER_BINDING_ERROR"));
             }
@@ -254,19 +254,19 @@
         return new UnbindStreamingServiceTransaction();
     }
 
-    public class UnbindStreamingServiceTransaction extends VoipCallTransaction {
+    public class UnbindStreamingServiceTransaction extends CallTransaction {
         public UnbindStreamingServiceTransaction() {
             super(mTelecomLock);
         }
 
         @SuppressLint("LongLogTag")
         @Override
-        public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+        public CompletionStage<CallTransactionResult> processTransaction(Void v) {
             Log.i(this, "processTransaction (unbindStreaming");
-            CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+            CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
 
             resetController();
-            future.complete(new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED,
+            future.complete(new CallTransactionResult(CallTransactionResult.RESULT_SUCCEED,
                     null));
             return future;
         }
@@ -275,7 +275,7 @@
     public class StartStreamingTransaction extends SerialTransaction {
         private Call mCall;
 
-        public StartStreamingTransaction(List<VoipCallTransaction> subTransactions, Call call,
+        public StartStreamingTransaction(List<CallTransaction> subTransactions, Call call,
                 TelecomSystem.SyncRoot lock) {
             super(subTransactions, lock);
             mCall = call;
@@ -287,7 +287,7 @@
         }
     }
 
-    public VoipCallTransaction getStartStreamingTransaction(CallsManager callsManager,
+    public CallTransaction getStartStreamingTransaction(CallsManager callsManager,
             TransactionalServiceWrapper wrapper, Call call, TelecomSystem.SyncRoot lock) {
         // start streaming transaction flow:
         //     1. make sure there's no ongoing streaming call --> bind to EXO
@@ -296,7 +296,7 @@
         // If bind to EXO failed, add transaction for stop the streaming
 
         // create list for multiple transactions
-        List<VoipCallTransaction> transactions = new ArrayList<>();
+        List<CallTransaction> transactions = new ArrayList<>();
         transactions.add(new QueryCallStreamingTransaction(callsManager));
         transactions.add(new AudioInterceptionTransaction(call, true, lock));
         transactions.add(getCallStreamingServiceTransaction(
@@ -304,10 +304,10 @@
         return new StartStreamingTransaction(transactions, call, lock);
     }
 
-    public VoipCallTransaction getStopStreamingTransaction(Call call, TelecomSystem.SyncRoot lock) {
+    public CallTransaction getStopStreamingTransaction(Call call, TelecomSystem.SyncRoot lock) {
         // TODO: implement this
         // Stop streaming transaction flow:
-        List<VoipCallTransaction> transactions = new ArrayList<>();
+        List<CallTransaction> transactions = new ArrayList<>();
 
         // 1. unbind to call streaming service
         transactions.add(getUnbindStreamingServiceTransaction());
@@ -352,7 +352,7 @@
                 mTransactionalServiceWrapper.getTransactionManager().addTransaction(transaction,
                         new OutcomeReceiver<>() {
                             @Override
-                            public void onResult(VoipCallTransactionResult result) {
+                            public void onResult(CallTransactionResult result) {
                                 // ignore
                             }
 
@@ -366,7 +366,7 @@
         }
     }
 
-    private class CallStreamingStateChangeTransaction extends VoipCallTransaction {
+    private class CallStreamingStateChangeTransaction extends CallTransaction {
         @StreamingCall.StreamingCallState int mState;
 
         public CallStreamingStateChangeTransaction(@StreamingCall.StreamingCallState int state) {
@@ -375,14 +375,14 @@
         }
 
         @Override
-        public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
-            CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+        public CompletionStage<CallTransactionResult> processTransaction(Void v) {
+            CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
             try {
                 mService.onCallStreamingStateChanged(mState);
-                future.complete(new VoipCallTransactionResult(
-                        VoipCallTransactionResult.RESULT_SUCCEED, null));
+                future.complete(new CallTransactionResult(
+                        CallTransactionResult.RESULT_SUCCEED, null));
             } catch (RemoteException e) {
-                future.complete(new VoipCallTransactionResult(
+                future.complete(new CallTransactionResult(
                         CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */,
                         "Exception when request "
                         + "setting state to streaming app."));
@@ -395,10 +395,10 @@
             ServiceConnection {
         private Call mCall;
         private TransactionalServiceWrapper mWrapper;
-        private CompletableFuture<VoipCallTransactionResult> mFuture;
+        private CompletableFuture<CallTransactionResult> mFuture;
 
         public CallStreamingServiceConnection(Call call, TransactionalServiceWrapper wrapper,
-                CompletableFuture<VoipCallTransactionResult> future) {
+                CompletableFuture<CallTransactionResult> future) {
             mCall = call;
             mWrapper = wrapper;
             mFuture = future;
@@ -409,11 +409,11 @@
             try {
                 Log.i(this, "onServiceConnected: " + name);
                 onConnectedInternal(mCall, mWrapper, service);
-                mFuture.complete(new VoipCallTransactionResult(
-                        VoipCallTransactionResult.RESULT_SUCCEED, null));
+                mFuture.complete(new CallTransactionResult(
+                        CallTransactionResult.RESULT_SUCCEED, null));
             } catch (RemoteException e) {
                 resetController();
-                mFuture.complete(new VoipCallTransactionResult(
+                mFuture.complete(new CallTransactionResult(
                         CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */,
                         StreamingServiceTransaction.MESSAGE));
             }
@@ -437,7 +437,7 @@
         private void clearBinding() {
             resetController();
             if (!mFuture.isDone()) {
-                mFuture.complete(new VoipCallTransactionResult(
+                mFuture.complete(new CallTransactionResult(
                         CallException.CODE_ERROR_UNKNOWN /* TODO:: define error b/335703584 */,
                         "STREAMING_FAILED_SENDER_BINDING_ERROR"));
             } else {
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 134bc4f..ab350bc 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -131,8 +131,10 @@
 import com.android.server.telecom.callfiltering.IncomingCallFilterGraph;
 import com.android.server.telecom.callfiltering.IncomingCallFilterGraphProvider;
 import com.android.server.telecom.callredirection.CallRedirectionProcessor;
+import com.android.server.telecom.callsequencing.CallSequencingController;
 import com.android.server.telecom.components.ErrorDialogActivity;
 import com.android.server.telecom.components.TelecomBroadcastReceiver;
+import com.android.server.telecom.callsequencing.CallsManagerCallSequencingAdapter;
 import com.android.server.telecom.flags.FeatureFlags;
 import com.android.server.telecom.metrics.ErrorStats;
 import com.android.server.telecom.metrics.TelecomMetricsController;
@@ -144,8 +146,8 @@
 import com.android.server.telecom.ui.DisconnectedCallNotifier;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 import com.android.server.telecom.ui.ToastFactory;
-import com.android.server.telecom.voip.VoipCallMonitor;
-import com.android.server.telecom.voip.TransactionManager;
+import com.android.server.telecom.callsequencing.voip.VoipCallMonitor;
+import com.android.server.telecom.callsequencing.TransactionManager;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -491,6 +493,7 @@
     private final UserManager mUserManager;
     private final CallStreamingNotification mCallStreamingNotification;
     private final BlockedNumbersManager mBlockedNumbersManager;
+    private final CallsManagerCallSequencingAdapter mCallSequencingAdapter;
     private final FeatureFlags mFeatureFlags;
     private final com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;
 
@@ -740,6 +743,9 @@
         mBlockedNumbersManager = mFeatureFlags.telecomMainlineBlockedNumbersManager()
                 ? mContext.getSystemService(BlockedNumbersManager.class)
                 : null;
+        mCallSequencingAdapter = new CallsManagerCallSequencingAdapter(this,
+                new CallSequencingController(this, mFeatureFlags.enableCallSequencing()),
+                mFeatureFlags.enableCallSequencing());
 
         if (mFeatureFlags.useImprovedListenerOrder()) {
             mListeners.add(mInCallController);
@@ -2083,21 +2089,18 @@
 
 
         // This future checks the status of existing calls and attempts to make room for the
-        // outgoing call. The future returned by the inner method will usually be pre-completed --
-        // we only pause here if user interaction is required to disconnect a self-managed call.
-        // It runs after the account handle is set, independently of the phone account suggestion
-        // future.
-        CompletableFuture<Call> makeRoomForCall = setAccountHandle.thenComposeAsync(
+        // outgoing call.
+        CompletableFuture<Boolean> makeRoomForCall = setAccountHandle.thenComposeAsync(
                 potentialPhoneAccounts -> {
                     Log.i(CallsManager.this, "make room for outgoing call stage");
                     if (mMmiUtils.isPotentialInCallMMICode(handle) && !isSelfManaged) {
-                        return CompletableFuture.completedFuture(finalCall);
+                        return CompletableFuture.completedFuture(true);
                     }
                     // If a call is being reused, then it has already passed the
                     // makeRoomForOutgoingCall check once and will fail the second time due to the
                     // call transitioning into the CONNECTING state.
                     if (isReusedCall) {
-                        return CompletableFuture.completedFuture(finalCall);
+                        return CompletableFuture.completedFuture(true);
                     } else {
                         Call reusableCall = reuseOutgoingCall(handle);
                         if (reusableCall != null) {
@@ -2124,48 +2127,75 @@
                                     finalCall.getTargetPhoneAccount(), finalCall);
                         }
                         finalCall.setStartFailCause(CallFailureCause.IN_EMERGENCY_CALL);
-                        return CompletableFuture.completedFuture(null);
+                        return CompletableFuture.completedFuture(false);
                     }
 
-                    // If we can not supportany more active calls, our options are to move a call
+                    // If we can not support any more active calls, our options are to move a call
                     // to hold, disconnect a call, or cancel this call altogether.
-                    boolean isRoomForCall = finalCall.isEmergencyCall() ?
-                            makeRoomForOutgoingEmergencyCall(finalCall) :
-                            makeRoomForOutgoingCall(finalCall);
-                    if (!isRoomForCall) {
-                        Call foregroundCall = getForegroundCall();
-                        Log.d(CallsManager.this, "No more room for outgoing call %s ", finalCall);
-                        if (foregroundCall.isSelfManaged()) {
-                            // If the ongoing call is a self-managed call, then prompt the user to
-                            // ask if they'd like to disconnect their ongoing call and place the
-                            // outgoing call.
-                            Log.i(CallsManager.this, "Prompting user to disconnect "
-                                    + "self-managed call");
-                            finalCall.setOriginalCallIntent(originalIntent);
-                            CompletableFuture<Call> completionFuture = new CompletableFuture<>();
-                            startCallConfirmation(finalCall, completionFuture);
-                            return completionFuture;
-                        } else {
-                            // If the ongoing call is a managed call, we will prevent the outgoing
-                            // call from dialing.
-                            if (isConference) {
-                                notifyCreateConferenceFailed(finalCall.getTargetPhoneAccount(),
-                                    finalCall);
-                            } else {
-                                notifyCreateConnectionFailed(
-                                        finalCall.getTargetPhoneAccount(), finalCall);
-                            }
+                    CompletableFuture<Boolean> isRoomForCallFuture =
+                            mCallSequencingAdapter.makeRoomForOutgoingCall(
+                                    finalCall.isEmergencyCall(), finalCall);
+                    isRoomForCallFuture.exceptionally((throwable -> {
+                        if (throwable != null) {
+                            Log.w(CallsManager.this,
+                                    "Exception thrown in makeRoomForOutgoing*Call, "
+                                            + "returning false. Ex:" + throwable);
                         }
-                        Log.i(CallsManager.this, "Aborting call since there's no room");
+                        return false;
+                    }));
+                    return isRoomForCallFuture;
+        }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSMCP", mLock));
+
+        // The future returned by the inner method will usually be pre-completed --
+        // we only pause here if user interaction is required to disconnect a self-managed call.
+        // It runs after the account handle is set, independently of the phone account suggestion
+        // future.
+        CompletableFuture<Call> makeRoomResultHandler = makeRoomForCall
+                .thenComposeAsync((isRoom) -> {
+                    // If we have an ongoing emergency call, we would have already notified
+                    // connection failure for the new call being placed. Catch this so we don't
+                    // resend it again.
+                    boolean hasOngoingEmergencyCall = !finalCall.isEmergencyCall()
+                            && isInEmergencyCall();
+                    if (isRoom) {
+                        return CompletableFuture.completedFuture(finalCall);
+                    } else if (hasOngoingEmergencyCall) {
                         return CompletableFuture.completedFuture(null);
                     }
-                    return CompletableFuture.completedFuture(finalCall);
-        }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSMCP", mLock));
+                    Call foregroundCall = getForegroundCall();
+                    Log.d(CallsManager.this, "No more room for outgoing call %s ",
+                            finalCall);
+                    if (foregroundCall.isSelfManaged()) {
+                        // If the ongoing call is a self-managed call, then prompt the
+                        // user to ask if they'd like to disconnect their ongoing call
+                        // and place the outgoing call.
+                        Log.i(CallsManager.this, "Prompting user to disconnect "
+                                + "self-managed call");
+                        finalCall.setOriginalCallIntent(originalIntent);
+                        CompletableFuture<Call> completionFuture =
+                                new CompletableFuture<>();
+                        startCallConfirmation(finalCall, completionFuture);
+                        return completionFuture;
+                    } else {
+                        // If the ongoing call is a managed call, we will prevent the
+                        // outgoing call from dialing.
+                        if (isConference) {
+                            notifyCreateConferenceFailed(
+                                    finalCall.getTargetPhoneAccount(),
+                                    finalCall);
+                        } else {
+                            notifyCreateConnectionFailed(
+                                    finalCall.getTargetPhoneAccount(), finalCall);
+                        }
+                    }
+                    Log.i(CallsManager.this,  "Aborting call since there's no room");
+                    return CompletableFuture.completedFuture(null);
+                }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.mROC", mLock));
 
         // The outgoing call can be placed, go forward. This future glues together the results of
         // the account suggestion stage and the make room for call stage.
         CompletableFuture<Pair<Call, List<PhoneAccountSuggestion>>> preSelectStage =
-                makeRoomForCall.thenCombine(suggestionFuture, Pair::create);
+                makeRoomResultHandler.thenCombine(suggestionFuture, Pair::create);
         mLatestPreAccountSelectionFuture = preSelectStage;
 
         // This future takes the list of suggested accounts and the call and determines if more
@@ -3102,7 +3132,22 @@
     public void answerCall(Call call, int videoState) {
         if (!mCalls.contains(call)) {
             Log.i(this, "Request to answer a non-existent call %s", call);
-        } else if (call.isTransactionalCall()) {
+        }
+        mCallSequencingAdapter.answerCall(call, videoState);
+    }
+
+    /**
+     * CS: Hold any existing calls, request focus, and then set the call state to answered state.
+     * <p>
+     * T: Call TransactionalServiceWrapper, which then generates transactions to hold calls
+     * {@link #transactionHoldPotentialActiveCallForNewCall} and then move the active call focus
+     * {@link #requestNewCallFocusAndVerify} and notify the remote VOIP app of the call state
+     * moving to active.
+     * <p>
+     * Note: This is only used when {@link FeatureFlags#enableCallSequencing()} is false.
+     */
+    public void answerCallOld(Call call, int videoState) {
+        if (call.isTransactionalCall()) {
             // InCallAdapter is requesting to answer the given transactioanl call. Must get an ack
             // from the client via a transaction before answering.
             call.answer(videoState);
@@ -3436,10 +3481,10 @@
     public void holdCall(Call call) {
         if (!mCalls.contains(call)) {
             Log.w(this, "Unknown call (%s) asked to be put on hold", call);
-        } else {
-            Log.d(this, "Putting call on hold: (%s)", call);
-            call.hold();
+            return;
         }
+        Log.d(this, "Putting call on hold: (%s)", call);
+        mCallSequencingAdapter.holdCall(call);
     }
 
     /**
@@ -3451,44 +3496,57 @@
     public void unholdCall(Call call) {
         if (!mCalls.contains(call)) {
             Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
-        } else {
-            if (getOutgoingCall() != null) {
-                Log.w(this, "There is an outgoing call, so it is unable to unhold this call %s",
-                        call);
-                return;
-            }
-            Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
-            String activeCallId = null;
-            if (activeCall != null && !activeCall.isLocallyDisconnecting()) {
-                activeCallId = activeCall.getId();
-                if (canHold(activeCall)) {
-                    activeCall.hold("Swap to " + call.getId());
-                    Log.addEvent(activeCall, LogUtils.Events.SWAP, "To " + call.getId());
-                    Log.addEvent(call, LogUtils.Events.SWAP, "From " + activeCall.getId());
-                } else {
-                    // This call does not support hold. If it is from a different connection
-                    // service or connection manager, then disconnect it, otherwise invoke
-                    // call.hold() and allow the connection service or connection manager to handle
-                    // the situation.
-                    if (!areFromSameSource(activeCall, call)) {
-                        if (!activeCall.isEmergencyCall()) {
-                            activeCall.disconnect("Swap to " + call.getId());
-                        } else {
-                            Log.w(this, "unholdCall: % is an emergency call, aborting swap to %s",
-                                    activeCall.getId(), call.getId());
-                            // Don't unhold the call as requested; we don't want to drop an
-                            // emergency call.
-                            return;
-                        }
+            return;
+        }
+        if (getOutgoingCall() != null) {
+            Log.w(this, "There is an outgoing call, so it is unable to unhold this call %s",
+                    call);
+            return;
+        }
+        mCallSequencingAdapter.unholdCall(call);
+    }
+
+    /**
+     * Instructs telecom to hold any ongoing active calls and bring this call to the active state.
+     * <p>
+     * Note: This is only used when {@link FeatureFlags#enableCallSequencing()} is false.
+     */
+    public void unholdCallOld(Call call) {
+        Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
+        String activeCallId = null;
+        if (activeCall != null && !activeCall.isLocallyDisconnecting()) {
+            activeCallId = activeCall.getId();
+            if (canHold(activeCall)) {
+                activeCall.hold("Swap to " + call.getId());
+                Log.addEvent(activeCall, LogUtils.Events.SWAP, "To " + call.getId());
+                Log.addEvent(call, LogUtils.Events.SWAP, "From " + activeCall.getId());
+            } else {
+                // This call does not support hold. If it is from a different connection
+                // service or connection manager, then disconnect it, otherwise invoke
+                // call.hold() and allow the connection service or connection manager to handle
+                // the situation.
+                if (!areFromSameSource(activeCall, call)) {
+                    if (!activeCall.isEmergencyCall()) {
+                        activeCall.disconnect("Swap to " + call.getId());
                     } else {
-                        activeCall.hold("Swap to " + call.getId());
+                        Log.w(this, "unholdCall: % is an emergency call, aborting swap to %s",
+                                activeCall.getId(), call.getId());
+                        // Don't unhold the call as requested; we don't want to drop an
+                        // emergency call.
+                        return;
                     }
+                } else {
+                    activeCall.hold("Swap to " + call.getId());
                 }
             }
-            mConnectionSvrFocusMgr.requestFocus(
-                    call,
-                    new RequestCallback(new ActionUnHoldCall(call, activeCallId)));
         }
+        requestActionUnholdCall(call, activeCallId);
+    }
+
+    public void requestActionUnholdCall(Call call, String activeCallId) {
+        mConnectionSvrFocusMgr.requestFocus(
+                call,
+                new RequestCallback(new ActionUnHoldCall(call, activeCallId)));
     }
 
     @Override
@@ -3857,6 +3915,11 @@
         maybeMoveToSpeakerPhone(call);
     }
 
+    void requestFocusActionAnswerCall(Call call, int videoState) {
+        mConnectionSvrFocusMgr.requestFocus(call, new CallsManager.RequestCallback(
+                new CallsManager.ActionAnswerCall(call, videoState)));
+    }
+
     /**
      * Returns true if the active call is held.
      */
@@ -3998,6 +4061,12 @@
         return supportsHold(activeCall) && areFromSameSource(activeCall, call);
     }
 
+    /**
+     * CS: Mark a call as active. If the call is self-mangaed, we will also hold any active call
+     * before moving the self-managed call to active.
+     * <p>
+     * Note: Only used when {@link FeatureFlags#enableCallSequencing()} is false.
+     */
     @VisibleForTesting
     public void markCallAsActive(Call call) {
         Log.i(this, "markCallAsActive, isSelfManaged: " + call.isSelfManaged());
@@ -4032,6 +4101,11 @@
         }
     }
 
+    /**
+     * Mark a call as on hold after the hold operation has already completed.
+     * <p>
+     * Note: only used when {@link FeatureFlags#enableCallSequencing()} is false.
+     */
     public void markCallAsOnHold(Call call) {
         setCallState(call, CallState.ON_HOLD, "on-hold set explicitly");
     }
@@ -4207,12 +4281,24 @@
     private void doRemoval(Call call) {
         call.maybeCleanupHandover();
         removeCall(call);
+        boolean isLocallyDisconnecting = mLocallyDisconnectingCalls.contains(call);
+        mLocallyDisconnectingCalls.remove(call);
+        mCallSequencingAdapter.unholdCallForRemoval(call, isLocallyDisconnecting);
+    }
+
+    /**
+     * Move the held call to foreground in the event that there is a held call and the disconnected
+     * call was disconnected locally or the held call has no way to auto-unhold because it does not
+     * support hold capability.
+     * <p>
+     * Note: This is only used when {@link FeatureFlags#enableCallSequencing()} is set to false.
+     */
+    public void maybeMoveHeldCallToForeground(Call removedCall, boolean isLocallyDisconnecting) {
         Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall();
-        if (mLocallyDisconnectingCalls.contains(call)) {
-            boolean isDisconnectingChildCall = call.isDisconnectingChildCall();
-            Log.v(this, "performRemoval: isDisconnectingChildCall = "
-                    + isDisconnectingChildCall + "call -> %s", call);
-            mLocallyDisconnectingCalls.remove(call);
+        if (isLocallyDisconnecting) {
+            boolean isDisconnectingChildCall = removedCall.isDisconnectingChildCall();
+            Log.v(this, "maybeMoveHeldCallToForeground: isDisconnectingChildCall = "
+                    + isDisconnectingChildCall + "call -> %s", removedCall);
             // Auto-unhold the foreground call due to a locally disconnected call, except if the
             // call which was disconnected is a member of a conference (don't want to auto
             // un-hold the conference if we remove a member of the conference).
@@ -4221,7 +4307,8 @@
             // implementations, especially if one is managed and the other is a VoIP CS.
             if (!isDisconnectingChildCall && foregroundCall != null
                     && foregroundCall.getState() == CallState.ON_HOLD
-                    && areFromSameSource(foregroundCall, call)) {
+                    && areFromSameSource(foregroundCall, removedCall)) {
+
                 foregroundCall.unhold();
             }
         } else if (foregroundCall != null &&
@@ -4231,8 +4318,8 @@
             // The new foreground call is on hold, however the carrier does not display the hold
             // button in the UI.  Therefore, we need to auto unhold the held call since the user
             // has no means of unholding it themselves.
-            Log.i(this, "performRemoval: Auto-unholding held foreground call (call doesn't "
-                    + "support hold)");
+            Log.i(this, "maybeMoveHeldCallToForeground: Auto-unholding held foreground call (call "
+                    + "doesn't support hold)");
             foregroundCall.unhold();
         }
     }
@@ -4456,6 +4543,10 @@
         return getFirstCallWithState(null, states);
     }
 
+    public Call getFirstCallWithLiveState() {
+        return getFirstCallWithState(null, LIVE_CALL_STATES);
+    }
+
     @VisibleForTesting
     public PhoneNumberUtilsAdapter getPhoneNumberUtilsAdapter() {
         return mPhoneNumberUtilsAdapter;
@@ -5037,7 +5128,7 @@
         return (int) callsStream.count();
     }
 
-    private boolean hasMaximumLiveCalls(Call exceptCall) {
+    public boolean hasMaximumLiveCalls(Call exceptCall) {
         return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL,
                 exceptCall, null /* phoneAccountHandle*/, LIVE_CALL_STATES);
     }
@@ -5181,6 +5272,14 @@
                 && incomingCall.getHandoverSourceCall() == null;
     }
 
+    /**
+     * Make room for a pending outgoing emergency {@link Call}.
+     * <p>
+     * Note: This method is only applicable when {@link FeatureFlags#enableCallSequencing()}
+     * is false.
+     * @param call The new pending outgoing call.
+     * @return true if room was made, false if no room could be made.
+     */
     @VisibleForTesting
     public boolean makeRoomForOutgoingEmergencyCall(Call emergencyCall) {
         // Always disconnect any ringing/incoming calls when an emergency call is placed to minimize
@@ -5347,6 +5446,14 @@
         return false;
     }
 
+    /**
+     * Make room for a pending outgoing {@link Call}.
+     * <p>
+     * Note: This method is only applicable when {@link FeatureFlags#enableCallSequencing()}
+     * is false.
+     * @param call The new pending outgoing call.
+     * @return true if room was made, false if no room could be made.
+     */
     @VisibleForTesting
     public boolean makeRoomForOutgoingCall(Call call) {
         // Already room!
@@ -6510,7 +6617,7 @@
                 call.can(Connection.CAPABILITY_HOLD)) && call.getState() != CallState.DIALING;
     }
 
-    private boolean supportsHold(Call call) {
+    public boolean supportsHold(Call call) {
         return call.can(Connection.CAPABILITY_SUPPORT_HOLD);
     }
 
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index e6a6dd6..1075ff7 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -86,11 +86,11 @@
 import com.android.server.telecom.metrics.ApiStats;
 import com.android.server.telecom.metrics.TelecomMetricsController;
 import com.android.server.telecom.settings.BlockedNumbersActivity;
-import com.android.server.telecom.voip.IncomingCallTransaction;
-import com.android.server.telecom.voip.OutgoingCallTransaction;
-import com.android.server.telecom.voip.TransactionManager;
-import com.android.server.telecom.voip.VoipCallTransaction;
-import com.android.server.telecom.voip.VoipCallTransactionResult;
+import com.android.server.telecom.callsequencing.voip.IncomingCallTransaction;
+import com.android.server.telecom.callsequencing.voip.OutgoingCallTransaction;
+import com.android.server.telecom.callsequencing.TransactionManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -192,7 +192,7 @@
                 extras.putInt(CallAttributes.CALLER_UID_KEY, Binder.getCallingUid());
                 extras.putInt(CallAttributes.CALLER_PID_KEY, Binder.getCallingPid());
 
-                VoipCallTransaction transaction = null;
+                CallTransaction transaction = null;
                 // create transaction based on the call direction
                 switch (callAttributes.getDirection()) {
                     case DIRECTION_OUTGOING:
@@ -212,7 +212,7 @@
 
                 mTransactionManager.addTransaction(transaction, new OutcomeReceiver<>() {
                     @Override
-                    public void onResult(VoipCallTransactionResult result) {
+                    public void onResult(CallTransactionResult result) {
                         Log.d(TAG, "addCall: onResult");
                         Call call = result.getCall();
 
@@ -2951,7 +2951,7 @@
         });
 
         mTransactionManager = TransactionManager.getInstance();
-        mTransactionalServiceRepository = new TransactionalServiceRepository();
+        mTransactionalServiceRepository = new TransactionalServiceRepository(mFeatureFlags);
         mBlockedNumbersManager = mFeatureFlags.telecomMainlineBlockedNumbersManager()
                 ? mContext.getSystemService(BlockedNumbersManager.class)
                 : null;
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 34afdb0..94bea42 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -55,7 +55,7 @@
 import com.android.server.telecom.ui.IncomingCallNotifier;
 import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
 import com.android.server.telecom.ui.ToastFactory;
-import com.android.server.telecom.voip.TransactionManager;
+import com.android.server.telecom.callsequencing.TransactionManager;
 
 import java.io.FileNotFoundException;
 import java.io.InputStream;
diff --git a/src/com/android/server/telecom/TransactionalServiceRepository.java b/src/com/android/server/telecom/TransactionalServiceRepository.java
index 793840e..5ae459e 100644
--- a/src/com/android/server/telecom/TransactionalServiceRepository.java
+++ b/src/com/android/server/telecom/TransactionalServiceRepository.java
@@ -20,6 +20,8 @@
 import android.telecom.PhoneAccountHandle;
 
 import com.android.internal.telecom.ICallEventCallback;
+import com.android.server.telecom.flags.FeatureFlags;
+import com.android.server.telecom.callsequencing.TransactionManager;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -32,8 +34,10 @@
     private static final String TAG = TransactionalServiceRepository.class.getSimpleName();
     private static final Map<PhoneAccountHandle, TransactionalServiceWrapper> mServiceLookupTable =
             new HashMap<>();
+    private final FeatureFlags mFlags;
 
-    public TransactionalServiceRepository() {
+    public TransactionalServiceRepository(FeatureFlags flags) {
+        mFlags = flags;
     }
 
     public TransactionalServiceWrapper addNewCallForTransactionalServiceWrapper
@@ -45,7 +49,8 @@
         if (!hasExistingServiceWrapper(phoneAccountHandle)) {
             Log.d(TAG, "creating a new TSW; handle=[%s]", phoneAccountHandle);
             service = new TransactionalServiceWrapper(callEventCallback,
-                    callsManager, phoneAccountHandle, call, this);
+                    callsManager, phoneAccountHandle, call, this,
+                    TransactionManager.getInstance(), mFlags.enableCallSequencing());
         } else {
             Log.d(TAG, "add a new call to an existing TSW; handle=[%s]", phoneAccountHandle);
             service = getTransactionalServiceWrapper(phoneAccountHandle);
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index b73de23..cf5ef41 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -39,23 +39,18 @@
 
 import com.android.internal.telecom.ICallControl;
 import com.android.internal.telecom.ICallEventCallback;
-import com.android.server.telecom.voip.CallEventCallbackAckTransaction;
-import com.android.server.telecom.voip.EndpointChangeTransaction;
-import com.android.server.telecom.voip.HoldCallTransaction;
-import com.android.server.telecom.voip.EndCallTransaction;
-import com.android.server.telecom.voip.MaybeHoldCallForNewCallTransaction;
-import com.android.server.telecom.voip.RequestNewActiveCallTransaction;
-import com.android.server.telecom.voip.SerialTransaction;
-import com.android.server.telecom.voip.SetMuteStateTransaction;
-import com.android.server.telecom.voip.RequestVideoStateTransaction;
-import com.android.server.telecom.voip.TransactionManager;
-import com.android.server.telecom.voip.VoipCallTransaction;
-import com.android.server.telecom.voip.VoipCallTransactionResult;
+import com.android.server.telecom.callsequencing.TransactionalCallSequencingAdapter;
+import com.android.server.telecom.callsequencing.voip.CallEventCallbackAckTransaction;
+import com.android.server.telecom.callsequencing.voip.EndpointChangeTransaction;
+import com.android.server.telecom.callsequencing.voip.SetMuteStateTransaction;
+import com.android.server.telecom.callsequencing.voip.RequestVideoStateTransaction;
+import com.android.server.telecom.callsequencing.TransactionManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Locale;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -73,6 +68,8 @@
     public static final String DISCONNECT = "Disconnect";
     public static final String START_STREAMING = "StartStreaming";
     public static final String REQUEST_VIDEO_STATE = "RequestVideoState";
+    public static final String SET_MUTE_STATE = "SetMuteState";
+    public static final String CALL_ENDPOINT_CHANGE = "CallEndpointChange";
 
     // CallEventCallback : Telecom --> Client (ex. voip app)
     public static final String ON_SET_ACTIVE = "onSetActive";
@@ -80,6 +77,7 @@
     public static final String ON_ANSWER = "onAnswer";
     public static final String ON_DISCONNECT = "onDisconnect";
     public static final String ON_STREAMING_STARTED = "onStreamingStarted";
+    public static final String STOP_STREAMING = "stopStreaming";
 
     private final CallsManager mCallsManager;
     private final ICallEventCallback mICallEventCallback;
@@ -93,6 +91,7 @@
     // needs to be non-final for testing
     private TransactionManager mTransactionManager;
     private CallStreamingController mStreamingController;
+    private final TransactionalCallSequencingAdapter mCallSequencingAdapter;
 
 
     // Each TransactionalServiceWrapper should have their own Binder.DeathRecipient to clean up
@@ -108,26 +107,24 @@
 
     public TransactionalServiceWrapper(ICallEventCallback callEventCallback,
             CallsManager callsManager, PhoneAccountHandle phoneAccountHandle, Call call,
-            TransactionalServiceRepository repo) {
+            TransactionalServiceRepository repo, TransactionManager transactionManager,
+            boolean isCallSequencingEnabled) {
         // passed args
         mICallEventCallback = callEventCallback;
         mCallsManager = callsManager;
         mPhoneAccountHandle = phoneAccountHandle;
         mTrackedCalls.put(call.getId(), call); // service is now tracking its first call
         mRepository = repo;
+        mTransactionManager = transactionManager;
         // init instance vars
         mPackageName = phoneAccountHandle.getComponentName().getPackageName();
-        mTransactionManager = TransactionManager.getInstance();
         mStreamingController = mCallsManager.getCallStreamingController();
         mLock = mCallsManager.getLock();
+        mCallSequencingAdapter = new TransactionalCallSequencingAdapter(mTransactionManager,
+                mCallsManager, isCallSequencingEnabled);
         setDeathRecipient(callEventCallback);
     }
 
-    @VisibleForTesting
-    public void setTransactionManager(TransactionManager transactionManager) {
-        mTransactionManager = transactionManager;
-    }
-
     public TransactionManager getTransactionManager() {
         return mTransactionManager;
     }
@@ -170,11 +167,7 @@
     }
 
     private void cleanupTransactionalServiceWrapper() {
-        for (Call call : mTrackedCalls.values()) {
-            mCallsManager.markCallAsDisconnected(call,
-                    new DisconnectCause(DisconnectCause.ERROR, "process died"));
-            mCallsManager.removeCall(call); // This will clear mTrackedCalls && ClientTWS
-        }
+        mCallSequencingAdapter.cleanup(mTrackedCalls.values());
     }
 
     /***
@@ -184,8 +177,7 @@
      */
     private final ICallControl mICallControl = new ICallControl.Stub() {
         @Override
-        public void setActive(String callId, android.os.ResultReceiver callback)
-                throws RemoteException {
+        public void setActive(String callId, android.os.ResultReceiver callback) {
             long token = Binder.clearCallingIdentity();
             try {
                 Log.startSession("TSW.sA");
@@ -197,8 +189,8 @@
         }
 
         @Override
-        public void answer(int videoState, String callId, android.os.ResultReceiver callback)
-                throws RemoteException {
+
+        public void answer(int videoState, String callId, android.os.ResultReceiver callback) {
             long token = Binder.clearCallingIdentity();
             try {
                 Log.startSession("TSW.a");
@@ -210,8 +202,7 @@
         }
 
         @Override
-        public void setInactive(String callId, android.os.ResultReceiver callback)
-                throws RemoteException {
+        public void setInactive(String callId, android.os.ResultReceiver callback) {
             long token = Binder.clearCallingIdentity();
             try {
                 Log.startSession("TSW.sI");
@@ -224,8 +215,7 @@
 
         @Override
         public void disconnect(String callId, DisconnectCause disconnectCause,
-                android.os.ResultReceiver callback)
-                throws RemoteException {
+                android.os.ResultReceiver callback) {
             long token = Binder.clearCallingIdentity();
             try {
                 Log.startSession("TSW.d");
@@ -237,12 +227,11 @@
         }
 
         @Override
-        public void setMuteState(boolean isMuted, android.os.ResultReceiver callback)
-                throws RemoteException {
+        public void setMuteState(boolean isMuted, android.os.ResultReceiver callback) {
             long token = Binder.clearCallingIdentity();
             try {
                 Log.startSession("TSW.sMS");
-                addTransactionsToManager(
+                addTransactionsToManager(SET_MUTE_STATE,
                         new SetMuteStateTransaction(mCallsManager, isMuted), callback);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -251,8 +240,7 @@
         }
 
         @Override
-        public void startCallStreaming(String callId, android.os.ResultReceiver callback)
-                throws RemoteException {
+        public void startCallStreaming(String callId, android.os.ResultReceiver callback) {
             long token = Binder.clearCallingIdentity();
             try {
                 Log.startSession("TSW.sCS");
@@ -264,8 +252,7 @@
         }
 
         @Override
-        public void requestVideoState(int videoState, String callId, ResultReceiver callback)
-                throws RemoteException {
+        public void requestVideoState(int videoState, String callId, ResultReceiver callback) {
             long token = Binder.clearCallingIdentity();
             try {
                 Log.startSession("TSW.rVS");
@@ -283,27 +270,29 @@
             if (call != null) {
                 switch (action) {
                     case SET_ACTIVE:
-                        handleCallControlNewCallFocusTransactions(call, SET_ACTIVE,
-                                false /* isAnswer */, 0/*VideoState (ignored)*/, callback);
+                        mCallSequencingAdapter.setActive(call,
+                                getCompleteReceiver(action, callback));
                         break;
                     case ANSWER:
-                        handleCallControlNewCallFocusTransactions(call, ANSWER,
-                                true /* isAnswer */, (int) objects[0] /*VideoState*/, callback);
+                        mCallSequencingAdapter.setAnswered(call, (int) objects[0] /*VideoState*/,
+                                getCompleteReceiver(action, callback));
                         break;
                     case DISCONNECT:
-                        addTransactionsToManager(new EndCallTransaction(mCallsManager,
-                                (DisconnectCause) objects[0], call), callback);
+                        DisconnectCause dc = (DisconnectCause) objects[0];
+                        mCallSequencingAdapter.setDisconnected(call, dc,
+                                getCompleteReceiver(action, callback));
                         break;
                     case SET_INACTIVE:
-                        addTransactionsToManager(
-                                new HoldCallTransaction(mCallsManager, call), callback);
+                        mCallSequencingAdapter.setInactive(call,
+                                getCompleteReceiver(action,callback));
                         break;
                     case START_STREAMING:
-                        addTransactionsToManager(mStreamingController.getStartStreamingTransaction(mCallsManager,
-                                TransactionalServiceWrapper.this, call, mLock), callback);
+                        addTransactionsToManager(action,
+                                mStreamingController.getStartStreamingTransaction(mCallsManager,
+                                TransactionalServiceWrapper.this, call, mLock),  callback);
                         break;
                     case REQUEST_VIDEO_STATE:
-                        addTransactionsToManager(
+                        addTransactionsToManager(action,
                                 new RequestVideoStateTransaction(mCallsManager, call,
                                         (int) objects[0]), callback);
                         break;
@@ -321,40 +310,13 @@
             }
         }
 
-        // The client is request their VoIP call state go ACTIVE/ANSWERED.
-        // This request is originating from the VoIP application.
-        private void handleCallControlNewCallFocusTransactions(Call call, String action,
-                boolean isAnswer, int potentiallyNewVideoState, ResultReceiver callback) {
-            mTransactionManager.addTransaction(
-                    createSetActiveTransactions(call, true /* isCallControlRequest */),
-                    new OutcomeReceiver<>() {
-                        @Override
-                        public void onResult(VoipCallTransactionResult result) {
-                            Log.i(TAG, String.format(Locale.US,
-                                    "%s: onResult: callId=[%s]", action, call.getId()));
-                            if (isAnswer) {
-                                call.setVideoState(potentiallyNewVideoState);
-                            }
-                            callback.send(TELECOM_TRANSACTION_SUCCESS, new Bundle());
-                        }
-
-                        @Override
-                        public void onError(CallException exception) {
-                            Bundle extras = new Bundle();
-                            extras.putParcelable(TRANSACTION_EXCEPTION_KEY, exception);
-                            callback.send(exception == null ? CallException.CODE_ERROR_UNKNOWN :
-                                    exception.getCode(), extras);
-                        }
-                    });
-        }
-
         @Override
         public void requestCallEndpointChange(CallEndpoint endpoint, ResultReceiver callback) {
             long token = Binder.clearCallingIdentity();
             try {
                 Log.startSession("TSW.rCEC");
-                addTransactionsToManager(new EndpointChangeTransaction(endpoint, mCallsManager),
-                        callback);
+                addTransactionsToManager(CALL_ENDPOINT_CHANGE,
+                        new EndpointChangeTransaction(endpoint, mCallsManager), callback);
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -384,26 +346,31 @@
         }
     };
 
-    private void addTransactionsToManager(VoipCallTransaction transaction,
+    private void addTransactionsToManager(String action, CallTransaction transaction,
             ResultReceiver callback) {
         Log.d(TAG, "addTransactionsToManager");
+        CompletableFuture<Boolean> transactionResult = mTransactionManager
+                .addTransaction(transaction, getCompleteReceiver(action, callback));
+    }
 
-        mTransactionManager.addTransaction(transaction, new OutcomeReceiver<>() {
+    private OutcomeReceiver<CallTransactionResult, CallException> getCompleteReceiver(
+            String action, ResultReceiver callback) {
+        return new OutcomeReceiver<>() {
             @Override
-            public void onResult(VoipCallTransactionResult result) {
-                Log.d(TAG, "addTransactionsToManager: onResult:");
+            public void onResult(CallTransactionResult result) {
+                Log.d(TAG, "completeReceiver: onResult[" + action + "]:" + result);
                 callback.send(TELECOM_TRANSACTION_SUCCESS, new Bundle());
             }
 
             @Override
             public void onError(CallException exception) {
-                Log.d(TAG, "addTransactionsToManager: onError");
+                Log.d(TAG, "completeReceiver: onError[" + action + "]" + exception);
                 Bundle extras = new Bundle();
                 extras.putParcelable(TRANSACTION_EXCEPTION_KEY, exception);
                 callback.send(exception == null ? CallException.CODE_ERROR_UNKNOWN :
                         exception.getCode(), extras);
             }
-        });
+        };
     }
 
     public ICallControl getICallControl() {
@@ -416,89 +383,53 @@
      **********************************************************************************************
      */
 
-    public void onSetActive(Call call) {
+    public CompletableFuture<Boolean> onSetActive(Call call) {
+        CallTransaction callTransaction = new CallEventCallbackAckTransaction(
+                mICallEventCallback, ON_SET_ACTIVE, call.getId(), mLock);
+        CompletableFuture<Boolean> onSetActiveFuture;
         try {
             Log.startSession("TSW.oSA");
             Log.d(TAG, String.format(Locale.US, "onSetActive: callId=[%s]", call.getId()));
-            handleCallEventCallbackNewFocus(call, ON_SET_ACTIVE, false /*isAnswerRequest*/,
-                    0 /*VideoState*/);
+            onSetActiveFuture = mCallSequencingAdapter.onSetActive(call,
+                    callTransaction, result ->
+                            Log.i(TAG, String.format(Locale.US,
+                                    "%s: onResult: callId=[%s], result=[%s]", ON_SET_ACTIVE,
+                                    call.getId(), result)));
         } finally {
             Log.endSession();
         }
+        return onSetActiveFuture;
     }
 
     public void onAnswer(Call call, int videoState) {
         try {
             Log.startSession("TSW.oA");
             Log.d(TAG, String.format(Locale.US, "onAnswer: callId=[%s]", call.getId()));
-            handleCallEventCallbackNewFocus(call, ON_ANSWER, true /*isAnswerRequest*/,
-                    videoState /*VideoState*/);
+            mCallSequencingAdapter.onSetAnswered(call, videoState,
+                    new CallEventCallbackAckTransaction(mICallEventCallback,
+                            ON_ANSWER, call.getId(), videoState, mLock),
+                    result -> Log.i(TAG, String.format(Locale.US,
+                            "%s: onResult: callId=[%s], result=[%s]",
+                            ON_ANSWER, call.getId(), result)));
         } finally {
             Log.endSession();
         }
     }
 
-    // handle a CallEventCallback to set a call ACTIVE/ANSWERED. Must get ack from client since the
-    // request has come from another source (ex. Android Auto is requesting a call to go active)
-    private void handleCallEventCallbackNewFocus(Call call, String action, boolean isAnswerRequest,
-            int potentiallyNewVideoState) {
-        // save CallsManager state before sending client state changes
-        Call foregroundCallBeforeSwap = mCallsManager.getForegroundCall();
-        boolean wasActive = foregroundCallBeforeSwap != null && foregroundCallBeforeSwap.isActive();
-
-        SerialTransaction serialTransactions = createSetActiveTransactions(call,
-                false /* isCallControlRequest */);
-        // 3. get ack from client (that the requested call can go active)
-        if (isAnswerRequest) {
-            serialTransactions.appendTransaction(
-                    new CallEventCallbackAckTransaction(mICallEventCallback,
-                            action, call.getId(), potentiallyNewVideoState, mLock));
-        } else {
-            serialTransactions.appendTransaction(
-                    new CallEventCallbackAckTransaction(mICallEventCallback,
-                            action, call.getId(), mLock));
-        }
-
-        // do CallsManager workload before asking client and
-        //   reset CallsManager state if client does NOT ack
-        mTransactionManager.addTransaction(serialTransactions,
-                new OutcomeReceiver<>() {
-                    @Override
-                    public void onResult(VoipCallTransactionResult result) {
-                        Log.i(TAG, String.format(Locale.US,
-                                "%s: onResult: callId=[%s]", action, call.getId()));
-                        if (isAnswerRequest) {
-                            call.setVideoState(potentiallyNewVideoState);
-                        }
-                    }
-
-                    @Override
-                    public void onError(CallException exception) {
-                        if (isAnswerRequest) {
-                            // This also sends the signal to untrack from TSW and the client_TSW
-                            removeCallFromCallsManager(call,
-                                    new DisconnectCause(DisconnectCause.REJECTED,
-                                            "client rejected to answer the call;"
-                                                    + " force disconnecting"));
-                        } else {
-                            mCallsManager.markCallAsOnHold(call);
-                        }
-                        maybeResetForegroundCall(foregroundCallBeforeSwap, wasActive);
-                    }
-                });
-    }
-
-
-    public void onSetInactive(Call call) {
+    public CompletableFuture<Boolean> onSetInactive(Call call) {
+        CallTransaction callTransaction = new CallEventCallbackAckTransaction(
+                mICallEventCallback, ON_SET_INACTIVE, call.getId(), mLock);
+        CompletableFuture<Boolean> onSetInactiveFuture;
         try {
             Log.startSession("TSW.oSI");
             Log.i(TAG, String.format(Locale.US, "onSetInactive: callId=[%s]", call.getId()));
-            mTransactionManager.addTransaction(
-                    new CallEventCallbackAckTransaction(mICallEventCallback,
-                            ON_SET_INACTIVE, call.getId(), mLock), new OutcomeReceiver<>() {
+            onSetInactiveFuture = mCallSequencingAdapter.onSetInactive(call,
+                    callTransaction, new OutcomeReceiver<>() {
                         @Override
-                        public void onResult(VoipCallTransactionResult result) {
-                            mCallsManager.markCallAsOnHold(call);
+                        public void onResult(CallTransactionResult result) {
+                            Log.i(TAG, String.format(Locale.US, "onSetInactive: callId=[%s]"
+                                            + ", result=[%s]",
+                                    call.getId(), result));
                         }
 
                         @Override
@@ -510,30 +441,26 @@
         } finally {
             Log.endSession();
         }
+        return onSetInactiveFuture;
     }
 
-    public void onDisconnect(Call call, DisconnectCause cause) {
+    public CompletableFuture<Boolean> onDisconnect(Call call,
+            DisconnectCause cause) {
+        CallTransaction callTransaction = new CallEventCallbackAckTransaction(
+                mICallEventCallback, ON_DISCONNECT, call.getId(), cause, mLock);
+        CompletableFuture<Boolean> onDisconnectFuture;
         try {
             Log.startSession("TSW.oD");
             Log.d(TAG, String.format(Locale.US, "onDisconnect: callId=[%s]", call.getId()));
-
-            mTransactionManager.addTransaction(
-                    new CallEventCallbackAckTransaction(mICallEventCallback, ON_DISCONNECT,
-                            call.getId(), cause, mLock), new OutcomeReceiver<>() {
-                        @Override
-                        public void onResult(VoipCallTransactionResult result) {
-                            removeCallFromCallsManager(call, cause);
-                        }
-
-                        @Override
-                        public void onError(CallException exception) {
-                            removeCallFromCallsManager(call, cause);
-                        }
-                    }
-            );
+            onDisconnectFuture = mCallSequencingAdapter.onSetDisconnected(call, cause,
+                    callTransaction,
+                    result -> Log.i(TAG, String.format(Locale.US,
+                            "%s: onResult: callId=[%s], result=[%s]",
+                            ON_DISCONNECT, call.getId(), result)));
         } finally {
             Log.endSession();
         }
+        return onDisconnectFuture;
     }
 
     public void onCallStreamingStarted(Call call) {
@@ -546,7 +473,7 @@
                     new CallEventCallbackAckTransaction(mICallEventCallback, ON_STREAMING_STARTED,
                             call.getId(), mLock), new OutcomeReceiver<>() {
                         @Override
-                        public void onResult(VoipCallTransactionResult result) {
+                        public void onResult(CallTransactionResult result) {
                         }
 
                         @Override
@@ -641,35 +568,6 @@
      **                                Helpers                                                  **
      **********************************************************************************************
      */
-    private void maybeResetForegroundCall(Call foregroundCallBeforeSwap, boolean wasActive) {
-        if (foregroundCallBeforeSwap == null) {
-            return;
-        }
-        if (wasActive && !foregroundCallBeforeSwap.isActive()) {
-            mCallsManager.markCallAsActive(foregroundCallBeforeSwap);
-        }
-    }
-
-    private void removeCallFromCallsManager(Call call, DisconnectCause cause) {
-        if (cause.getCode() != DisconnectCause.REJECTED) {
-            mCallsManager.markCallAsDisconnected(call, cause);
-        }
-        mCallsManager.removeCall(call);
-    }
-
-    private SerialTransaction createSetActiveTransactions(Call call, boolean isCallControlRequest) {
-        // create list for multiple transactions
-        List<VoipCallTransaction> transactions = new ArrayList<>();
-
-        // potentially hold the current active call in order to set a new call (active/answered)
-        transactions.add(
-                new MaybeHoldCallForNewCallTransaction(mCallsManager, call, isCallControlRequest));
-        // And request a new focus call update
-        transactions.add(new RequestNewActiveCallTransaction(mCallsManager, call));
-
-        return new SerialTransaction(transactions, mLock);
-    }
-
     private void setDeathRecipient(ICallEventCallback callEventCallback) {
         try {
             callEventCallback.asBinder().linkToDeath(mAppDeathListener, 0);
@@ -720,9 +618,10 @@
     public void stopCallStreaming(Call call) {
         Log.i(this, "stopCallStreaming; callid=%s", call.getId());
         if (call != null && call.isStreaming()) {
-            VoipCallTransaction stopStreamingTransaction = mStreamingController
+            CallTransaction stopStreamingTransaction = mStreamingController
                     .getStopStreamingTransaction(call, mLock);
-            addTransactionsToManager(stopStreamingTransaction, new ResultReceiver(null));
+            addTransactionsToManager(STOP_STREAMING, stopStreamingTransaction,
+                    new ResultReceiver(null));
         }
     }
 }
diff --git a/src/com/android/server/telecom/callsequencing/CallSequencingController.java b/src/com/android/server/telecom/callsequencing/CallSequencingController.java
new file mode 100644
index 0000000..2f0ae45
--- /dev/null
+++ b/src/com/android/server/telecom/callsequencing/CallSequencingController.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 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.callsequencing;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Controls the sequencing between calls when moving between the user ACTIVE (RINGING/ACTIVE) and
+ * user INACTIVE (INCOMING/HOLD/DISCONNECTED) states.
+ */
+public class CallSequencingController {
+//    private final CallsManager mCallsManager;
+    private final TransactionManager mTransactionManager;
+//    private final Handler mHandler;
+//    private boolean mCallSequencingEnabled;
+
+    public CallSequencingController(CallsManager callsManager, boolean callSequencingEnabled) {
+//        mCallsManager = callsManager;
+        mTransactionManager = TransactionManager.getInstance();
+        HandlerThread handlerThread = new HandlerThread(this.toString());
+        handlerThread.start();
+//        mHandler = new Handler(handlerThread.getLooper());
+//        mCallSequencingEnabled = callSequencingEnabled;
+    }
+
+    public void answerCall(Call incomingCall, int videoState) {
+        // Todo: call sequencing logic (stubbed)
+    }
+
+//    private CompletableFuture<Boolean> holdActiveCallForNewCallWithSequencing(Call call) {
+//        // Todo: call sequencing logic (stubbed)
+//        return null;
+//    }
+
+    public void unholdCall(Call call) {
+        // Todo: call sequencing logic (stubbed)
+    }
+
+    public CompletableFuture<Boolean> makeRoomForOutgoingCall(boolean isEmergency, Call call) {
+        // Todo: call sequencing logic (stubbed)
+        return CompletableFuture.completedFuture(true);
+//        return isEmergency ? makeRoomForOutgoingEmergencyCall(call) : makeRoomForOutgoingCall(call);
+    }
+
+//    private CompletableFuture<Boolean> makeRoomForOutgoingEmergencyCall(Call emergencyCall) {
+//        // Todo: call sequencing logic (stubbed)
+//        return CompletableFuture.completedFuture(true);
+//    }
+
+//    private CompletableFuture<Boolean> makeRoomForOutgoingCall(Call call) {
+//        // Todo: call sequencing logic (stubbed)
+//        return CompletableFuture.completedFuture(true);
+//    }
+
+//    private void resetProcessingCallSequencing() {
+//        mTransactionManager.setProcessingCallSequencing(false);
+//    }
+
+    public CompletableFuture<Boolean> disconnectCall() {
+        return CompletableFuture.completedFuture(true);
+    }
+}
diff --git a/src/com/android/server/telecom/voip/VoipCallTransaction.java b/src/com/android/server/telecom/callsequencing/CallTransaction.java
similarity index 85%
rename from src/com/android/server/telecom/voip/VoipCallTransaction.java
rename to src/com/android/server/telecom/callsequencing/CallTransaction.java
index a589a6d..8d7da7c 100644
--- a/src/com/android/server/telecom/voip/VoipCallTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/CallTransaction.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing;
 
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -34,7 +34,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 
-public class VoipCallTransaction {
+public class CallTransaction {
     //TODO: add log events
     private static final long DEFAULT_TRANSACTION_TIMEOUT_MS = 5000L;
 
@@ -52,7 +52,7 @@
         private long mFinishedTimeNs = -1L;
         // If finished, did this transaction finish because it timed out?
         private boolean mIsTimedOut = false;
-        private VoipCallTransactionResult  mTransactionResult = null;
+        private CallTransactionResult  mTransactionResult = null;
 
         public Stats() {
             addedTimeStamp = LocalDateTime.now();
@@ -70,7 +70,7 @@
         /**
          * Mark the transaction as completed and record the time.
          */
-        public void markComplete(boolean isTimedOut, VoipCallTransactionResult result) {
+        public void markComplete(boolean isTimedOut, CallTransactionResult result) {
             if (mFinishedTimeNs > -1) return;
             mFinishedTimeNs = System.nanoTime();
             mIsTimedOut = isTimedOut;
@@ -124,7 +124,7 @@
          * @return the result if the transaction completed, null if it timed out or hasn't completed
          * yet.
          */
-        public VoipCallTransactionResult getTransactionResult() {
+        public CallTransactionResult getTransactionResult() {
             return mTransactionResult;
         }
     }
@@ -134,13 +134,13 @@
     private final HandlerThread mHandlerThread;
     protected final Handler mHandler;
     protected TransactionManager.TransactionCompleteListener mCompleteListener;
-    protected final List<VoipCallTransaction> mSubTransactions;
+    protected final List<CallTransaction> mSubTransactions;
     protected final TelecomSystem.SyncRoot mLock;
     protected final long mTransactionTimeoutMs;
     protected final Stats mStats;
 
-    public VoipCallTransaction(
-            List<VoipCallTransaction> subTransactions, TelecomSystem.SyncRoot lock,
+    public CallTransaction(
+            List<CallTransaction> subTransactions, TelecomSystem.SyncRoot lock,
             long timeoutMs) {
         mSubTransactions = subTransactions;
         mHandlerThread = new HandlerThread(this.toString());
@@ -151,15 +151,15 @@
         mStats = Flags.enableCallSequencing() ? new Stats() : null;
     }
 
-    public VoipCallTransaction(List<VoipCallTransaction> subTransactions,
+    public CallTransaction(List<CallTransaction> subTransactions,
             TelecomSystem.SyncRoot lock) {
         this(subTransactions, lock, DEFAULT_TRANSACTION_TIMEOUT_MS);
     }
-    public VoipCallTransaction(TelecomSystem.SyncRoot lock, long timeoutMs) {
+    public CallTransaction(TelecomSystem.SyncRoot lock, long timeoutMs) {
         this(null /* mSubTransactions */, lock, timeoutMs);
     }
 
-    public VoipCallTransaction(TelecomSystem.SyncRoot lock) {
+    public CallTransaction(TelecomSystem.SyncRoot lock) {
         this(null /* mSubTransactions */, lock);
     }
 
@@ -178,7 +178,7 @@
     }
 
     /**
-     * By default, this processes this transaction. For VoipCallTransactions with sub-transactions,
+     * By default, this processes this transaction. For CallTransaction with sub-transactions,
      * this implementation should be overwritten to handle also processing sub-transactions.
      */
     protected void processTransactions() {
@@ -187,7 +187,7 @@
 
     /**
      * This method is called when the transaction has finished either successfully or exceptionally.
-     * VoipCallTransactions that are extending this class should override this method to clean up
+     * CallTransaction that are extending this class should override this method to clean up
      * any leftover state.
      */
     protected void finishTransaction() {
@@ -199,7 +199,7 @@
                 mTransactionName + "@" + hashCode() + ".sT", mLock);
         CompletableFuture<Void> future = CompletableFuture.completedFuture(null);
         future.thenComposeAsync(this::processTransaction, executor)
-                .thenApplyAsync((Function<VoipCallTransactionResult, Void>) result -> {
+                .thenApplyAsync((Function<CallTransactionResult, Void>) result -> {
                     notifyListenersOfResult(result);
                     return null;
                 }, executor)
@@ -208,14 +208,14 @@
                     // Instead, propagate the failure to the other transactions immediately!
                     String errorMessage = throwable != null ? throwable.getMessage() :
                             "encountered an exception while processing " + mTransactionName;
-                    notifyListenersOfResult(new VoipCallTransactionResult(
+                    notifyListenersOfResult(new CallTransactionResult(
                             CallException.CODE_ERROR_UNKNOWN, errorMessage));
                     Log.e(this, throwable, "Error while executing transaction.");
                     return null;
                 }));
     }
 
-    protected void notifyListenersOfResult(VoipCallTransactionResult result){
+    protected void notifyListenersOfResult(CallTransactionResult result){
         mCompleted.set(true);
         finish(result);
         if (mCompleteListener != null) {
@@ -223,9 +223,9 @@
         }
     }
 
-    protected CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    protected CompletionStage<CallTransactionResult> processTransaction(Void v) {
         return CompletableFuture.completedFuture(
-                new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED, null));
+                new CallTransactionResult(CallTransactionResult.RESULT_SUCCEED, null));
     }
 
     public final void setCompleteListener(TransactionManager.TransactionCompleteListener listener) {
@@ -248,11 +248,11 @@
         return mHandler;
     }
 
-    public final void finish(VoipCallTransactionResult result) {
+    public final void finish(CallTransactionResult result) {
         finish(false, result);
     }
 
-    private void finish(boolean isTimedOut, VoipCallTransactionResult result) {
+    private void finish(boolean isTimedOut, CallTransactionResult result) {
         if (mStats != null) mStats.markComplete(isTimedOut, result);
         finishTransaction();
         // finish all sub transactions
diff --git a/src/com/android/server/telecom/voip/VoipCallTransactionResult.java b/src/com/android/server/telecom/callsequencing/CallTransactionResult.java
similarity index 66%
rename from src/com/android/server/telecom/voip/VoipCallTransactionResult.java
rename to src/com/android/server/telecom/callsequencing/CallTransactionResult.java
index 50871f2..8b5f5bf 100644
--- a/src/com/android/server/telecom/voip/VoipCallTransactionResult.java
+++ b/src/com/android/server/telecom/callsequencing/CallTransactionResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,33 +14,38 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing;
 
 import com.android.server.telecom.Call;
 
 import java.util.Objects;
 
-public class VoipCallTransactionResult {
+public class CallTransactionResult {
     public static final int RESULT_SUCCEED = 0;
+    private static final String VOIP_TRANSACTION_TAG = "VoipCallTransactionResult";
+    private static final String PSTN_TRANSACTION_TAG = "PstnTransactionResult";
 
-    // NOTE: if the VoipCallTransactionResult should not use the RESULT_SUCCEED to represent a
+    // NOTE: if the CallTransactionResult should not use the RESULT_SUCCEED to represent a
     // successful transaction, use an error code defined in the
     // {@link android.telecom.CallException} class
 
     private final int mResult;
     private final String mMessage;
     private final Call mCall;
+    private final String mCallType;
 
-    public VoipCallTransactionResult(int result, String message) {
+    public CallTransactionResult(int result, String message) {
         mResult = result;
         mMessage = message;
         mCall = null;
+        mCallType = "";
     }
 
-    public VoipCallTransactionResult(int result, Call call, String message) {
+    public CallTransactionResult(int result, Call call, String message, boolean isVoip) {
         mResult = result;
         mCall = call;
         mMessage = message;
+        mCallType = isVoip ? VOIP_TRANSACTION_TAG : PSTN_TRANSACTION_TAG;
     }
 
     public int getResult() {
@@ -58,8 +63,8 @@
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
-        if (!(o instanceof VoipCallTransactionResult)) return false;
-        VoipCallTransactionResult that = (VoipCallTransactionResult) o;
+        if (!(o instanceof CallTransactionResult)) return false;
+        CallTransactionResult that = (CallTransactionResult) o;
         return mResult == that.mResult && Objects.equals(mMessage, that.mMessage);
     }
 
@@ -71,7 +76,9 @@
     @Override
     public String toString() {
         return new StringBuilder().
-                append("{ VoipCallTransactionResult: [mResult: ").
+                append("{ ").
+                append(mCallType).
+                append(": [mResult: ").
                 append(mResult).
                 append("], [mCall: ").
                 append((mCall != null) ? mCall : "null").
diff --git a/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java b/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java
new file mode 100644
index 0000000..8410c54
--- /dev/null
+++ b/src/com/android/server/telecom/callsequencing/CallsManagerCallSequencingAdapter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 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.callsequencing;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Abstraction layer for CallsManager to perform call sequencing operations through CallsManager
+ * or CallSequencingController, which is controlled by {@link FeatureFlags#enableCallSequencing()}.
+ */
+public class CallsManagerCallSequencingAdapter {
+
+    private final CallsManager mCallsManager;
+    private final CallSequencingController mSequencingController;
+    private final boolean mIsCallSequencingEnabled;
+
+    public CallsManagerCallSequencingAdapter(CallsManager callsManager,
+            CallSequencingController sequencingController,
+            boolean isCallSequencingEnabled) {
+        mCallsManager = callsManager;
+        mSequencingController = sequencingController;
+        mIsCallSequencingEnabled = isCallSequencingEnabled;
+    }
+
+    public void answerCall(Call incomingCall, int videoState) {
+        if (mIsCallSequencingEnabled && !incomingCall.isTransactionalCall()) {
+            mSequencingController.answerCall(incomingCall, videoState);
+        } else {
+            mCallsManager.answerCallOld(incomingCall, videoState);
+        }
+    }
+
+    public void unholdCall(Call call) {
+        if (mIsCallSequencingEnabled) {
+            mSequencingController.unholdCall(call);
+        } else {
+            mCallsManager.unholdCallOld(call);
+        }
+    }
+
+    public void holdCall(Call call) {
+        // Sequencing already taken care of for CSW/TSW in Call class.
+        call.hold();
+    }
+
+    public void unholdCallForRemoval(Call removedCall,
+            boolean isLocallyDisconnecting) {
+        // Todo: confirm verification of disconnect logic
+        // Sequencing already taken care of for CSW/TSW in Call class.
+        mCallsManager.maybeMoveHeldCallToForeground(removedCall, isLocallyDisconnecting);
+    }
+
+    public CompletableFuture<Boolean> makeRoomForOutgoingCall(boolean isEmergency, Call call) {
+        if (mIsCallSequencingEnabled) {
+            return mSequencingController.makeRoomForOutgoingCall(isEmergency, call);
+        } else {
+            return isEmergency
+                    ? CompletableFuture.completedFuture(
+                            makeRoomForOutgoingEmergencyCallFlagOff(call))
+                    : CompletableFuture.completedFuture(makeRoomForOutgoingCallFlagOff(call));
+        }
+    }
+
+    private boolean makeRoomForOutgoingCallFlagOff(Call call) {
+        return mCallsManager.makeRoomForOutgoingCall(call);
+    }
+
+    private boolean makeRoomForOutgoingEmergencyCallFlagOff(Call call) {
+        return mCallsManager.makeRoomForOutgoingEmergencyCall(call);
+    }
+}
diff --git a/src/com/android/server/telecom/voip/TransactionManager.java b/src/com/android/server/telecom/callsequencing/TransactionManager.java
similarity index 76%
rename from src/com/android/server/telecom/voip/TransactionManager.java
rename to src/com/android/server/telecom/callsequencing/TransactionManager.java
index 0086d07..a3b3828 100644
--- a/src/com/android/server/telecom/voip/TransactionManager.java
+++ b/src/com/android/server/telecom/callsequencing/TransactionManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing;
 
 import static android.telecom.CallException.CODE_OPERATION_TIMED_OUT;
 
@@ -32,18 +32,20 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Queue;
+import java.util.concurrent.CompletableFuture;
 
 public class TransactionManager {
-    private static final String TAG = "VoipCallTransactionManager";
+    private static final String TAG = "CallTransactionManager";
     private static final int TRANSACTION_HISTORY_SIZE = 20;
     private static TransactionManager INSTANCE = null;
     private static final Object sLock = new Object();
-    private final Queue<VoipCallTransaction> mTransactions;
-    private final Deque<VoipCallTransaction> mCompletedTransactions;
-    private VoipCallTransaction mCurrentTransaction;
+    private final Queue<CallTransaction> mTransactions;
+    private final Deque<CallTransaction> mCompletedTransactions;
+    private CallTransaction mCurrentTransaction;
+    private boolean mProcessingCallSequencing;
 
     public interface TransactionCompleteListener {
-        void onTransactionCompleted(VoipCallTransactionResult result, String transactionName);
+        void onTransactionCompleted(CallTransactionResult result, String transactionName);
         void onTransactionTimeout(String transactionName);
     }
 
@@ -70,28 +72,32 @@
         return new TransactionManager();
     }
 
-    public void addTransaction(VoipCallTransaction transaction,
-            OutcomeReceiver<VoipCallTransactionResult, CallException> receiver) {
+    public CompletableFuture<Boolean> addTransaction(CallTransaction transaction,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        CompletableFuture<Boolean> transactionCompleteFuture = new CompletableFuture<>();
         synchronized (sLock) {
             mTransactions.add(transaction);
         }
         transaction.setCompleteListener(new TransactionCompleteListener() {
             @Override
-            public void onTransactionCompleted(VoipCallTransactionResult result,
+            public void onTransactionCompleted(CallTransactionResult result,
                     String transactionName) {
                 Log.i(TAG, String.format("transaction %s completed: with result=[%d]",
                         transactionName, result.getResult()));
                 try {
                     if (result.getResult() == TelecomManager.TELECOM_TRANSACTION_SUCCESS) {
                         receiver.onResult(result);
+                        transactionCompleteFuture.complete(true);
                     } else {
                         receiver.onError(
                                 new CallException(result.getMessage(),
                                         result.getResult()));
+                        transactionCompleteFuture.complete(false);
                     }
                 } catch (Exception e) {
                     Log.e(TAG, String.format("onTransactionCompleted: Notifying transaction result"
                             + " %s resulted in an Exception.", result), e);
+                    transactionCompleteFuture.complete(false);
                 }
                 finishTransaction();
             }
@@ -102,15 +108,18 @@
                 try {
                     receiver.onError(new CallException(transactionName + " timeout",
                             CODE_OPERATION_TIMED_OUT));
+                    transactionCompleteFuture.complete(false);
                 } catch (Exception e) {
                     Log.e(TAG, String.format("onTransactionTimeout: Notifying transaction "
                             + " %s resulted in an Exception.", transactionName), e);
+                    transactionCompleteFuture.complete(false);
                 }
                 finishTransaction();
             }
         });
 
         startTransactions();
+        return transactionCompleteFuture;
     }
 
     private void startTransactions() {
@@ -141,17 +150,17 @@
 
     @VisibleForTesting
     public void clear() {
-        List<VoipCallTransaction> pendingTransactions;
+        List<CallTransaction> pendingTransactions;
         synchronized (sLock) {
             pendingTransactions = new ArrayList<>(mTransactions);
         }
-        for (VoipCallTransaction t : pendingTransactions) {
-            t.finish(new VoipCallTransactionResult(CallException.CODE_ERROR_UNKNOWN
+        for (CallTransaction t : pendingTransactions) {
+            t.finish(new CallTransactionResult(CallException.CODE_ERROR_UNKNOWN
                     /* TODO:: define error b/335703584 */, "clear called"));
         }
     }
 
-    private void addTransactionToHistory(VoipCallTransaction t) {
+    private void addTransactionToHistory(CallTransaction t) {
         if (!Flags.enableCallSequencing()) return;
 
         mCompletedTransactions.add(t);
@@ -160,6 +169,14 @@
         }
     }
 
+    public void setProcessingCallSequencing(boolean processingCallSequencing) {
+        mProcessingCallSequencing = processingCallSequencing;
+    }
+
+    public boolean isProcessingCallSequencing() {
+        return mProcessingCallSequencing;
+    }
+
     /**
      * Called when the dumpsys is created for telecom to capture the current state.
      */
@@ -171,7 +188,7 @@
         synchronized (sLock) {
             pw.println("Pending Transactions:");
             pw.increaseIndent();
-            for (VoipCallTransaction t : mTransactions) {
+            for (CallTransaction t : mTransactions) {
                 printPendingTransactionStats(t, pw);
             }
             pw.decreaseIndent();
@@ -185,7 +202,7 @@
 
             pw.println("Completed Transactions:");
             pw.increaseIndent();
-            for (VoipCallTransaction t : mCompletedTransactions) {
+            for (CallTransaction t : mCompletedTransactions) {
                 printCompleteTransactionStats(t, pw);
             }
             pw.decreaseIndent();
@@ -193,12 +210,12 @@
     }
 
     /**
-     * Recursively print the pending {@link VoipCallTransaction} stats for logging purposes.
+     * Recursively print the pending {@link CallTransaction} stats for logging purposes.
      * @param t The transaction that stats should be printed for
      * @param pw The IndentingPrintWriter to print the result to
      */
-    private void printPendingTransactionStats(VoipCallTransaction t, IndentingPrintWriter pw) {
-        VoipCallTransaction.Stats s = t.getStats();
+    private void printPendingTransactionStats(CallTransaction t, IndentingPrintWriter pw) {
+        CallTransaction.Stats s = t.getStats();
         if (s == null) {
             pw.println(String.format(Locale.getDefault(), "%s: <NO STATS>", t.mTransactionName));
             return;
@@ -215,7 +232,7 @@
             return;
         }
         pw.increaseIndent();
-        for (VoipCallTransaction subTransaction : t.mSubTransactions) {
+        for (CallTransaction subTransaction : t.mSubTransactions) {
             printPendingTransactionStats(subTransaction, pw);
         }
         pw.decreaseIndent();
@@ -226,8 +243,8 @@
      * @param t The transaction that stats should be printed for
      * @param pw The IndentingPrintWriter to print the result to
      */
-    private void printCompleteTransactionStats(VoipCallTransaction t, IndentingPrintWriter pw) {
-        VoipCallTransaction.Stats s = t.getStats();
+    private void printCompleteTransactionStats(CallTransaction t, IndentingPrintWriter pw) {
+        CallTransaction.Stats s = t.getStats();
         if (s == null) {
             pw.println(String.format(Locale.getDefault(), "%s: <NO STATS>", t.mTransactionName));
             return;
@@ -242,16 +259,16 @@
             return;
         }
         pw.increaseIndent();
-        for (VoipCallTransaction subTransaction : t.mSubTransactions) {
+        for (CallTransaction subTransaction : t.mSubTransactions) {
             printCompleteTransactionStats(subTransaction, pw);
         }
         pw.decreaseIndent();
     }
 
-    private String parseTransactionResult(VoipCallTransaction.Stats s) {
+    private String parseTransactionResult(CallTransaction.Stats s) {
         if (s.isTimedOut()) return "TIMED OUT";
         if (s.getTransactionResult() == null) return "PENDING";
-        if (s.getTransactionResult().getResult() == VoipCallTransactionResult.RESULT_SUCCEED) {
+        if (s.getTransactionResult().getResult() == CallTransactionResult.RESULT_SUCCEED) {
             return "SUCCESS";
         }
         return s.getTransactionResult().toString();
diff --git a/src/com/android/server/telecom/callsequencing/TransactionalCallSequencingAdapter.java b/src/com/android/server/telecom/callsequencing/TransactionalCallSequencingAdapter.java
new file mode 100644
index 0000000..7c8bbe4
--- /dev/null
+++ b/src/com/android/server/telecom/callsequencing/TransactionalCallSequencingAdapter.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2024 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.callsequencing;
+
+import android.os.OutcomeReceiver;
+import android.telecom.CallException;
+import android.telecom.DisconnectCause;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.callsequencing.voip.EndCallTransaction;
+import com.android.server.telecom.callsequencing.voip.HoldCallTransaction;
+import com.android.server.telecom.callsequencing.voip.MaybeHoldCallForNewCallTransaction;
+import com.android.server.telecom.callsequencing.voip.RequestNewActiveCallTransaction;
+import com.android.server.telecom.callsequencing.voip.SerialTransaction;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Helper adapter class used to centralized code that will be affected by toggling the
+ * {@link Flags#enableCallSequencing()} flag.
+ */
+public class TransactionalCallSequencingAdapter {
+    private final TransactionManager mTransactionManager;
+    private final CallsManager mCallsManager;
+//    private final boolean mIsCallSequencingEnabled;
+
+    public TransactionalCallSequencingAdapter(TransactionManager transactionManager,
+            CallsManager callsManager, boolean isCallSequencingEnabled) {
+        mTransactionManager = transactionManager;
+        mCallsManager = callsManager;
+        // TODO implement call sequencing changes
+//        mIsCallSequencingEnabled = isCallSequencingEnabled;
+    }
+
+    /**
+     * Client -> Server request to set a call active
+     */
+    public void setActive(Call call,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        setActiveFlagOff(call, receiver);
+    }
+
+    /**
+     * Client -> Server request to answer a call
+     */
+    public void setAnswered(Call call, int newVideoState,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        setAnsweredFlagOff(call, newVideoState, receiver);
+    }
+
+    /**
+     * Client -> Server request to set a call to disconnected
+     */
+    public void setDisconnected(Call call, DisconnectCause dc,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        setDisconnectedFlagOff(call, dc, receiver);
+    }
+
+    /**
+     * Client -> Server request to set a call to inactive
+     */
+    public void setInactive(Call call,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        setInactiveFlagOff(call, receiver);
+    }
+
+    /**
+     * Server -> Client command to set the call active, which if it fails, will try to reset the
+     * state to what it was before the call was set to active.
+     */
+    public CompletableFuture<Boolean> onSetActive(Call call,
+            CallTransaction clientCbT,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        return onSetActiveFlagOff(call, clientCbT, receiver);
+    }
+
+    /**
+     * Server -> Client command to answer an incoming call, which if it fails, will trigger the
+     * disconnect of the call and then reset the state of the other call back to what it was before.
+     */
+    public void onSetAnswered(Call call, int videoState, CallTransaction clientCbT,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        onSetAnsweredFlagOff(call, videoState, clientCbT, receiver);
+    }
+
+    /**
+     * Server -> Client command to set the call as inactive
+     */
+    public CompletableFuture<Boolean> onSetInactive(Call call,
+            CallTransaction clientCbT,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        return onSetInactiveFlagOff(call, clientCbT, receiver);
+    }
+
+    /**
+     * Server -> Client command to disconnect the call
+     */
+    public CompletableFuture<Boolean> onSetDisconnected(Call call,
+            DisconnectCause dc, CallTransaction clientCbT, OutcomeReceiver<CallTransactionResult,
+            CallException> receiver) {
+        return onSetDisconnectedFlagOff(call, dc, clientCbT, receiver);
+    }
+
+    /**
+     * Clean up the calls that have been passed in from CallsManager
+     */
+    public void cleanup(Collection<Call> calls) {
+        cleanupFlagOff(calls);
+    }
+
+    private void setActiveFlagOff(Call call,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        CompletableFuture<Boolean> transactionResult = mTransactionManager
+                .addTransaction(createSetActiveTransactions(call,
+                true /* callControlRequest */), receiver);
+    }
+
+    private void setAnsweredFlagOff(Call call, int newVideoState,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        CompletableFuture<Boolean> transactionResult = mTransactionManager
+                .addTransaction(createSetActiveTransactions(call,
+                                true /* callControlRequest */),
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(CallTransactionResult callTransactionResult) {
+                        call.setVideoState(newVideoState);
+                        receiver.onResult(callTransactionResult);
+                    }
+
+                    @Override
+                    public void onError(CallException error) {
+                        receiver.onError(error);
+                    }
+                });
+    }
+
+    private void setDisconnectedFlagOff(Call call, DisconnectCause dc,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        CompletableFuture<Boolean> transactionResult = mTransactionManager
+                .addTransaction(new EndCallTransaction(mCallsManager,
+                        dc, call), receiver);
+    }
+
+    private void setInactiveFlagOff(Call call,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        CompletableFuture<Boolean> transactionResult = mTransactionManager
+                .addTransaction(new HoldCallTransaction(mCallsManager,call), receiver);
+    }
+
+    private CompletableFuture<Boolean> onSetActiveFlagOff(Call call,
+            CallTransaction clientCbT,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        // save CallsManager state before sending client state changes
+        Call foregroundCallBeforeSwap = mCallsManager.getForegroundCall();
+        boolean wasActive = foregroundCallBeforeSwap != null && foregroundCallBeforeSwap.isActive();
+        SerialTransaction serialTransactions = createSetActiveTransactions(call,
+                false /* callControlRequest */);
+        serialTransactions.appendTransaction(clientCbT);
+        // do CallsManager workload before asking client and
+        //   reset CallsManager state if client does NOT ack
+        return mTransactionManager.addTransaction(
+                serialTransactions,
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(CallTransactionResult result) {
+                        receiver.onResult(result);
+                    }
+
+                    @Override
+                    public void onError(CallException exception) {
+                        mCallsManager.markCallAsOnHold(call);
+                        maybeResetForegroundCall(foregroundCallBeforeSwap, wasActive);
+                        receiver.onError(exception);
+                    }
+                });
+    }
+
+    private void onSetAnsweredFlagOff(Call call, int videoState, CallTransaction clientCbT,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        // save CallsManager state before sending client state changes
+        Call foregroundCallBeforeSwap = mCallsManager.getForegroundCall();
+        boolean wasActive = foregroundCallBeforeSwap != null && foregroundCallBeforeSwap.isActive();
+        SerialTransaction serialTransactions = createSetActiveTransactions(call,
+                false /* callControlRequest */);
+        serialTransactions.appendTransaction(clientCbT);
+        // do CallsManager workload before asking client and
+        //   reset CallsManager state if client does NOT ack
+        CompletableFuture<Boolean> transactionResult = mTransactionManager
+                .addTransaction(serialTransactions,
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(CallTransactionResult result) {
+                        call.setVideoState(videoState);
+                        receiver.onResult(result);
+                    }
+
+                    @Override
+                    public void onError(CallException exception) {
+                        // This also sends the signal to untrack from TSW and the client_TSW
+                        removeCallFromCallsManager(call,
+                                new DisconnectCause(DisconnectCause.REJECTED,
+                                        "client rejected to answer the call;"
+                                                + " force disconnecting"));
+                        maybeResetForegroundCall(foregroundCallBeforeSwap, wasActive);
+                        receiver.onError(exception);
+                    }
+                });
+    }
+
+    private CompletableFuture<Boolean> onSetInactiveFlagOff(Call call,
+            CallTransaction clientCbT,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        return mTransactionManager.addTransaction(clientCbT,
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(CallTransactionResult callTransactionResult) {
+                        mCallsManager.markCallAsOnHold(call);
+                        receiver.onResult(callTransactionResult);
+                    }
+
+                    @Override
+                    public void onError(CallException error) {
+                        receiver.onError(error);
+                    }
+                });
+    }
+
+    /**
+     * Server -> Client command to disconnect the call
+     */
+    private CompletableFuture<Boolean> onSetDisconnectedFlagOff(Call call,
+            DisconnectCause dc, CallTransaction clientCbT,
+            OutcomeReceiver<CallTransactionResult, CallException> receiver) {
+        return mTransactionManager.addTransaction(clientCbT,
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(CallTransactionResult result) {
+                        removeCallFromCallsManager(call, dc);
+                        receiver.onResult(result);
+                    }
+
+                    @Override
+                    public void onError(CallException exception) {
+                        removeCallFromCallsManager(call, dc);
+                        receiver.onError(exception);
+                    }
+                }
+        );
+    }
+
+    private SerialTransaction createSetActiveTransactions(Call call, boolean isCallControlRequest) {
+        // create list for multiple transactions
+        List<CallTransaction> transactions = new ArrayList<>();
+
+        // potentially hold the current active call in order to set a new call (active/answered)
+        transactions.add(new MaybeHoldCallForNewCallTransaction(mCallsManager, call,
+                isCallControlRequest));
+        // And request a new focus call update
+        transactions.add(new RequestNewActiveCallTransaction(mCallsManager, call));
+
+        return new SerialTransaction(transactions, mCallsManager.getLock());
+    }
+
+    private void removeCallFromCallsManager(Call call, DisconnectCause cause) {
+        if (cause.getCode() != DisconnectCause.REJECTED) {
+            mCallsManager.markCallAsDisconnected(call, cause);
+        }
+        mCallsManager.removeCall(call);
+    }
+
+    private void maybeResetForegroundCall(Call foregroundCallBeforeSwap, boolean wasActive) {
+        if (foregroundCallBeforeSwap == null) {
+            return;
+        }
+        if (wasActive && !foregroundCallBeforeSwap.isActive()) {
+            mCallsManager.markCallAsActive(foregroundCallBeforeSwap);
+        }
+    }
+    private void cleanupFlagOff(Collection<Call> calls) {
+        for (Call call : calls) {
+            mCallsManager.markCallAsDisconnected(call,
+                    new DisconnectCause(DisconnectCause.ERROR, "process died"));
+            mCallsManager.removeCall(call); // This will clear mTrackedCalls && ClientTWS
+        }
+    }
+}
diff --git a/src/com/android/server/telecom/voip/VerifyCallStateChangeTransaction.java b/src/com/android/server/telecom/callsequencing/VerifyCallStateChangeTransaction.java
similarity index 69%
rename from src/com/android/server/telecom/voip/VerifyCallStateChangeTransaction.java
rename to src/com/android/server/telecom/callsequencing/VerifyCallStateChangeTransaction.java
index 5de4b1d..82b32fb 100644
--- a/src/com/android/server/telecom/voip/VerifyCallStateChangeTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/VerifyCallStateChangeTransaction.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.telecom.Call;
@@ -22,8 +22,11 @@
 
 import android.telecom.Log;
 
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /**
  * VerifyCallStateChangeTransaction is a transaction that verifies a CallState change and has
@@ -31,21 +34,22 @@
  * <p>
  * Note: This transaction has a timeout of 2 seconds.
  */
-public class VerifyCallStateChangeTransaction extends VoipCallTransaction {
+public class VerifyCallStateChangeTransaction extends CallTransaction {
     private static final String TAG = VerifyCallStateChangeTransaction.class.getSimpleName();
     private static final long CALL_STATE_TIMEOUT_MILLISECONDS = 2000L;
     private final Call mCall;
-    private final int mTargetCallState;
-    private final CompletableFuture<VoipCallTransactionResult> mTransactionResult =
+    private final Set<Integer> mTargetCallStates;
+    private final CompletableFuture<CallTransactionResult> mTransactionResult =
             new CompletableFuture<>();
 
     private final Call.CallStateListener mCallStateListenerImpl = new Call.CallStateListener() {
         @Override
         public void onCallStateChanged(int newCallState) {
-            Log.d(TAG, "newState=[%d], expectedState=[%d]", newCallState, mTargetCallState);
-            if (newCallState == mTargetCallState) {
-                mTransactionResult.complete(new VoipCallTransactionResult(
-                        VoipCallTransactionResult.RESULT_SUCCEED, TAG));
+            Log.d(TAG, "newState=[%d], possible expected state(s)=[%s]", newCallState,
+                    mTargetCallStates);
+            if (mTargetCallStates.contains(newCallState)) {
+                mTransactionResult.complete(new CallTransactionResult(
+                        CallTransactionResult.RESULT_SUCCEED, TAG));
             }
             // NOTE:: keep listening to the call state until the timeout is reached. It's possible
             // another call state is reached in between...
@@ -53,19 +57,19 @@
     };
 
     public VerifyCallStateChangeTransaction(TelecomSystem.SyncRoot lock,  Call call,
-            int targetCallState) {
+            int... targetCallStates) {
         super(lock, CALL_STATE_TIMEOUT_MILLISECONDS);
         mCall = call;
-        mTargetCallState = targetCallState;
+        mTargetCallStates = IntStream.of(targetCallStates).boxed().collect(Collectors.toSet());;
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction:");
         // It's possible the Call is already in the expected call state
         if (isNewCallStateTargetCallState()) {
-            mTransactionResult.complete(new VoipCallTransactionResult(
-                    VoipCallTransactionResult.RESULT_SUCCEED, TAG));
+            mTransactionResult.complete(new CallTransactionResult(
+                    CallTransactionResult.RESULT_SUCCEED, TAG));
             return mTransactionResult;
         }
         mCall.addCallStateListener(mCallStateListenerImpl);
@@ -78,11 +82,11 @@
     }
 
     private boolean isNewCallStateTargetCallState() {
-        return mCall.getState() == mTargetCallState;
+        return mTargetCallStates.contains(mCall.getState());
     }
 
     @VisibleForTesting
-    public CompletableFuture<VoipCallTransactionResult> getTransactionResult() {
+    public CompletableFuture<CallTransactionResult> getTransactionResult() {
         return mTransactionResult;
     }
 
diff --git a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java b/src/com/android/server/telecom/callsequencing/voip/CallEventCallbackAckTransaction.java
similarity index 90%
rename from src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/CallEventCallbackAckTransaction.java
index 9e140a7..802ea7e 100644
--- a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/CallEventCallbackAckTransaction.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import static android.telecom.TelecomManager.TELECOM_TRANSACTION_SUCCESS;
 import static android.telecom.CallException.CODE_OPERATION_TIMED_OUT;
@@ -29,6 +29,8 @@
 import com.android.internal.telecom.ICallEventCallback;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.TransactionalServiceWrapper;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
@@ -39,7 +41,7 @@
  * SRP: using the ICallEventCallback binder, reach out to the client for the pending call event and
  * get an acknowledgement that the call event can be completed.
  */
-public class CallEventCallbackAckTransaction extends VoipCallTransaction {
+public class CallEventCallbackAckTransaction extends CallTransaction {
     private static final String TAG = CallEventCallbackAckTransaction.class.getSimpleName();
     private final ICallEventCallback mICallEventCallback;
     private final String mAction;
@@ -48,7 +50,7 @@
     private int mVideoState = CallAttributes.AUDIO_CALL;
     private DisconnectCause mDisconnectCause = null;
 
-    private final VoipCallTransactionResult TRANSACTION_FAILED = new VoipCallTransactionResult(
+    private final CallTransactionResult TRANSACTION_FAILED = new CallTransactionResult(
             CODE_OPERATION_TIMED_OUT, "failed to complete the operation before timeout");
 
     private static class AckResultReceiver extends ResultReceiver {
@@ -96,7 +98,7 @@
 
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction");
         CountDownLatch latch = new CountDownLatch(1);
         ResultReceiver receiver = new AckResultReceiver(latch);
@@ -134,7 +136,7 @@
             } else {
                 // success
                 return CompletableFuture.completedFuture(
-                        new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED,
+                        new CallTransactionResult(CallTransactionResult.RESULT_SUCCEED,
                                 "success"));
             }
         } catch (InterruptedException ie) {
diff --git a/src/com/android/server/telecom/voip/EndCallTransaction.java b/src/com/android/server/telecom/callsequencing/voip/EndCallTransaction.java
similarity index 82%
rename from src/com/android/server/telecom/voip/EndCallTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/EndCallTransaction.java
index 0cb7458..b4c92fe 100644
--- a/src/com/android/server/telecom/voip/EndCallTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/EndCallTransaction.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import android.telecom.DisconnectCause;
 import android.util.Log;
@@ -22,6 +22,8 @@
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallState;
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
@@ -29,7 +31,7 @@
 /**
  * This transaction should only be created for a CallControl action.
  */
-public class EndCallTransaction extends VoipCallTransaction {
+public class EndCallTransaction extends CallTransaction {
     private static final String TAG = EndCallTransaction.class.getSimpleName();
     private final CallsManager mCallsManager;
     private final Call mCall;
@@ -43,7 +45,7 @@
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         int code = mCause.getCode();
         Log.d(TAG, String.format("processTransaction: mCode=[%d], mCall=[%s]", code, mCall));
 
@@ -56,7 +58,7 @@
         mCallsManager.markCallAsRemoved(mCall);
 
         return CompletableFuture.completedFuture(
-                new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED,
+                new CallTransactionResult(CallTransactionResult.RESULT_SUCCEED,
                         "EndCallTransaction: RESULT_SUCCEED"));
     }
 }
diff --git a/src/com/android/server/telecom/voip/EndpointChangeTransaction.java b/src/com/android/server/telecom/callsequencing/voip/EndpointChangeTransaction.java
similarity index 75%
rename from src/com/android/server/telecom/voip/EndpointChangeTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/EndpointChangeTransaction.java
index 6841fcf..46678da 100644
--- a/src/com/android/server/telecom/voip/EndpointChangeTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/EndpointChangeTransaction.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -23,11 +23,13 @@
 import android.util.Log;
 
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 
-public class EndpointChangeTransaction extends VoipCallTransaction {
+public class EndpointChangeTransaction extends CallTransaction {
     private static final String TAG = EndpointChangeTransaction.class.getSimpleName();
     private final CallEndpoint mCallEndpoint;
     private final CallsManager mCallsManager;
@@ -39,19 +41,19 @@
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.i(TAG, "processTransaction");
-        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+        CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
         mCallsManager.requestCallEndpointChange(mCallEndpoint, new ResultReceiver(null) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 Log.i(TAG, "processTransaction: code=" + resultCode);
                 if (resultCode == CallEndpoint.ENDPOINT_OPERATION_SUCCESS) {
-                    future.complete(new VoipCallTransactionResult(
-                            VoipCallTransactionResult.RESULT_SUCCEED, null));
+                    future.complete(new CallTransactionResult(
+                            CallTransactionResult.RESULT_SUCCEED, null));
                 } else {
                     // TODO:: define errors in CallException class. b/335703584
-                    future.complete(new VoipCallTransactionResult(
+                    future.complete(new CallTransactionResult(
                             CallException.CODE_ERROR_UNKNOWN, null));
                 }
             }
diff --git a/src/com/android/server/telecom/voip/HoldCallTransaction.java b/src/com/android/server/telecom/callsequencing/voip/HoldCallTransaction.java
similarity index 72%
rename from src/com/android/server/telecom/voip/HoldCallTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/HoldCallTransaction.java
index 6c4e8b7..2fa7ff7 100644
--- a/src/com/android/server/telecom/voip/HoldCallTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/HoldCallTransaction.java
@@ -14,18 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import android.telecom.CallException;
 import android.util.Log;
 
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 
-public class HoldCallTransaction extends VoipCallTransaction {
+public class HoldCallTransaction extends CallTransaction {
 
     private static final String TAG = HoldCallTransaction.class.getSimpleName();
     private final CallsManager mCallsManager;
@@ -38,17 +40,17 @@
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction");
-        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+        CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
 
         if (mCallsManager.canHold(mCall)) {
             mCallsManager.markCallAsOnHold(mCall);
-            future.complete(new VoipCallTransactionResult(
-                    VoipCallTransactionResult.RESULT_SUCCEED, null));
+            future.complete(new CallTransactionResult(
+                    CallTransactionResult.RESULT_SUCCEED, null));
         } else {
             Log.d(TAG, "processTransaction: onError");
-            future.complete(new VoipCallTransactionResult(
+            future.complete(new CallTransactionResult(
                     CallException.CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL, "cannot hold call"));
         }
         return future;
diff --git a/src/com/android/server/telecom/voip/IncomingCallTransaction.java b/src/com/android/server/telecom/callsequencing/voip/IncomingCallTransaction.java
similarity index 85%
rename from src/com/android/server/telecom/voip/IncomingCallTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/IncomingCallTransaction.java
index ed0c7d6..31ce303 100644
--- a/src/com/android/server/telecom/voip/IncomingCallTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/IncomingCallTransaction.java
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import static android.telecom.CallAttributes.CALL_CAPABILITIES_KEY;
 import static android.telecom.CallAttributes.DISPLAY_NAME_KEY;
 
-import static com.android.server.telecom.voip.VideoStateTranslation.TransactionalVideoStateToVideoProfileState;
+import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
+        .TransactionalVideoStateToVideoProfileState;
 
 import android.os.Bundle;
 import android.telecom.CallAttributes;
@@ -30,12 +31,14 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 import com.android.server.telecom.flags.FeatureFlags;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 
-public class IncomingCallTransaction extends VoipCallTransaction {
+public class IncomingCallTransaction extends CallTransaction {
 
     private static final String TAG = IncomingCallTransaction.class.getSimpleName();
     private final String mCallId;
@@ -64,7 +67,7 @@
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction");
 
         if (mCallsManager.isIncomingCallPermitted(mCallAttributes.getPhoneAccountHandle())) {
@@ -75,13 +78,13 @@
                     generateExtras(mCallAttributes), false);
 
             return CompletableFuture.completedFuture(
-                    new VoipCallTransactionResult(
-                            VoipCallTransactionResult.RESULT_SUCCEED, call, "success"));
+                    new CallTransactionResult(
+                            CallTransactionResult.RESULT_SUCCEED, call, "success", true));
         } else {
             Log.d(TAG, "processTransaction: incoming call is not permitted at this time");
 
             return CompletableFuture.completedFuture(
-                    new VoipCallTransactionResult(
+                    new CallTransactionResult(
                             CallException.CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
                             "incoming call not permitted at the current time"));
         }
diff --git a/src/com/android/server/telecom/voip/MaybeHoldCallForNewCallTransaction.java b/src/com/android/server/telecom/callsequencing/voip/MaybeHoldCallForNewCallTransaction.java
similarity index 74%
rename from src/com/android/server/telecom/voip/MaybeHoldCallForNewCallTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/MaybeHoldCallForNewCallTransaction.java
index 3bed088..32062b5 100644
--- a/src/com/android/server/telecom/voip/MaybeHoldCallForNewCallTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/MaybeHoldCallForNewCallTransaction.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import android.os.OutcomeReceiver;
 import android.telecom.CallException;
@@ -22,15 +22,17 @@
 
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 
 /**
- * This VoipCallTransaction is responsible for holding any active call in favor of a new call
+ * This VOIP CallTransaction is responsible for holding any active call in favor of a new call
  * request. If the active call cannot be held or disconnected, the transaction will fail.
  */
-public class MaybeHoldCallForNewCallTransaction extends VoipCallTransaction {
+public class MaybeHoldCallForNewCallTransaction extends CallTransaction {
 
     private static final String TAG = MaybeHoldCallForNewCallTransaction.class.getSimpleName();
     private final CallsManager mCallsManager;
@@ -46,23 +48,23 @@
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction");
-        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+        CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
 
         mCallsManager.transactionHoldPotentialActiveCallForNewCall(mCall, mIsCallControlRequest,
                 new OutcomeReceiver<>() {
             @Override
             public void onResult(Boolean result) {
                 Log.d(TAG, "processTransaction: onResult");
-                future.complete(new VoipCallTransactionResult(
-                        VoipCallTransactionResult.RESULT_SUCCEED, null));
+                future.complete(new CallTransactionResult(
+                        CallTransactionResult.RESULT_SUCCEED, null));
             }
 
             @Override
             public void onError(CallException exception) {
                 Log.d(TAG, "processTransaction: onError");
-                future.complete(new VoipCallTransactionResult(
+                future.complete(new CallTransactionResult(
                        exception.getCode(), exception.getMessage()));
             }
         });
diff --git a/src/com/android/server/telecom/voip/OutgoingCallTransaction.java b/src/com/android/server/telecom/callsequencing/voip/OutgoingCallTransaction.java
similarity index 87%
rename from src/com/android/server/telecom/voip/OutgoingCallTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/OutgoingCallTransaction.java
index 68ffecf..572de55 100644
--- a/src/com/android/server/telecom/voip/OutgoingCallTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/OutgoingCallTransaction.java
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import static android.Manifest.permission.CALL_PRIVILEGED;
 import static android.telecom.CallAttributes.CALL_CAPABILITIES_KEY;
 import static android.telecom.CallAttributes.DISPLAY_NAME_KEY;
 import static android.telecom.CallException.CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME;
 
-import static com.android.server.telecom.voip.VideoStateTranslation.TransactionalVideoStateToVideoProfileState;
+import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
+        .TransactionalVideoStateToVideoProfileState;
 
 import android.content.Context;
 import android.content.Intent;
@@ -35,12 +36,14 @@
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.LoggedHandlerExecutor;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 import com.android.server.telecom.flags.FeatureFlags;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 
-public class OutgoingCallTransaction extends VoipCallTransaction {
+public class OutgoingCallTransaction extends CallTransaction {
 
     private static final String TAG = OutgoingCallTransaction.class.getSimpleName();
     private final String mCallId;
@@ -73,7 +76,7 @@
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction");
 
         final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission(
@@ -95,11 +98,11 @@
 
             if (callFuture == null) {
                 return CompletableFuture.completedFuture(
-                        new VoipCallTransactionResult(
+                        new CallTransactionResult(
                                 CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
                                 "incoming call not permitted at the current time"));
             }
-            CompletionStage<VoipCallTransactionResult> result = callFuture.thenComposeAsync(
+            CompletionStage<CallTransactionResult> result = callFuture.thenComposeAsync(
                     (call) -> {
 
                         Log.d(TAG, "processTransaction: completing future");
@@ -107,7 +110,7 @@
                         if (call == null) {
                             Log.d(TAG, "processTransaction: call is null");
                             return CompletableFuture.completedFuture(
-                                    new VoipCallTransactionResult(
+                                    new CallTransactionResult(
                                             CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
                                             "call could not be created at this time"));
                         } else {
@@ -121,16 +124,16 @@
                         }
 
                         return CompletableFuture.completedFuture(
-                                new VoipCallTransactionResult(
-                                        VoipCallTransactionResult.RESULT_SUCCEED,
-                                        call, null));
+                                new CallTransactionResult(
+                                        CallTransactionResult.RESULT_SUCCEED,
+                                        call, null, true));
                     }
                     , new LoggedHandlerExecutor(mHandler, "OCT.pT", null));
 
             return result;
         } else {
             return CompletableFuture.completedFuture(
-                    new VoipCallTransactionResult(
+                    new CallTransactionResult(
                             CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
                             "incoming call not permitted at the current time"));
 
diff --git a/src/com/android/server/telecom/voip/ParallelTransaction.java b/src/com/android/server/telecom/callsequencing/voip/ParallelTransaction.java
similarity index 79%
rename from src/com/android/server/telecom/voip/ParallelTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/ParallelTransaction.java
index e235ead..77e93f9 100644
--- a/src/com/android/server/telecom/voip/ParallelTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/ParallelTransaction.java
@@ -14,22 +14,25 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import android.telecom.CallException;
 
 import com.android.server.telecom.LoggedHandlerExecutor;
 import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
+import com.android.server.telecom.callsequencing.TransactionManager;
 
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * A VoipCallTransaction implementation that its sub transactions will be executed in parallel
+ * A CallTransaction implementation that its sub transactions will be executed in parallel
  */
-public class ParallelTransaction extends VoipCallTransaction {
-    public ParallelTransaction(List<VoipCallTransaction> subTransactions,
+public class ParallelTransaction extends CallTransaction {
+    public ParallelTransaction(List<CallTransaction> subTransactions,
             TelecomSystem.SyncRoot lock) {
         super(subTransactions, lock);
     }
@@ -45,9 +48,9 @@
                     private final AtomicInteger mCount = new AtomicInteger(mSubTransactions.size());
 
                     @Override
-                    public void onTransactionCompleted(VoipCallTransactionResult result,
+                    public void onTransactionCompleted(CallTransactionResult result,
                             String transactionName) {
-                        if (result.getResult() != VoipCallTransactionResult.RESULT_SUCCEED) {
+                        if (result.getResult() != CallTransactionResult.RESULT_SUCCEED) {
                             CompletableFuture.completedFuture(null).thenApplyAsync(
                                     (x) -> {
                                         finish(result);
@@ -68,8 +71,8 @@
                     public void onTransactionTimeout(String transactionName) {
                         CompletableFuture.completedFuture(null).thenApplyAsync(
                                 (x) -> {
-                                    VoipCallTransactionResult mainResult =
-                                            new VoipCallTransactionResult(
+                                    CallTransactionResult mainResult =
+                                            new CallTransactionResult(
                                                     CallException.CODE_OPERATION_TIMED_OUT,
                                             String.format("sub transaction %s timed out",
                                                     transactionName));
@@ -82,7 +85,7 @@
                                                 + ".oTT", mLock));
                     }
                 };
-        for (VoipCallTransaction transaction : mSubTransactions) {
+        for (CallTransaction transaction : mSubTransactions) {
             transaction.setCompleteListener(subTransactionListener);
             transaction.start();
         }
diff --git a/src/com/android/server/telecom/voip/RequestNewActiveCallTransaction.java b/src/com/android/server/telecom/callsequencing/voip/RequestNewActiveCallTransaction.java
similarity index 83%
rename from src/com/android/server/telecom/voip/RequestNewActiveCallTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/RequestNewActiveCallTransaction.java
index e3aed8e..8e6e354 100644
--- a/src/com/android/server/telecom/voip/RequestNewActiveCallTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/RequestNewActiveCallTransaction.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import android.os.OutcomeReceiver;
 import android.telecom.CallException;
@@ -24,6 +24,8 @@
 import com.android.server.telecom.CallState;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.ConnectionServiceFocusManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 import com.android.server.telecom.flags.Flags;
 
 import java.util.concurrent.CompletableFuture;
@@ -42,7 +44,7 @@
  * - MaybeHoldCallForNewCallTransaction was performed before this so any potential active calls
  * should be held now.
  */
-public class RequestNewActiveCallTransaction extends VoipCallTransaction {
+public class RequestNewActiveCallTransaction extends CallTransaction {
 
     private static final String TAG = RequestNewActiveCallTransaction.class.getSimpleName();
     private final CallsManager mCallsManager;
@@ -55,14 +57,14 @@
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction");
-        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+        CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
         int currentCallState = mCall.getState();
 
         // certain calls cannot go active/answered (ex. disconnect calls, etc.)
         if (!canBecomeNewCallFocus(currentCallState)) {
-            future.complete(new VoipCallTransactionResult(
+            future.complete(new CallTransactionResult(
                     CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE,
                     "CallState cannot be set to active or answered due to current call"
                             + " state being in invalid state"));
@@ -71,7 +73,7 @@
 
         if (!Flags.transactionalHoldDisconnectsUnholdable() &&
                 mCallsManager.getActiveCall() != null) {
-            future.complete(new VoipCallTransactionResult(
+            future.complete(new CallTransactionResult(
                     CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE,
                     "Already an active call. Request hold on current active call."));
             return future;
@@ -81,14 +83,14 @@
                     @Override
                     public void onResult(Boolean result) {
                         Log.d(TAG, "processTransaction: onResult");
-                        future.complete(new VoipCallTransactionResult(
-                                VoipCallTransactionResult.RESULT_SUCCEED, null));
+                        future.complete(new CallTransactionResult(
+                                CallTransactionResult.RESULT_SUCCEED, null));
                     }
 
                     @Override
                     public void onError(CallException exception) {
                         Log.d(TAG, "processTransaction: onError");
-                        future.complete(new VoipCallTransactionResult(
+                        future.complete(new CallTransactionResult(
                                 exception.getCode(), exception.getMessage()));
                     }
                 });
diff --git a/src/com/android/server/telecom/voip/RequestVideoStateTransaction.java b/src/com/android/server/telecom/callsequencing/voip/RequestVideoStateTransaction.java
similarity index 73%
rename from src/com/android/server/telecom/voip/RequestVideoStateTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/RequestVideoStateTransaction.java
index c1bc343..6fb1836 100644
--- a/src/com/android/server/telecom/voip/RequestVideoStateTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/RequestVideoStateTransaction.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
-import static com.android.server.telecom.voip.VideoStateTranslation.TransactionalVideoStateToVideoProfileState;
+import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
+        .TransactionalVideoStateToVideoProfileState;
 
 import android.telecom.CallException;
 import android.telecom.VideoProfile;
@@ -24,11 +25,13 @@
 
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.Call;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 
-public class RequestVideoStateTransaction extends VoipCallTransaction {
+public class RequestVideoStateTransaction extends CallTransaction {
 
     private static final String TAG = RequestVideoStateTransaction.class.getSimpleName();
     private final Call mCall;
@@ -42,19 +45,19 @@
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction");
-        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+        CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
 
         if (isRequestingVideoTransmission(mVideoProfileState) &&
                 !mCall.isVideoCallingSupportedByPhoneAccount()) {
-            future.complete(new VoipCallTransactionResult(
+            future.complete(new CallTransactionResult(
                     CallException.CODE_ERROR_UNKNOWN /*TODO:: define error code. b/335703584 */,
                     "Video calling is not supported by the target account"));
         } else {
             mCall.setVideoState(mVideoProfileState);
-            future.complete(new VoipCallTransactionResult(
-                    VoipCallTransactionResult.RESULT_SUCCEED,
+            future.complete(new CallTransactionResult(
+                    CallTransactionResult.RESULT_SUCCEED,
                     "The Video State was changed successfully"));
         }
         return future;
diff --git a/src/com/android/server/telecom/voip/SerialTransaction.java b/src/com/android/server/telecom/callsequencing/voip/SerialTransaction.java
similarity index 79%
rename from src/com/android/server/telecom/voip/SerialTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/SerialTransaction.java
index 748f285..d5d75d0 100644
--- a/src/com/android/server/telecom/voip/SerialTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/SerialTransaction.java
@@ -14,27 +14,30 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import android.telecom.CallException;
 
 import com.android.server.telecom.LoggedHandlerExecutor;
 import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
+import com.android.server.telecom.callsequencing.TransactionManager;
 
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * A VoipCallTransaction implementation that its sub transactions will be executed in serial
+ * A CallTransaction implementation that its sub transactions will be executed in serial
  */
-public class SerialTransaction extends VoipCallTransaction {
-    public SerialTransaction(List<VoipCallTransaction> subTransactions,
+public class SerialTransaction extends CallTransaction {
+    public SerialTransaction(List<CallTransaction> subTransactions,
             TelecomSystem.SyncRoot lock) {
         super(subTransactions, lock);
     }
 
-    public void appendTransaction(VoipCallTransaction transaction){
+    public void appendTransaction(CallTransaction transaction){
         mSubTransactions.add(transaction);
     }
 
@@ -49,9 +52,9 @@
                     private final AtomicInteger mTransactionIndex = new AtomicInteger(0);
 
                     @Override
-                    public void onTransactionCompleted(VoipCallTransactionResult result,
+                    public void onTransactionCompleted(CallTransactionResult result,
                             String transactionName) {
-                        if (result.getResult() != VoipCallTransactionResult.RESULT_SUCCEED) {
+                        if (result.getResult() != CallTransactionResult.RESULT_SUCCEED) {
                             handleTransactionFailure();
                             CompletableFuture.completedFuture(null).thenApplyAsync(
                                     (x) -> {
@@ -65,7 +68,7 @@
                         } else {
                             int currTransactionIndex = mTransactionIndex.incrementAndGet();
                             if (currTransactionIndex < mSubTransactions.size()) {
-                                VoipCallTransaction transaction = mSubTransactions.get(
+                                CallTransaction transaction = mSubTransactions.get(
                                         currTransactionIndex);
                                 transaction.setCompleteListener(this);
                                 transaction.start();
@@ -80,8 +83,8 @@
                         handleTransactionFailure();
                         CompletableFuture.completedFuture(null).thenApplyAsync(
                                 (x) -> {
-                                    VoipCallTransactionResult mainResult =
-                                            new VoipCallTransactionResult(
+                                    CallTransactionResult mainResult =
+                                            new CallTransactionResult(
                                                     CallException.CODE_OPERATION_TIMED_OUT,
                                             String.format("sub transaction %s timed out",
                                                     transactionName));
@@ -94,7 +97,7 @@
                                                 + ".oTT", mLock));
                     }
                 };
-        VoipCallTransaction transaction = mSubTransactions.get(0);
+        CallTransaction transaction = mSubTransactions.get(0);
         transaction.setCompleteListener(subTransactionListener);
         transaction.start();
 
diff --git a/src/com/android/server/telecom/voip/SetMuteStateTransaction.java b/src/com/android/server/telecom/callsequencing/voip/SetMuteStateTransaction.java
similarity index 73%
rename from src/com/android/server/telecom/voip/SetMuteStateTransaction.java
rename to src/com/android/server/telecom/callsequencing/voip/SetMuteStateTransaction.java
index d9f7329..14f8945 100644
--- a/src/com/android/server/telecom/voip/SetMuteStateTransaction.java
+++ b/src/com/android/server/telecom/callsequencing/voip/SetMuteStateTransaction.java
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import android.util.Log;
 
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
@@ -27,7 +29,7 @@
  * This transaction should be used to change the global mute state for transactional
  * calls. There is currently no way for this transaction to fail.
  */
-public class SetMuteStateTransaction extends VoipCallTransaction {
+public class SetMuteStateTransaction extends CallTransaction {
 
     private static final String TAG = SetMuteStateTransaction.class.getSimpleName();
     private final CallsManager mCallsManager;
@@ -40,14 +42,14 @@
     }
 
     @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+    public CompletionStage<CallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction");
-        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+        CompletableFuture<CallTransactionResult> future = new CompletableFuture<>();
 
         mCallsManager.mute(mIsMuted);
 
-        future.complete(new VoipCallTransactionResult(
-                VoipCallTransactionResult.RESULT_SUCCEED,
+        future.complete(new CallTransactionResult(
+                CallTransactionResult.RESULT_SUCCEED,
                 "The Mute State was changed successfully"));
 
         return future;
diff --git a/src/com/android/server/telecom/voip/VideoStateTranslation.java b/src/com/android/server/telecom/callsequencing/voip/VideoStateTranslation.java
similarity index 95%
rename from src/com/android/server/telecom/voip/VideoStateTranslation.java
rename to src/com/android/server/telecom/callsequencing/voip/VideoStateTranslation.java
index 3812d15..4610f96 100644
--- a/src/com/android/server/telecom/voip/VideoStateTranslation.java
+++ b/src/com/android/server/telecom/callsequencing/voip/VideoStateTranslation.java
@@ -14,17 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import android.telecom.CallAttributes;
 import android.telecom.Log;
 import android.telecom.VideoProfile;
 
-import com.android.server.telecom.AnomalyReporterAdapter;
-import com.android.server.telecom.AnomalyReporterAdapterImpl;
-
-import java.util.UUID;
-
 /**
  * This remapping class is needed because {@link VideoProfile} has more fine grain levels of video
  * states as apposed to Transactional video states (defined in  {@link CallAttributes.CallType}.
diff --git a/src/com/android/server/telecom/voip/VoipCallMonitor.java b/src/com/android/server/telecom/callsequencing/voip/VoipCallMonitor.java
similarity index 99%
rename from src/com/android/server/telecom/voip/VoipCallMonitor.java
rename to src/com/android/server/telecom/callsequencing/voip/VoipCallMonitor.java
index 8f6ad51..1d1a1a6 100644
--- a/src/com/android/server/telecom/voip/VoipCallMonitor.java
+++ b/src/com/android/server/telecom/callsequencing/voip/VoipCallMonitor.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.telecom.voip;
+package com.android.server.telecom.callsequencing.voip;
 
 import static android.app.ForegroundServiceDelegationOptions.DELEGATION_SERVICE_PHONE_CALL;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index c592908..79fd3d5 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -142,7 +142,7 @@
 import com.android.server.telecom.ui.CallStreamingNotification;
 import com.android.server.telecom.ui.DisconnectedCallNotifier;
 import com.android.server.telecom.ui.ToastFactory;
-import com.android.server.telecom.voip.TransactionManager;
+import com.android.server.telecom.callsequencing.TransactionManager;
 
 import com.google.common.base.Objects;
 
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 793cf14..07a12c4 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -97,9 +97,9 @@
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 import com.android.server.telecom.flags.FeatureFlags;
 import com.android.server.telecom.metrics.TelecomMetricsController;
-import com.android.server.telecom.voip.IncomingCallTransaction;
-import com.android.server.telecom.voip.OutgoingCallTransaction;
-import com.android.server.telecom.voip.TransactionManager;
+import com.android.server.telecom.callsequencing.voip.IncomingCallTransaction;
+import com.android.server.telecom.callsequencing.voip.OutgoingCallTransaction;
+import com.android.server.telecom.callsequencing.TransactionManager;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/tests/src/com/android/server/telecom/tests/TransactionTests.java b/tests/src/com/android/server/telecom/tests/TransactionTests.java
index 5876474..78c2210 100644
--- a/tests/src/com/android/server/telecom/tests/TransactionTests.java
+++ b/tests/src/com/android/server/telecom/tests/TransactionTests.java
@@ -16,24 +16,25 @@
 
 package com.android.server.telecom.tests;
 
-import static com.android.server.telecom.voip.VideoStateTranslation.TransactionalVideoStateToVideoProfileState;
-import static com.android.server.telecom.voip.VideoStateTranslation.VideoProfileStateToTransactionalVideoState;
+import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
+        .TransactionalVideoStateToVideoProfileState;
+import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation
+        .VideoProfileStateToTransactionalVideoState;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.isA;
-import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
@@ -59,30 +60,26 @@
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.ConnectionServiceWrapper;
-import com.android.server.telecom.flags.FeatureFlags;
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
 import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
+import com.android.server.telecom.callsequencing.TransactionManager;
+import com.android.server.telecom.callsequencing.VerifyCallStateChangeTransaction;
+import com.android.server.telecom.callsequencing.voip.EndCallTransaction;
+import com.android.server.telecom.callsequencing.voip.HoldCallTransaction;
+import com.android.server.telecom.callsequencing.voip.IncomingCallTransaction;
+import com.android.server.telecom.callsequencing.voip.MaybeHoldCallForNewCallTransaction;
+import com.android.server.telecom.callsequencing.voip.OutgoingCallTransaction;
+import com.android.server.telecom.callsequencing.voip.RequestNewActiveCallTransaction;
 import com.android.server.telecom.ui.ToastFactory;
-import com.android.server.telecom.voip.EndCallTransaction;
-import com.android.server.telecom.voip.HoldCallTransaction;
-import com.android.server.telecom.voip.IncomingCallTransaction;
-import com.android.server.telecom.voip.OutgoingCallTransaction;
-import com.android.server.telecom.voip.MaybeHoldCallForNewCallTransaction;
-import com.android.server.telecom.voip.RequestNewActiveCallTransaction;
-import com.android.server.telecom.voip.TransactionManager;
-import com.android.server.telecom.voip.VerifyCallStateChangeTransaction;
-import com.android.server.telecom.voip.VideoStateTranslation;
-import com.android.server.telecom.voip.VoipCallTransactionResult;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -421,7 +418,7 @@
 
         // THEN
         verify(mMockCall1, times(1)).addCallStateListener(t.getCallStateListenerImpl());
-        assertEquals(VoipCallTransactionResult.RESULT_SUCCEED,
+        assertEquals(CallTransactionResult.RESULT_SUCCEED,
                 t.getTransactionResult().get(2, TimeUnit.SECONDS).getResult());
         verify(mMockCall1, atLeastOnce()).removeCallStateListener(any());
     }
diff --git a/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
index fa5f2a2..fea6135 100644
--- a/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
+++ b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
@@ -39,10 +39,10 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.TransactionalServiceRepository;
 import com.android.server.telecom.TransactionalServiceWrapper;
-import com.android.server.telecom.voip.EndCallTransaction;
-import com.android.server.telecom.voip.HoldCallTransaction;
-import com.android.server.telecom.voip.SerialTransaction;
-import com.android.server.telecom.voip.TransactionManager;
+import com.android.server.telecom.callsequencing.voip.EndCallTransaction;
+import com.android.server.telecom.callsequencing.voip.HoldCallTransaction;
+import com.android.server.telecom.callsequencing.voip.SerialTransaction;
+import com.android.server.telecom.callsequencing.TransactionManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -83,9 +83,8 @@
         Mockito.when(mCallsManager.getLock()).thenReturn(mLock);
         Mockito.when(mCallEventCallback.asBinder()).thenReturn(mIBinder);
         mTransactionalServiceWrapper = new TransactionalServiceWrapper(mCallEventCallback,
-                mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository);
-
-        mTransactionalServiceWrapper.setTransactionManager(mTransactionManager);
+                mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository, mTransactionManager,
+                false /*call sequencing*/);
     }
 
     @Override
@@ -98,7 +97,8 @@
     public void testTransactionalServiceWrapperStartState() throws Exception {
         TransactionalServiceWrapper service =
                 new TransactionalServiceWrapper(mCallEventCallback,
-                        mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository);
+                        mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository, mTransactionManager,
+                        false /*call sequencing*/);
 
         assertEquals(SERVICE_HANDLE, service.getPhoneAccountHandle());
         assertEquals(1, service.getNumberOfTrackedCalls());
@@ -108,7 +108,8 @@
     public void testTransactionalServiceWrapperCallCount() throws Exception {
         TransactionalServiceWrapper service =
                 new TransactionalServiceWrapper(mCallEventCallback,
-                        mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository);
+                        mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository, mTransactionManager,
+                        false /*call sequencing*/);
 
         assertEquals(1, service.getNumberOfTrackedCalls());
         service.trackCall(mMockCall2);
diff --git a/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java b/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
index 7f7399c..bf68f8c 100644
--- a/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
+++ b/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
@@ -49,7 +49,7 @@
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallState;
 import com.android.server.telecom.TelecomSystem;
-import com.android.server.telecom.voip.VoipCallMonitor;
+import com.android.server.telecom.callsequencing.voip.VoipCallMonitor;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java b/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
index c5be130..c479aac 100644
--- a/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
+++ b/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
@@ -25,11 +25,11 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.telecom.TelecomSystem;
-import com.android.server.telecom.voip.ParallelTransaction;
-import com.android.server.telecom.voip.SerialTransaction;
-import com.android.server.telecom.voip.TransactionManager;
-import com.android.server.telecom.voip.VoipCallTransaction;
-import com.android.server.telecom.voip.VoipCallTransactionResult;
+import com.android.server.telecom.callsequencing.voip.ParallelTransaction;
+import com.android.server.telecom.callsequencing.voip.SerialTransaction;
+import com.android.server.telecom.callsequencing.TransactionManager;
+import com.android.server.telecom.callsequencing.CallTransaction;
+import com.android.server.telecom.callsequencing.CallTransactionResult;
 
 import org.junit.After;
 import org.junit.Before;
@@ -51,7 +51,7 @@
     private TransactionManager mTransactionManager;
     private static final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
 
-    private class TestVoipCallTransaction extends VoipCallTransaction {
+    private class TestVoipCallTransaction extends CallTransaction {
         public static final int SUCCESS = 0;
         public static final int FAILED = 1;
         public static final int TIMEOUT = 2;
@@ -70,27 +70,27 @@
         }
 
         @Override
-        public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+        public CompletionStage<CallTransactionResult> processTransaction(Void v) {
             if (mType == EXCEPTION) {
                 mLog.append(mName).append(" exception;\n");
                 throw new IllegalStateException("TEST EXCEPTION");
             }
-            CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
+            CompletableFuture<CallTransactionResult> resultFuture = new CompletableFuture<>();
             mHandler.postDelayed(() -> {
                 if (mType == SUCCESS) {
                     mLog.append(mName).append(" success;\n");
                     resultFuture.complete(
-                            new VoipCallTransactionResult(VoipCallTransactionResult.RESULT_SUCCEED,
+                            new CallTransactionResult(CallTransactionResult.RESULT_SUCCEED,
                                     null));
                 } else if (mType == FAILED) {
                     mLog.append(mName).append(" failed;\n");
                     resultFuture.complete(
-                            new VoipCallTransactionResult(CallException.CODE_ERROR_UNKNOWN,
+                            new CallTransactionResult(CallException.CODE_ERROR_UNKNOWN,
                                     null));
                 } else {
                     mLog.append(mName).append(" timeout;\n");
                     resultFuture.complete(
-                            new VoipCallTransactionResult(CallException.CODE_ERROR_UNKNOWN,
+                            new CallTransactionResult(CallException.CODE_ERROR_UNKNOWN,
                                     "timeout"));
                 }
             }, mSleepTime);
@@ -122,7 +122,7 @@
     @Test
     public void testSerialTransactionSuccess()
             throws ExecutionException, InterruptedException, TimeoutException {
-        List<VoipCallTransaction> subTransactions = new ArrayList<>();
+        List<CallTransaction> subTransactions = new ArrayList<>();
         TestVoipCallTransaction t1 = new TestVoipCallTransaction("t1", 1000L,
                 TestVoipCallTransaction.SUCCESS);
         TestVoipCallTransaction t2 = new TestVoipCallTransaction("t2", 1000L,
@@ -132,13 +132,13 @@
         subTransactions.add(t1);
         subTransactions.add(t2);
         subTransactions.add(t3);
-        CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
+        CompletableFuture<CallTransactionResult> resultFuture = new CompletableFuture<>();
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeReceiver =
                 resultFuture::complete;
         String expectedLog = "t1 success;\nt2 success;\nt3 success;\n";
         mTransactionManager.addTransaction(new SerialTransaction(subTransactions, mLock),
                 outcomeReceiver);
-        assertEquals(VoipCallTransactionResult.RESULT_SUCCEED,
+        assertEquals(CallTransactionResult.RESULT_SUCCEED,
                 resultFuture.get(5000L, TimeUnit.MILLISECONDS).getResult());
         assertEquals(expectedLog, mLog.toString());
         verifyTransactionsFinished(t1, t2, t3);
@@ -148,7 +148,7 @@
     @Test
     public void testSerialTransactionFailed()
             throws ExecutionException, InterruptedException, TimeoutException {
-        List<VoipCallTransaction> subTransactions = new ArrayList<>();
+        List<CallTransaction> subTransactions = new ArrayList<>();
         TestVoipCallTransaction t1 = new TestVoipCallTransaction("t1", 1000L,
                 TestVoipCallTransaction.SUCCESS);
         TestVoipCallTransaction t2 = new TestVoipCallTransaction("t2", 1000L,
@@ -159,10 +159,10 @@
         subTransactions.add(t2);
         subTransactions.add(t3);
         CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
-                new OutcomeReceiver<VoipCallTransactionResult, CallException>() {
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeReceiver =
+                new OutcomeReceiver<CallTransactionResult, CallException>() {
                     @Override
-                    public void onResult(VoipCallTransactionResult result) {
+                    public void onResult(CallTransactionResult result) {
 
                     }
 
@@ -183,7 +183,7 @@
     @Test
     public void testParallelTransactionSuccess()
             throws ExecutionException, InterruptedException, TimeoutException {
-        List<VoipCallTransaction> subTransactions = new ArrayList<>();
+        List<CallTransaction> subTransactions = new ArrayList<>();
         TestVoipCallTransaction t1 = new TestVoipCallTransaction("t1", 1000L,
                 TestVoipCallTransaction.SUCCESS);
         TestVoipCallTransaction t2 = new TestVoipCallTransaction("t2", 500L,
@@ -193,12 +193,12 @@
         subTransactions.add(t1);
         subTransactions.add(t2);
         subTransactions.add(t3);
-        CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
+        CompletableFuture<CallTransactionResult> resultFuture = new CompletableFuture<>();
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeReceiver =
                 resultFuture::complete;
         mTransactionManager.addTransaction(new ParallelTransaction(subTransactions, mLock),
                 outcomeReceiver);
-        assertEquals(VoipCallTransactionResult.RESULT_SUCCEED,
+        assertEquals(CallTransactionResult.RESULT_SUCCEED,
                 resultFuture.get(5000L, TimeUnit.MILLISECONDS).getResult());
         String log = mLog.toString();
         assertTrue(log.contains("t1 success;\n"));
@@ -211,7 +211,7 @@
     @Test
     public void testParallelTransactionFailed()
             throws ExecutionException, InterruptedException, TimeoutException {
-        List<VoipCallTransaction> subTransactions = new ArrayList<>();
+        List<CallTransaction> subTransactions = new ArrayList<>();
         TestVoipCallTransaction t1 = new TestVoipCallTransaction("t1", 1000L,
                 TestVoipCallTransaction.SUCCESS);
         TestVoipCallTransaction t2 = new TestVoipCallTransaction("t2", 500L,
@@ -222,10 +222,10 @@
         subTransactions.add(t2);
         subTransactions.add(t3);
         CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeReceiver =
                 new OutcomeReceiver<>() {
             @Override
-            public void onResult(VoipCallTransactionResult result) {
+            public void onResult(CallTransactionResult result) {
 
             }
 
@@ -248,10 +248,10 @@
         TestVoipCallTransaction t = new TestVoipCallTransaction("t", 10000L,
                 TestVoipCallTransaction.SUCCESS);
         CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeReceiver =
                 new OutcomeReceiver<>() {
                     @Override
-                    public void onResult(VoipCallTransactionResult result) {
+                    public void onResult(CallTransactionResult result) {
 
                     }
 
@@ -275,10 +275,10 @@
         TestVoipCallTransaction t2 = new TestVoipCallTransaction("t2", 1000L,
                 TestVoipCallTransaction.SUCCESS);
         CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeExceptionReceiver =
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeExceptionReceiver =
                 new OutcomeReceiver<>() {
                     @Override
-                    public void onResult(VoipCallTransactionResult result) {
+                    public void onResult(CallTransactionResult result) {
                     }
 
                     @Override
@@ -291,12 +291,12 @@
         exceptionFuture.get(7000L, TimeUnit.MILLISECONDS);
         assertTrue(mLog.toString().contains("t1 exception;\n"));
         // Verify an exception in a processing a previous transaction does not stall the next one.
-        CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
+        CompletableFuture<CallTransactionResult> resultFuture = new CompletableFuture<>();
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeReceiver =
                 resultFuture::complete;
         mTransactionManager.addTransaction(t2, outcomeReceiver);
         String expectedLog = "t1 exception;\nt2 success;\n";
-        assertEquals(VoipCallTransactionResult.RESULT_SUCCEED,
+        assertEquals(CallTransactionResult.RESULT_SUCCEED,
                 resultFuture.get(5000L, TimeUnit.MILLISECONDS).getResult());
         assertEquals(expectedLog, mLog.toString());
         verifyTransactionsFinished(t1, t2);
@@ -317,10 +317,10 @@
                 TestVoipCallTransaction.EXCEPTION);
         // verify the TransactionManager informs the client of the failed transaction
         CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeExceptionReceiver =
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeExceptionReceiver =
                 new OutcomeReceiver<>() {
                     @Override
-                    public void onResult(VoipCallTransactionResult result) {
+                    public void onResult(CallTransactionResult result) {
                     }
 
                     @Override
@@ -346,10 +346,10 @@
                 TestVoipCallTransaction.SUCCESS);
         TestVoipCallTransaction t3 = new TestVoipCallTransaction("t3", 1000L,
                 TestVoipCallTransaction.SUCCESS);
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeExceptionReceiver =
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeExceptionReceiver =
                 new OutcomeReceiver<>() {
                     @Override
-                    public void onResult(VoipCallTransactionResult result) {
+                    public void onResult(CallTransactionResult result) {
                         throw new IllegalStateException("RESULT EXCEPTION");
                     }
 
@@ -358,10 +358,10 @@
                     }
                 };
         mTransactionManager.addTransaction(t1, outcomeExceptionReceiver);
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeException2Receiver =
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeException2Receiver =
                 new OutcomeReceiver<>() {
                     @Override
-                    public void onResult(VoipCallTransactionResult result) {
+                    public void onResult(CallTransactionResult result) {
                     }
 
                     @Override
@@ -371,12 +371,12 @@
                 };
         mTransactionManager.addTransaction(t2, outcomeException2Receiver);
         // Verify an exception in a previous transaction result does not stall the next one.
-        CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
-        OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
+        CompletableFuture<CallTransactionResult> resultFuture = new CompletableFuture<>();
+        OutcomeReceiver<CallTransactionResult, CallException> outcomeReceiver =
                 resultFuture::complete;
         mTransactionManager.addTransaction(t3, outcomeReceiver);
         String expectedLog = "t1 success;\nt2 success;\nt3 success;\n";
-        assertEquals(VoipCallTransactionResult.RESULT_SUCCEED,
+        assertEquals(CallTransactionResult.RESULT_SUCCEED,
                 resultFuture.get(5000L, TimeUnit.MILLISECONDS).getResult());
         assertEquals(expectedLog, mLog.toString());
         verifyTransactionsFinished(t1, t2, t3);