Merge "Set telecomCallId in Remote ConnectionRequest" into main
diff --git a/flags/telecom_api_flags.aconfig b/flags/telecom_api_flags.aconfig
index c44bea4..c0f4cba 100644
--- a/flags/telecom_api_flags.aconfig
+++ b/flags/telecom_api_flags.aconfig
@@ -48,4 +48,12 @@
   namespace: "telecom"
   description: "Enables enriched calling features (e.g. Business name will show for a call)"
   bug: "311688497"
+  is_exported: true
+}
+
+flag{
+  name: "get_last_known_cell_identity"
+  namespace: "telecom"
+  description: "Formalizes the getLastKnownCellIdentity API that Telecom reliees on as a system api"
+  bug: "327454165"
 }
diff --git a/src/com/android/server/telecom/CallAudioRouteController.java b/src/com/android/server/telecom/CallAudioRouteController.java
index 091c8fc..d38577d 100644
--- a/src/com/android/server/telecom/CallAudioRouteController.java
+++ b/src/com/android/server/telecom/CallAudioRouteController.java
@@ -836,6 +836,16 @@
         CallAudioState oldState = mCallAudioState;
         mCallAudioState = callAudioState;
         mCallsManager.onCallAudioStateChanged(oldState, mCallAudioState);
+        updateAudioStateForTrackedCalls(mCallAudioState);
+    }
+
+    private void updateAudioStateForTrackedCalls(CallAudioState newCallAudioState) {
+        Set<Call> calls = mCallsManager.getTrackedCalls();
+        for (Call call : calls) {
+            if (call != null && call.getConnectionService() != null) {
+                call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState);
+            }
+        }
     }
 
     private AudioRoute getPreferredAudioRouteFromStrategy() {
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index f6360a3..56c568f 100644
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom;
 
+import static android.provider.CallLog.AddCallParams.AddCallParametersBuilder.MAX_NUMBER_OF_CHARACTERS;
 import static android.provider.CallLog.Calls.BLOCK_REASON_NOT_BLOCKED;
 import static android.telephony.CarrierConfigManager.KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL;
 
@@ -31,6 +32,7 @@
 import android.location.Location;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Looper;
@@ -60,8 +62,6 @@
 import java.util.Locale;
 import java.util.Objects;
 import java.util.UUID;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
 import java.util.stream.Stream;
 
 /**
@@ -419,11 +419,24 @@
         paramBuilder.setCallType(callLogType);
         paramBuilder.setIsRead(call.isSelfManaged());
         paramBuilder.setMissedReason(call.getMissedReason());
-        if (Flags.businessCallComposer() && call.getExtras() != null) {
-            paramBuilder.setIsBusinessCall(call.getExtras().getBoolean(
-                    android.telecom.Call.EXTRA_IS_BUSINESS_CALL, false));
-            paramBuilder.setBusinessName(call.getExtras().getString(
-                    android.telecom.Call.EXTRA_ASSERTED_DISPLAY_NAME, ""));
+        if (mFeatureFlags.businessCallComposer() && call.getExtras() != null) {
+            Bundle extras = call.getExtras();
+            boolean isBusinessCall =
+                    extras.getBoolean(android.telecom.Call.EXTRA_IS_BUSINESS_CALL, false);
+            paramBuilder.setIsBusinessCall(isBusinessCall);
+            if (isBusinessCall) {
+                Log.i(TAG, "logging business call");
+                String assertedDisplayName =
+                        extras.getString(android.telecom.Call.EXTRA_ASSERTED_DISPLAY_NAME, "");
+                if (assertedDisplayName.length() > MAX_NUMBER_OF_CHARACTERS) {
+                    // avoid throwing an IllegalArgumentException and only log the first 256
+                    // characters of the name.
+                    paramBuilder.setAssertedDisplayName(
+                            assertedDisplayName.substring(0, MAX_NUMBER_OF_CHARACTERS));
+                } else {
+                    paramBuilder.setAssertedDisplayName(assertedDisplayName);
+                }
+            }
         }
         sendAddCallBroadcast(callLogType, call.getAgeMillis());
 
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index ae096af..f4b7840 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -82,7 +82,6 @@
 import android.provider.BlockedNumberContract.BlockedNumbers;
 import android.provider.CallLog.Calls;
 import android.provider.Settings;
-import android.sysprop.TelephonyProperties;
 import android.telecom.CallAttributes;
 import android.telecom.CallAudioState;
 import android.telecom.CallEndpoint;
@@ -165,7 +164,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -491,6 +489,7 @@
     private final UserManager mUserManager;
     private final CallStreamingNotification mCallStreamingNotification;
     private final FeatureFlags mFeatureFlags;
+    private final com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;
 
     private final IncomingCallFilterGraphProvider mIncomingCallFilterGraphProvider;
 
@@ -610,6 +609,7 @@
             CallStreamingNotification callStreamingNotification,
             BluetoothDeviceManager bluetoothDeviceManager,
             FeatureFlags featureFlags,
+            com.android.internal.telephony.flags.FeatureFlags telephonyFlags,
             IncomingCallFilterGraphProvider incomingCallFilterGraphProvider) {
 
         mContext = context;
@@ -717,6 +717,7 @@
         mCallStreamingController = new CallStreamingController(mContext, mLock);
         mCallStreamingNotification = callStreamingNotification;
         mFeatureFlags = featureFlags;
+        mTelephonyFeatureFlags = telephonyFlags;
 
         if (mFeatureFlags.useImprovedListenerOrder()) {
             mListeners.add(mInCallController);
@@ -3428,8 +3429,14 @@
     // then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP.
     @VisibleForTesting
     public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user,
-            boolean isVideo, boolean isEmergency) {
-        return constructPossiblePhoneAccounts(handle, user, isVideo, isEmergency, false);
+            boolean isVideo, boolean isEmergency,  boolean isConference) {
+        if (mTelephonyFeatureFlags.simultaneousCallingIndications()) {
+            return constructPossiblePhoneAccountsNew(handle, user, isVideo, isEmergency,
+                    isConference);
+        } else {
+            return constructPossiblePhoneAccountsOld(handle, user, isVideo, isEmergency,
+                    isConference);
+        }
     }
 
     // Returns whether the device is capable of 2 simultaneous active voice calls on different subs.
@@ -3444,7 +3451,7 @@
         }
     }
 
-    public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user,
+    private List<PhoneAccountHandle> constructPossiblePhoneAccountsOld(Uri handle, UserHandle user,
             boolean isVideo, boolean isEmergency, boolean isConference) {
 
         if (handle == null) {
@@ -3485,6 +3492,82 @@
         return allAccounts;
     }
 
+    /**
+     * Filters the list of all PhoneAccounts that match the outgoing call Handle's schema against
+     * the outgoing call request criteria and the state of the already ongoing calls on the
+     * device and their potential simultaneous calling restrictions.
+     * @return The filtered List
+     */
+    private List<PhoneAccountHandle> constructPossiblePhoneAccountsNew(Uri handle, UserHandle user,
+            boolean isVideo, boolean isEmergency, boolean isConference) {
+        if (handle == null) {
+            return Collections.emptyList();
+        }
+        // If we're specifically looking for video capable accounts, then include that capability,
+        // otherwise specify no additional capability constraints. When handling the emergency call,
+        // it also needs to find the phone accounts excluded by CAPABILITY_EMERGENCY_CALLS_ONLY.
+        int capabilities = isVideo ? PhoneAccount.CAPABILITY_VIDEO_CALLING : 0;
+        capabilities |= isConference ? PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING : 0;
+        List<PhoneAccountHandle> allAccounts =
+                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user,
+                        capabilities,
+                        isEmergency ? 0 : PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY,
+                        isEmergency);
+        Log.v(this, "constructPossiblePhoneAccountsNew: allAccounts=" + allAccounts);
+        Set<PhoneAccountHandle> activeCallAccounts = mCalls.stream()
+                .filter(c -> !c.isDisconnected() && !c.isNew()).map(Call::getTargetPhoneAccount)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        Log.v(this, "constructPossiblePhoneAccountsNew: activeCallAccounts="
+                + activeCallAccounts);
+        // No Active calls - all accounts are valid
+        if (activeCallAccounts.isEmpty()) return allAccounts;
+        // The emergency call should be attempted only over the same SIM PhoneAccounts where there
+        // are already ongoing calls - filter out inactive SIM PhoneAccounts in this case.
+        if (isEmergency) {
+            Set<PhoneAccountHandle> simAccounts =
+                    new HashSet<>(mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser());
+            if (activeCallAccounts.stream().anyMatch(simAccounts::contains)) {
+                allAccounts.removeIf(h -> {
+                    boolean isRemoved = simAccounts.contains(h) && !activeCallAccounts.contains(h);
+                    if (isRemoved) {
+                        Log.i(this, "constructPossiblePhoneAccountsNew: removing candidate PAH ["
+                                + h + "] because another SIM account is active with an emergency "
+                                + "call");
+                    }
+                    return isRemoved;
+                });
+            }
+        }
+        // Apply restrictions to which PhoneAccounts can be used to place a call by looking at
+        // active calls and removing candidate PhoneAccounts if they are from the same source
+        // as the active call and the candidate PhoneAccount is not part of the restriction.
+        for (PhoneAccountHandle callHandle : activeCallAccounts) {
+            allAccounts.removeIf(candidateHandle -> {
+                PhoneAccount callAcct = mPhoneAccountRegistrar.getPhoneAccount(callHandle,
+                        user);
+                if (callAcct == null) {
+                    Log.w(this, "constructPossiblePhoneAccountsNew: unexpected"
+                            + "null PA for PAH, removing : " + candidateHandle);
+                    return true;
+                }
+                boolean isRemoved = !Objects.equals(candidateHandle, callHandle)
+                        && Objects.equals(candidateHandle.getComponentName(),
+                                callHandle.getComponentName())
+                        && callAcct.hasSimultaneousCallingRestriction()
+                        && !callAcct.getSimultaneousCallingRestriction().contains(candidateHandle);
+                if (isRemoved) {
+                    Log.i(this, "constructPossiblePhoneAccountsNew: removing candidate"
+                            + " PAH [" + candidateHandle + "] because it is not part of the"
+                            + " restriction set by [" + callHandle + "], restriction="
+                            + callAcct.getSimultaneousCallingRestriction());
+                }
+                return isRemoved;
+            });
+        }
+        return allAccounts;
+    }
+
     private TelephonyManager getTelephonyManager() {
         return mContext.getSystemService(TelephonyManager.class);
     }
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index b4c3a4d..1d0300d 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -230,7 +230,8 @@
             Executor asyncTaskExecutor,
             Executor asyncCallAudioTaskExecutor,
             BlockedNumbersAdapter blockedNumbersAdapter,
