Merge "Move getLastKnownCellIdentity to its own flag." into main
diff --git a/flags/telecom_api_flags.aconfig b/flags/telecom_api_flags.aconfig
index 8e1ab52..c0f4cba 100644
--- a/flags/telecom_api_flags.aconfig
+++ b/flags/telecom_api_flags.aconfig
@@ -48,6 +48,7 @@
namespace: "telecom"
description: "Enables enriched calling features (e.g. Business name will show for a call)"
bug: "311688497"
+ is_exported: true
}
flag{
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(),