Merge "Revert^2 "Modify Tests to make SessionManager final for tests"" into main
diff --git a/flags/telecom_non_critical_security_flags.aconfig b/flags/telecom_non_critical_security_flags.aconfig
index 37929a8..e492073 100644
--- a/flags/telecom_non_critical_security_flags.aconfig
+++ b/flags/telecom_non_critical_security_flags.aconfig
@@ -7,4 +7,15 @@
   namespace: "telecom"
   description: "When set, Telecom will unregister accounts if the service is not resolvable"
   bug: "281061708"
+}
+
+# OWNER=tgunn TARGET=25Q2
+flag {
+  name: "enforce_transactional_exclusivity"
+  namespace: "telecom"
+  description: "When set, ensure that transactional accounts cannot also be call capable"
+  bug: "376936125"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
\ No newline at end of file
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index d2ac13a..ba75d0c 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -100,7 +100,7 @@
     <string name="notification_channel_background_calls" msgid="7785659903711350506">"Pozivi u pozadini"</string>
     <string name="notification_channel_disconnected_calls" msgid="8228636543997645757">"Prekinuti pozivi"</string>
     <string name="notification_channel_in_call_service_crash" msgid="7313237519166984267">"Padovi aplikacija za telefon"</string>
-    <string name="notification_channel_call_streaming" msgid="5100510699787538991">"Prijenos poziva"</string>
+    <string name="notification_channel_call_streaming" msgid="5100510699787538991">"Prenos poziva"</string>
     <string name="alert_outgoing_call" msgid="5319895109298927431">"Upućivanje ovog poziva će prekinuti poziv: <xliff:g id="OTHER_APP">%1$s</xliff:g>"</string>
     <string name="alert_redirect_outgoing_call_or_not" msgid="665409645789521636">"Odaberite kako želite uputiti ovaj poziv"</string>
     <string name="alert_place_outgoing_call_with_redirection" msgid="5221065030959024121">"Preusmjeri poziv pomoću aplikacije <xliff:g id="OTHER_APP">%1$s</xliff:g>"</string>
@@ -131,7 +131,7 @@
     <string name="callendpoint_name_speaker" msgid="1971760468695323189">"Zvučnik"</string>
     <string name="callendpoint_name_streaming" msgid="2337595450408275576">"Vanjski"</string>
     <string name="callendpoint_name_unknown" msgid="2199074708477193852">"Nepoznato"</string>
-    <string name="call_streaming_notification_body" msgid="502216105683378263">"Prijenos zvuka na drugom uređaju"</string>
+    <string name="call_streaming_notification_body" msgid="502216105683378263">"Prenos zvuka na drugom uređaju"</string>
     <string name="call_streaming_notification_action_hang_up" msgid="7017663335289063827">"Prekini vezu"</string>
     <string name="call_streaming_notification_action_switch_here" msgid="3524180754186221228">"Prebaci ovdje"</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 876b049..7d8045a 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -126,7 +126,7 @@
     <string name="cancel" msgid="6733466216239934756">"Anulo"</string>
     <string name="back" msgid="6915955601805550206">"Pas"</string>
     <string name="callendpoint_name_earpiece" msgid="7047285080319678594">"Receptori"</string>
-    <string name="callendpoint_name_bluetooth" msgid="210210953208913172">"Bluetooth"</string>
+    <string name="callendpoint_name_bluetooth" msgid="210210953208913172">"Bluetooth-i"</string>
     <string name="callendpoint_name_wiredheadset" msgid="6860787176412079742">"Kufje me tel"</string>
     <string name="callendpoint_name_speaker" msgid="1971760468695323189">"Altoparlant"</string>
     <string name="callendpoint_name_streaming" msgid="2337595450408275576">"E jashtme"</string>
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/InCallController.java b/src/com/android/server/telecom/InCallController.java
index a2998fb..bda5063 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -1581,8 +1581,16 @@
                         mHandler.postDelayed(new Runnable("ICC.oDCTP", mLock) {
                             @Override
                             public void loggedRun() {
-                                Log.i(this, "onDisconnectedTonePlaying: unbinding");
-                                connection.disconnect();
+                                Log.i(this, "onDisconnectedTonePlaying: unbinding from BT ICS.");
+                                // Prevent unbinding in the case that this is run while another call
+                                // has been placed/received. Otherwise, we will early unbind from
+                                // the BT ICS and not be able to properly relay call state updates.
+                                if (!mBTInCallServiceConnections.containsKey(userHandle)) {
+                                    connection.disconnect();
+                                } else {
+                                    Log.i(this, "onDisconnectedTonePlaying: Refraining from "
+                                            + "unbinding BT ICS. Another call is ongoing.");
+                                }
                             }
                         }.prepare(), mTimeoutsAdapter.getCallRemoveUnbindInCallServicesDelay(
                                 mContext.getContentResolver()));
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index f0423c3..796a62b 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -1284,12 +1284,15 @@
         boolean isNewAccount;
 
         // add self-managed capability for transactional accounts that are missing it
-        if (hasTransactionalCallCapabilities(account) &&
-                !account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
+        if (hasTransactionalCallCapabilities(account)
+                && !account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
             account = account.toBuilder()
                     .setCapabilities(account.getCapabilities()
                             | PhoneAccount.CAPABILITY_SELF_MANAGED)
                     .build();
+            // Note: below we will automatically remove CAPABILITY_CONNECTION_MANAGER,
+            // CAPABILITY_CALL_PROVIDER, and CAPABILITY_SIM_SUBSCRIPTION if this magically becomes
+            // a self-managed phone account here.
         }
 
         PhoneAccount oldAccount = getPhoneAccountUnchecked(account.getAccountHandle());
@@ -1310,6 +1313,12 @@
         if (account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
             // Turn off bits we don't want to be able to set (TelecomServiceImpl protects against
             // this but we'll also prevent it from happening here, just to be safe).
+            if ((account.getCapabilities() & (PhoneAccount.CAPABILITY_CALL_PROVIDER
+                    | PhoneAccount.CAPABILITY_CONNECTION_MANAGER
+                    | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) > 0) {
+                Log.w(this, "addOrReplacePhoneAccount: attempt to register a "
+                        + "VoIP phone account with call provider/cm/sim sub capabilities.");
+            }
             int newCapabilities = account.getCapabilities() &
                     ~(PhoneAccount.CAPABILITY_CALL_PROVIDER |
                         PhoneAccount.CAPABILITY_CONNECTION_MANAGER |
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index e6a6dd6..7ec2687 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();
 
@@ -882,16 +882,20 @@
                     try {
                         enforcePhoneAccountModificationForPackage(
                                 account.getAccountHandle().getComponentName().getPackageName());
-                        if (account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
+                        if (account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+                                || (mFeatureFlags.enforceTransactionalExclusivity()
+                                && account.hasCapabilities(
+                                PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS))) {
                             enforceRegisterSelfManaged();
                             if (account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) ||
                                     account.hasCapabilities(
                                             PhoneAccount.CAPABILITY_CONNECTION_MANAGER) ||
                                     account.hasCapabilities(
                                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
-                                throw new SecurityException("Self-managed ConnectionServices " +
-                                        "cannot also be call capable, connection managers, or " +
-                                        "SIM accounts.");
+                                throw new SecurityException("Self-managed ConnectionServices and "
+                                        + "transactional voip apps "
+                                        + "cannot also be call capable, connection managers, or "
+                                        + "SIM accounts.");
                             }
 
                             // For self-managed CS, the phone account registrar will override the
@@ -2951,7 +2955,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/testapps/transactionalVoipApp/res/values-bs/strings.xml b/testapps/transactionalVoipApp/res/values-bs/strings.xml
index 24ffba2..f417043 100644
--- a/testapps/transactionalVoipApp/res/values-bs/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-bs/strings.xml
@@ -31,7 +31,7 @@
     <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Slušalica"</string>
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Zvučnik"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
-    <string name="start_stream" msgid="3567634786280097431">"pokreni prijenos"</string>
+    <string name="start_stream" msgid="3567634786280097431">"pokreni prenos"</string>
     <string name="crash_app" msgid="2548690390730057704">"izbaci izuzetak"</string>
     <string name="update_notification" msgid="8677916482672588779">"ažuriraj obavještenje u stil poziva u toku"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-in/strings.xml b/testapps/transactionalVoipApp/res/values-in/strings.xml
index 935f036..ba41376 100644
--- a/testapps/transactionalVoipApp/res/values-in/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-in/strings.xml
@@ -27,7 +27,7 @@
     <string name="set_call_active" msgid="3365404393507589899">"setelAktif"</string>
     <string name="answer" msgid="5423590397665409939">"jawab"</string>
     <string name="set_call_inactive" msgid="7106775211368705195">"setelNonaktif"</string>
-    <string name="disconnect_call" msgid="1349412380315371385">"putuskan koneksi"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"berhenti hubungkan"</string>
     <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Earpiece"</string>
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
diff --git a/testapps/transactionalVoipApp/res/values-sq/strings.xml b/testapps/transactionalVoipApp/res/values-sq/strings.xml
index ddaba66..2816473 100644
--- a/testapps/transactionalVoipApp/res/values-sq/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sq/strings.xml
@@ -30,7 +30,7 @@
     <string name="disconnect_call" msgid="1349412380315371385">"shkëput"</string>
     <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Receptori"</string>
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altoparlanti"</string>
-    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth-i"</string>
     <string name="start_stream" msgid="3567634786280097431">"nis transmetimin"</string>
     <string name="crash_app" msgid="2548690390730057704">"gjenero një përjashtim"</string>
     <string name="update_notification" msgid="8677916482672588779">"përditëso njoftimin me stilin e telefonatës në vazhdim"</string>
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/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index 45b4ed1..519e596 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -113,7 +113,8 @@
     private final String PACKAGE_1 = "PACKAGE_1";
     private final String PACKAGE_2 = "PACKAGE_2";
     private final String COMPONENT_NAME = "com.android.server.telecom.tests.MockConnectionService";
-    private final UserHandle USER_HANDLE_10 = new UserHandle(10);
+    private final UserHandle USER_HANDLE_10 = UserHandle.of(10);
+    private final UserHandle USER_HANDLE_1000 = UserHandle.of(1000);
     private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
     private PhoneAccountRegistrar mRegistrar;
     @Mock private SubscriptionManager mSubscriptionManager;
@@ -140,6 +141,7 @@
         mRegistrar = new PhoneAccountRegistrar(
                 mComponentContextFixture.getTestDouble().getApplicationContext(), mLock, FILE_NAME,
                 mDefaultDialerCache, mAppLabelProxy, mTelephonyFeatureFlags, mFeatureFlags);
+        mRegistrar.setCurrentUserHandle(UserHandle.SYSTEM);
         when(mFeatureFlags.onlyUpdateTelephonyOnValidSubIds()).thenReturn(false);
         when(mFeatureFlags.unregisterUnresolvableAccounts()).thenReturn(true);
         when(mTelephonyFeatureFlags.workProfileApiSplit()).thenReturn(false);
@@ -1306,8 +1308,7 @@
                 Mockito.mock(IConnectionService.class));
         UserManager userManager = mContext.getSystemService(UserManager.class);
 
-        List<UserHandle> users = Arrays.asList(new UserHandle(0),
-                new UserHandle(1000));
+        List<UserHandle> users = Arrays.asList(UserHandle.SYSTEM, USER_HANDLE_1000);
 
         PhoneAccount pa1 = new PhoneAccount.Builder(
                 new PhoneAccountHandle(new ComponentName(PACKAGE_1, COMPONENT_NAME), "1234",
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);