-            FeatureFlags featureFlags) {
+            FeatureFlags featureFlags,
+            com.android.internal.telephony.flags.FeatureFlags telephonyFlags) {
         mContext = context.getApplicationContext();
         mFeatureFlags = featureFlags;
         LogUtils.initLogging(mContext);
@@ -427,6 +428,7 @@
                     callStreamingNotification,
                     bluetoothDeviceManager,
                     featureFlags,
+                    telephonyFlags,
                     IncomingCallFilterGraph::new);
 
             mIncomingCallNotifier = incomingCallNotifier;
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 24e5d57..845f788 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -234,7 +234,8 @@
                                             showNotification);
                                 }
                             },
-                            new FeatureFlagsImpl()));
+                            new FeatureFlagsImpl(),
+                            new com.android.internal.telephony.flags.FeatureFlagsImpl()));
         }
     }
 
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index 645a42b..e048f21 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -34,6 +34,7 @@
     <uses-permission android:name="android.permission.REGISTER_CONNECTION_MANAGER"/>
     <uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
     <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
 
     <application android:label="@string/app_name">
         <uses-library android:name="android.test.runner"/>
diff --git a/testapps/res/layout/testdialer_main.xml b/testapps/res/layout/testdialer_main.xml
index 749d236..e4b5bef 100644
--- a/testapps/res/layout/testdialer_main.xml
+++ b/testapps/res/layout/testdialer_main.xml
@@ -54,6 +54,23 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/cancelMissedButton" />
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+    <EditText
+        android:id="@+id/set_composer_edit_text"
+        android:inputType="number"
+        android:layout_width="200dp"
+        android:layout_height="wrap_content" />
+    <Button
+        android:id="@+id/submit_composer_value"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/submitCallComposerLabel" />
+    </LinearLayout>
+
     <CheckBox
         android:id="@+id/call_with_rtt_checkbox"
         android:layout_width="wrap_content"
diff --git a/testapps/res/values/donottranslate_strings.xml b/testapps/res/values/donottranslate_strings.xml
index b1a1f80..39c2deb 100644
--- a/testapps/res/values/donottranslate_strings.xml
+++ b/testapps/res/values/donottranslate_strings.xml
@@ -100,6 +100,8 @@
 
     <string name="postCallActivityLabel">Test Post Call Screen</string>
 
+    <string name="submitCallComposerLabel">Set Call Composer</string>
+
     <string-array name="rtt_mode_array">
         <item>Full</item>
         <item>HCO</item>
diff --git a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
index f17af2c..5a59c24 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
@@ -20,6 +20,7 @@
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsRcsManager;
 import android.util.Log;
 import android.view.View;
@@ -30,9 +31,11 @@
 import android.widget.Toast;
 
 public class TestDialerActivity extends Activity {
+    private static final String TAG = TestDialerActivity.class.getSimpleName();
     private static final int REQUEST_CODE_SET_DEFAULT_DIALER = 1;
 
     private EditText mNumberView;
+    private EditText mCallComposerView;
     private CheckBox mRttCheckbox;
     private CheckBox mComposerCheckbox;
     private EditText mPriorityView;
@@ -57,6 +60,13 @@
             }
         });
 
+        findViewById(R.id.submit_composer_value).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                setCallComposer();
+            }
+        });
+
         findViewById(R.id.place_call_button).setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -79,6 +89,7 @@
         });
 
         mNumberView = (EditText) findViewById(R.id.number);
+        mCallComposerView = (EditText) findViewById(R.id.set_composer_edit_text);
         mRttCheckbox = (CheckBox) findViewById(R.id.call_with_rtt_checkbox);
         mComposerCheckbox = (CheckBox) findViewById(R.id.add_composer_attachments_checkbox);
         findViewById(R.id.enable_car_mode).setOnClickListener(new OnClickListener() {
@@ -141,6 +152,23 @@
         }
     }
 
+    // Testers need a way of setting the call composer since this is currently not supported by
+    // Dialer.  In the future, this will be a Dialer setting that users can enable/disable.
+    private void setCallComposer() {
+        final TelephonyManager telephonyManager =
+                (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+        String number = mCallComposerView.getText().toString();
+        try {
+            Log.i(TAG, "setCallComposer: value=[" + number + "]");
+            telephonyManager.setCallComposerStatus(Integer.parseInt(number));
+            Log.i(TAG, "setCallComposer: successfully set composer");
+        } catch (Exception e) {
+            Log.i(TAG, "setCallComposer: hit exception while setting the call composer."
+                    + " See stack trace below for more info!");
+            e.printStackTrace();
+        }
+    }
+
     private void placeCall() {
         final TelecomManager telecomManager =
                 (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 1529629..cb9aba9 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -78,13 +78,13 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneCapability;
 import android.telephony.TelephonyManager;
+import android.util.ArraySet;
 import android.util.Pair;
 import android.widget.Toast;
 
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.telecom.IConnectionService;
 import com.android.server.telecom.AnomalyReporterAdapter;
 import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.Call;
@@ -103,7 +103,6 @@
 import com.android.server.telecom.ConnectionServiceFocusManager;
 import com.android.server.telecom.ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory;
 import com.android.server.telecom.ConnectionServiceWrapper;
-import com.android.server.telecom.CreateConnectionResponse;
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.EmergencyCallDiagnosticLogger;
 import com.android.server.telecom.EmergencyCallHelper;
@@ -112,7 +111,6 @@
 import com.android.server.telecom.HeadsetMediaButtonFactory;
 import com.android.server.telecom.InCallController;
 import com.android.server.telecom.InCallControllerFactory;
-import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.InCallTonePlayer;
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.InCallWakeLockControllerFactory;
@@ -140,6 +138,8 @@
 import com.android.server.telecom.ui.ToastFactory;
 import com.android.server.telecom.voip.TransactionManager;
 
+import com.google.common.base.Objects;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -148,7 +148,6 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatchers;
 import org.mockito.InOrder;
-import org.mockito.Matchers;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -158,10 +157,12 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 @RunWith(JUnit4.class)
 public class CallsManagerTest extends TelecomTestCase {
@@ -177,6 +178,10 @@
             new UserHandle(SECONDARY_USER_ID));
     private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle(
             ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2");
+    private static final PhoneAccountHandle SIM_3_HANDLE = new PhoneAccountHandle(
+            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim3");
+    private static final PhoneAccountHandle CALL_PROVIDER_HANDLE = new PhoneAccountHandle(
+            ComponentName.unflattenFromString("com.sip.foo/.Blah"), "sip1");
     private static final PhoneAccountHandle CONNECTION_MGR_1_HANDLE = new PhoneAccountHandle(
             ComponentName.unflattenFromString("com.bar/.Conn"), "Cm1");
     private static final PhoneAccountHandle CONNECTION_MGR_2_HANDLE = new PhoneAccountHandle(
@@ -210,6 +215,18 @@
                     | PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING)
             .setIsEnabled(true)
             .build();
+    private static final PhoneAccount SIM_3_ACCOUNT = new PhoneAccount.Builder(SIM_3_HANDLE, "Sim3")
+            .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
+                    | PhoneAccount.CAPABILITY_CALL_PROVIDER
+                    | PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING)
+            .setIsEnabled(true)
+            .build();
+    private static final PhoneAccount CALL_PROVIDER_ACCOUNT =
+            new PhoneAccount.Builder(CALL_PROVIDER_HANDLE, "sip1")
+            .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER
+                    | PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING)
+            .setIsEnabled(true)
+            .build();
     private static final PhoneAccount SELF_MANAGED_ACCOUNT = new PhoneAccount.Builder(
             SELF_MANAGED_HANDLE, "Self")
             .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
@@ -293,6 +310,7 @@
     @Mock private CallStreamingNotification mCallStreamingNotification;
     @Mock private BluetoothDeviceManager mBluetoothDeviceManager;
     @Mock private FeatureFlags mFeatureFlags;
+    @Mock private com.android.internal.telephony.flags.FeatureFlags mTelephonyFlags;
     @Mock private IncomingCallFilterGraph mIncomingCallFilterGraph;
     private CallsManager mCallsManager;
 
@@ -370,6 +388,7 @@
                 mCallStreamingNotification,
                 mBluetoothDeviceManager,
                 mFeatureFlags,
+                mTelephonyFlags,
                 (call, listener, context, timeoutsAdapter, lock) -> mIncomingCallFilterGraph);
 
         when(mPhoneAccountRegistrar.getPhoneAccount(
@@ -379,6 +398,10 @@
         when(mPhoneAccountRegistrar.getPhoneAccount(
                 eq(SIM_2_HANDLE), any())).thenReturn(SIM_2_ACCOUNT);
         when(mPhoneAccountRegistrar.getPhoneAccount(
+                eq(SIM_3_HANDLE), any())).thenReturn(SIM_3_ACCOUNT);
+        when(mPhoneAccountRegistrar.getPhoneAccount(
+                eq(CALL_PROVIDER_HANDLE), any())).thenReturn(CALL_PROVIDER_ACCOUNT);
+        when(mPhoneAccountRegistrar.getPhoneAccount(
                 eq(WORK_HANDLE), any())).thenReturn(WORK_ACCOUNT);
         when(mToastFactory.makeText(any(), anyInt(), anyInt())).thenReturn(mToast);
         when(mToastFactory.makeText(any(), any(), anyInt())).thenReturn(mToast);
@@ -394,8 +417,21 @@
     @MediumTest
     @Test
     public void testConstructPossiblePhoneAccounts() throws Exception {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(false);
+        setupMsimAccounts();
         // Should be empty since the URI is null.
-        assertEquals(0, mCallsManager.constructPossiblePhoneAccounts(null, null, false, false).size());
+        assertEquals(0, mCallsManager.constructPossiblePhoneAccounts(null, null,
+                false, false, false).size());
+    }
+
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccounts_simulCalling() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupMsimAccounts();
+        // Should be empty since the URI is null.
+        assertEquals(0, mCallsManager.constructPossiblePhoneAccounts(null, null,
+                false, false, false).size());
     }
 
     private Call constructOngoingCall(String callId, PhoneAccountHandle phoneAccountHandle) {
@@ -419,6 +455,7 @@
         ongoingCall.setState(CallState.ACTIVE, "just cuz");
         return ongoingCall;
     }
+
     /**
      * Verify behavior for multisim devices where we want to ensure that the active sim is used for
      * placing a new call.
@@ -427,32 +464,127 @@
     @MediumTest
     @Test
     public void testConstructPossiblePhoneAccountsMultiSimActive() throws Exception {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(false);
         setupMsimAccounts();
 
         Call ongoingCall = constructOngoingCall("1", SIM_2_HANDLE);
         mCallsManager.addCall(ongoingCall);
 
         List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
-                TEST_ADDRESS, null, false, false);
+                TEST_ADDRESS, null, false, false, false);
         assertEquals(1, phoneAccountHandles.size());
         assertEquals(SIM_2_HANDLE, phoneAccountHandles.get(0));
     }
 
     /**
+     * Verify behavior for multisim devices where we want to ensure that the active sim is used for
+     * placing a new call when a restriction is set as well as other call providers from different
+     * apps.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimActive_simulCallingRestriction() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsWithCallingRestriction(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE,
+                CALL_PROVIDER_HANDLE), Collections.emptySet());
+
+        Call ongoingCall = constructOngoingCall("1", SIM_2_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, false, false);
+        assertEquals(Arrays.asList(SIM_2_HANDLE, CALL_PROVIDER_HANDLE), phoneAccountHandles);
+    }
+
+    /**
+     * When we have 3 SIM PhoneAccounts on a device, but only 2 allow simultaneous calling, place a
+     * call on a SIM that allows simultaneous calling and verify that the subset of PhoneAccounts
+     * are available when in a call as well as the call provider.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimActive_simulCallingRestrictionSubset() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsWithCallingRestriction(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE, SIM_3_HANDLE,
+                CALL_PROVIDER_HANDLE), new ArraySet<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
+
+        Call ongoingCall = constructOngoingCall("1", SIM_2_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, false, false);
+        assertEquals(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE, CALL_PROVIDER_HANDLE),
+                phoneAccountHandles);
+    }
+
+    /**
+     * When we have 3 SIM PhoneAccounts on a device, but only 2 allow simultaneous calling, place a
+     * call on the SIM that does not allow simultaneous calling and verify  that only that SIM and
+     * the separate call provider are allowed to place a second call.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimActive_simulCallingRestrictionSubset2() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsWithCallingRestriction(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE, SIM_3_HANDLE,
+                CALL_PROVIDER_HANDLE), new ArraySet<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
+
+        Call ongoingCall = constructOngoingCall("1", SIM_3_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, false, false);
+        assertEquals(Arrays.asList(SIM_3_HANDLE, CALL_PROVIDER_HANDLE),
+                phoneAccountHandles);
+    }
+
+    /**
      * Verify behavior for multisim devices when there are no calls active; expect both accounts.
      * @throws Exception
      */
     @MediumTest
     @Test
     public void testConstructPossiblePhoneAccountsMultiSimIdle() throws Exception {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(false);
         setupMsimAccounts();
 
         List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
-                TEST_ADDRESS, null, false, false);
+                TEST_ADDRESS, null, false, false, false);
         assertEquals(2, phoneAccountHandles.size());
     }
 
     /**
+     * Verify behavior for multisim devices when there are no calls active and there are no calling
+     * restrictions set; expect both accounts.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimIdle_noSimulCallingRestriction() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsNoSimultaneousCallingRestriction();
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, false, false);
+        assertEquals(3, phoneAccountHandles.size());
+    }
+
+    /**
+     * Verify behavior for multisim devices when there are no calls active and there are no calling
+     * restrictions set; expect both accounts.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimIdle_withSimulCallingRestriction() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsWithCallingRestriction(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE,
+                CALL_PROVIDER_HANDLE), Collections.emptySet());
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, false, false);
+        assertEquals(3, phoneAccountHandles.size());
+    }
+
+    /**
      * For DSDA-enabled multisim devices with an ongoing call, verify that both SIMs'
      * PhoneAccountHandles are constructed while placing a new call.
      * @throws Exception
@@ -461,6 +593,7 @@
     @Test
     public void testConstructPossiblePhoneAccountsMultiSimActive_dsdaCallingPossible() throws
             Exception {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(false);
         setupMsimAccounts();
         setMaxActiveVoiceSubscriptions(2);
 
@@ -468,11 +601,29 @@
         mCallsManager.addCall(ongoingCall);
 
         List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
-                TEST_ADDRESS, null, false, false);
+                TEST_ADDRESS, null, false, false, false);
         assertEquals(2, phoneAccountHandles.size());
     }
 
     /**
+     * For multisim devices with an ongoing call, verify that all call capable PhoneAccounts are
+     * available when creating a second call.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimActive_simulCalling_dsdaPossible() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsNoSimultaneousCallingRestriction();
+
+        Call ongoingCall = constructOngoingCall("1", SIM_2_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, false, false);
+        assertEquals(3, phoneAccountHandles.size());
+    }
+
+    /**
      * For DSDA-enabled multisim devices with an ongoing call, verify that only the active SIMs'
      * PhoneAccountHandle is constructed while placing an emergency call.
      * @throws Exception
@@ -481,6 +632,7 @@
     @Test
     public void testConstructPossiblePhoneAccountsMultiSimActive_dsdaCallingPossible_emergencyCall()
             throws Exception {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(false);
         setupMsimAccounts();
         setMaxActiveVoiceSubscriptions(2);
 
@@ -488,11 +640,96 @@
         mCallsManager.addCall(ongoingCall);
 
         List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
-                TEST_ADDRESS, null, false, true /* isEmergency */);
+                TEST_ADDRESS, null, false, true /* isEmergency */, false);
         assertEquals(1, phoneAccountHandles.size());
         assertEquals(SIM_2_HANDLE, phoneAccountHandles.get(0));
     }
 
+    /**
+     * For multisim devices with an ongoing call, verify that only the active SIM's
+     * PhoneAccountHandle is available if we have a calling restriction where only one SIM is
+     * active at a time.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimActive_simulCalling_emergencyCall() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsWithCallingRestriction(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE,
+                CALL_PROVIDER_HANDLE), Collections.emptySet());
+
+        Call ongoingCall = constructOngoingCall("1", SIM_2_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, true /* isEmergency */, false);
+        assertEquals(2, phoneAccountHandles.size());
+        assertTrue("Candidate PAHs must contain the SIM account hosting the emergency call",
+                phoneAccountHandles.contains(SIM_2_HANDLE));
+        assertFalse("Candidate PAHs must not contain other SIM accounts",
+                phoneAccountHandles.contains(SIM_1_HANDLE));
+    }
+
+    /**
+     * For devices with an ongoing call on a call provider, verify that when an emergency
+     * call is placed, all SIM accounts are still available for SIM selection.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccounts_callProvider_emergencyCall() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsWithCallingRestriction(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE,
+                CALL_PROVIDER_HANDLE), Collections.emptySet());
+
+        Call ongoingCall = constructOngoingCall("1", CALL_PROVIDER_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, true /* isEmergency */, false);
+        assertEquals(3, phoneAccountHandles.size());
+    }
+
+    /**
+     * For multisim devices with an ongoing call, for backwards compatibility, only allow the
+     * SIM with the active call to be chosen to place an emergency call, even if there is no
+     * calling restriction.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimActive_simulCallingRest_emergencyCall() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsNoSimultaneousCallingRestriction();
+
+        Call ongoingCall = constructOngoingCall("1", SIM_2_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, true /* isEmergency */, false);
+        assertEquals(2, phoneAccountHandles.size());
+        assertTrue("Candidate PAHs must contain the SIM account hosting the emergency call",
+                phoneAccountHandles.contains(SIM_2_HANDLE));
+        assertFalse("Candidate PAHs must not contain other SIM accounts",
+                phoneAccountHandles.contains(SIM_1_HANDLE));
+    }
+
+    /**
+     * For multisim devices with an ongoing call on a call provider, it is still possible to place
+     * a SIM call on any SIM account.
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccounts_crossAccount_simulCalling() {
+        when(mTelephonyFlags.simultaneousCallingIndications()).thenReturn(true);
+        setupAccountsWithCallingRestriction(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE,
+                CALL_PROVIDER_HANDLE), Collections.emptySet());
+
+        Call ongoingCall = constructOngoingCall("1", CALL_PROVIDER_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, false /* isEmergency */, false);
+        assertEquals(3, phoneAccountHandles.size());
+    }
+
     private void setupCallerInfoLookupHelper() {
         doAnswer(invocation -> {
             Uri handle = invocation.getArgument(0);
@@ -3428,6 +3665,9 @@
         callback.onRequestFocusDone(call);
     }
 
+    /**
+     * Set up 2 SIM accounts in DSDS mode, where only one SIM can be active at a time for calls.
+     */
     private void setupMsimAccounts() {
         TelephonyManager mockTelephonyManager = mComponentContextFixture.getTelephonyManager();
         when(mockTelephonyManager.getMaxNumberOfSimultaneouslyActiveSims()).thenReturn(1);
@@ -3438,6 +3678,54 @@
                 new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
     }
 
+    /**
+     * Set up 2 SIM accounts in DSDS mode and one call provider, where there is no restriction on
+     * simultaneous calls across accounts.
+     */
+    private void setupAccountsNoSimultaneousCallingRestriction() {
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), anyInt(), anyInt(), anyBoolean())).thenReturn(
+                new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE, CALL_PROVIDER_HANDLE)));
+        when(mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser()).thenReturn(
+                new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
+    }
+
+    /**
+     * Set up the call capable PhoneAccounts passed in here to have simultaneous calling
+     * restrictions. If the callCapableHandle is a SIM account and it is in the restriction set, we
+     * will set that callCapableHandle's restriction to the Set. If not, we will set the restriction
+     * to allow no other simultaneous calls.
+     */
+    private void setupAccountsWithCallingRestriction(List<PhoneAccountHandle> callCapableHandles,
+            Set<PhoneAccountHandle> restrictions) {
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), anyInt(), anyInt(), anyBoolean())).thenReturn(
+                new ArrayList<>(callCapableHandles));
+        List<PhoneAccountHandle> allSims = Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE,
+                SIM_3_HANDLE);
+        List<PhoneAccountHandle> simsToTest = callCapableHandles.stream()
+                .filter(allSims::contains).toList();
+        when(mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser()).thenReturn(simsToTest);
+        // Remap the PhoneAccounts to inherit the restriction set
+        for (PhoneAccountHandle callCapableHandle : callCapableHandles) {
+            PhoneAccount pa = mPhoneAccountRegistrar.getPhoneAccount(
+                    callCapableHandle, callCapableHandle.getUserHandle());
+            assertNotNull("test setup error: could not find PA for PAH:" + callCapableHandle,
+                    pa);
+            // For simplicity, for testing only apply restrictions to SIM accounts
+            if (!allSims.contains(callCapableHandle)) continue;
+            if (restrictions.contains(callCapableHandle)) {
+                pa = new PhoneAccount.Builder(pa)
+                        .setSimultaneousCallingRestriction(restrictions).build();
+            } else {
+                pa = new PhoneAccount.Builder(pa)
+                        .setSimultaneousCallingRestriction(Collections.emptySet()).build();
+            }
+            when(mPhoneAccountRegistrar.getPhoneAccount(eq(callCapableHandle),
+                    any())).thenReturn(pa);
+        }
+    }
+
     private void setMaxActiveVoiceSubscriptions(int num) {
         TelephonyManager mockTelephonyManager = mComponentContextFixture.getTelephonyManager();
         when(mockTelephonyManager.getPhoneCapability()).thenReturn(mPhoneCapability);
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index b7de84f..57802e3 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -221,6 +221,8 @@
     CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker;
     @Mock
     FeatureFlags mFeatureFlags;
+    @Mock
+    com.android.internal.telephony.flags.FeatureFlags mTelephonyFlags;
 
     final ComponentName mInCallServiceComponentNameX =
             new ComponentName(
@@ -581,7 +583,8 @@
                 Runnable::run,
                 Runnable::run,
                 mBlockedNumbersAdapter,
-                mFeatureFlags);
+                mFeatureFlags,
+                mTelephonyFlags);
 
         mComponentContextFixture.setTelecomManager(new TelecomManager(
                 mComponentContextFixture.getTestDouble(),