Merge "Finalize multiendpoint functionality." into nyc-mr1-dev
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index aae9609..1139cc5 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -343,6 +343,7 @@
private final CallsManager mCallsManager;
private final TelecomSystem.SyncRoot mLock;
private final String mId;
+ private String mConnectionId;
private Analytics.CallInfo mAnalytics;
private boolean mWasConferencePreviouslyMerged = false;
@@ -379,6 +380,8 @@
*/
private boolean mIsVideoCallingSupported = false;
+ private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
+
/**
* Persists the specified parameters and initializes the new instance.
*
@@ -404,6 +407,7 @@
ConnectionServiceRepository repository,
ContactsAsyncHelper contactsAsyncHelper,
CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
+ PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
Uri handle,
GatewayInfo gatewayInfo,
PhoneAccountHandle connectionManagerPhoneAccountHandle,
@@ -412,11 +416,13 @@
boolean shouldAttachToExistingConnection,
boolean isConference) {
mId = callId;
+ mConnectionId = callId;
mState = isConference ? CallState.ACTIVE : CallState.NEW;
mContext = context;
mCallsManager = callsManager;
mLock = lock;
mRepository = repository;
+ mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
setHandle(handle);
mPostDialDigits = handle != null
? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
@@ -458,6 +464,7 @@
ConnectionServiceRepository repository,
ContactsAsyncHelper contactsAsyncHelper,
CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
+ PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
Uri handle,
GatewayInfo gatewayInfo,
PhoneAccountHandle connectionManagerPhoneAccountHandle,
@@ -467,7 +474,7 @@
boolean isConference,
long connectTimeMillis) {
this(callId, context, callsManager, lock, repository, contactsAsyncHelper,
- callerInfoAsyncQueryFactory, handle, gatewayInfo,
+ callerInfoAsyncQueryFactory, phoneNumberUtilsAdapter, handle, gatewayInfo,
connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
shouldAttachToExistingConnection, isConference);
@@ -595,6 +602,21 @@
}
/**
+ * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that
+ * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor.
+ * @return The call ID with an appended attempt id.
+ */
+ public String getConnectionId() {
+ if(mCreateConnectionProcessor != null) {
+ mConnectionId = mId + "_" +
+ String.valueOf(mCreateConnectionProcessor.getConnectionAttempt());
+ return mConnectionId;
+ } else {
+ return mConnectionId;
+ }
+ }
+
+ /**
* Sets the call state. Although there exists the notion of appropriate state transitions
* (see {@link CallState}), in practice those expectations break down when cellular systems
* misbehave and they do this very often. The result is that we do not enforce state transitions
@@ -750,8 +772,9 @@
// Let's not allow resetting of the emergency flag. Once a call becomes an emergency
// call, it will remain so for the rest of it's lifetime.
if (!mIsEmergencyCall) {
- mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
- mContext, mHandle.getSchemeSpecificPart());
+ mIsEmergencyCall = mHandle != null &&
+ mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(mContext,
+ mHandle.getSchemeSpecificPart());
}
startCallerInfoLookup();
for (Listener l : mListeners) {
@@ -1674,7 +1697,7 @@
return false;
}
- if (PhoneNumberUtils.isUriNumber(getHandle().toString())) {
+ if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) {
// The incoming number is actually a URI (i.e. a SIP address),
// not a regular PSTN phone number, and we can't send SMSes to
// SIP addresses.
diff --git a/src/com/android/server/telecom/CallIdMapper.java b/src/com/android/server/telecom/CallIdMapper.java
index b097bea..da2c199 100644
--- a/src/com/android/server/telecom/CallIdMapper.java
+++ b/src/com/android/server/telecom/CallIdMapper.java
@@ -77,7 +77,16 @@
}
}
+ public interface ICallInfo {
+ String getCallId(Call call);
+ }
+
private final BiMap<String, Call> mCalls = new BiMap<>();
+ private ICallInfo mCallInfo;
+
+ public CallIdMapper(ICallInfo callInfo) {
+ mCallInfo = callInfo;
+ }
void replaceCall(Call newCall, Call callToReplace) {
// Use the old call's ID for the new call.
@@ -93,7 +102,7 @@
}
void addCall(Call call) {
- addCall(call, call.getId());
+ addCall(call, mCallInfo.getCallId(call));
}
void removeCall(Call call) {
@@ -111,7 +120,7 @@
if (call == null || mCalls.getKey(call) == null) {
return null;
}
- return call.getId();
+ return mCallInfo.getCallId(call);
}
Call getCall(Object objId) {
diff --git a/src/com/android/server/telecom/CallIntentProcessor.java b/src/com/android/server/telecom/CallIntentProcessor.java
index ffa76ce..1219216 100644
--- a/src/com/android/server/telecom/CallIntentProcessor.java
+++ b/src/com/android/server/telecom/CallIntentProcessor.java
@@ -148,7 +148,7 @@
// process will be running throughout the duration of the phone call and should never
// be killed.
NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
- context, callsManager, call, intent, new PhoneNumberUtilsAdapterImpl(),
+ context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
isPrivilegedDialer);
final int result = broadcaster.processIntent();
final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 0b56357..d3b23c2 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -191,6 +191,7 @@
private final CallerInfoLookupHelper mCallerInfoLookupHelper;
private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
private final Timeouts.Adapter mTimeoutsAdapter;
+ private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
/* Handler tied to thread in which CallManager was initialized. */
@@ -221,9 +222,11 @@
SystemStateProvider systemStateProvider,
DefaultDialerManagerAdapter defaultDialerAdapter,
Timeouts.Adapter timeoutsAdapter,
- AsyncRingtonePlayer asyncRingtonePlayer) {
+ AsyncRingtonePlayer asyncRingtonePlayer,
+ PhoneNumberUtilsAdapter phoneNumberUtilsAdapter) {
mContext = context;
mLock = lock;
+ mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
mContactsAsyncHelper = contactsAsyncHelper;
mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
mPhoneAccountRegistrar = phoneAccountRegistrar;
@@ -669,6 +672,7 @@
mConnectionServiceRepository,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
+ mPhoneNumberUtilsAdapter,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
@@ -701,6 +705,7 @@
mConnectionServiceRepository,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
+ mPhoneNumberUtilsAdapter,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
@@ -773,6 +778,7 @@
mConnectionServiceRepository,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
+ mPhoneNumberUtilsAdapter,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
@@ -1549,6 +1555,11 @@
return getFirstCallWithState(null, states);
}
+ @VisibleForTesting
+ public PhoneNumberUtilsAdapter getPhoneNumberUtilsAdapter() {
+ return mPhoneNumberUtilsAdapter;
+ }
+
/**
* Returns the first call that it finds with the given states. The states are treated as having
* priority order so that any call with the first state will be returned before any call with
@@ -1606,6 +1617,7 @@
mConnectionServiceRepository,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
+ mPhoneNumberUtilsAdapter,
null /* handle */,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
@@ -1996,6 +2008,7 @@
mConnectionServiceRepository,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
+ mPhoneNumberUtilsAdapter,
connection.getHandle() /* handle */,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 5a58601..bf82a99 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -681,7 +681,7 @@
}
private final Adapter mAdapter = new Adapter();
- private final CallIdMapper mCallIdMapper = new CallIdMapper();
+ private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getConnectionId);
private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
private Binder2 mBinder = new Binder2();
@@ -731,6 +731,17 @@
}
}
+ /** See {@link IConnectionService#removeConnectionServiceAdapter}. */
+ private void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
+ if (isServiceValid("removeConnectionServiceAdapter")) {
+ try {
+ logOutgoing("removeConnectionServiceAdapter %s", adapter);
+ mServiceInterface.removeConnectionServiceAdapter(adapter);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
/**
* Creates a new connection for a new outgoing call or to attach to an existing incoming call.
*/
@@ -1050,18 +1061,23 @@
/** {@inheritDoc} */
@Override
protected void setServiceInterface(IBinder binder) {
- if (binder == null) {
- // We have lost our service connection. Notify the world that this service is done.
- // We must notify the adapter before CallsManager. The adapter will force any pending
- // outgoing calls to try the next service. This needs to happen before CallsManager
- // tries to clean up any calls still associated with this service.
- handleConnectionServiceDeath();
- mCallsManager.handleConnectionServiceDeath(this);
- mServiceInterface = null;
- } else {
- mServiceInterface = IConnectionService.Stub.asInterface(binder);
- addConnectionServiceAdapter(mAdapter);
- }
+ mServiceInterface = IConnectionService.Stub.asInterface(binder);
+ Log.v(this, "Adding Connection Service Adapter.");
+ addConnectionServiceAdapter(mAdapter);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void removeServiceInterface() {
+ Log.v(this, "Removing Connection Service Adapter.");
+ removeConnectionServiceAdapter(mAdapter);
+ // We have lost our service connection. Notify the world that this service is done.
+ // We must notify the adapter before CallsManager. The adapter will force any pending
+ // outgoing calls to try the next service. This needs to happen before CallsManager
+ // tries to clean up any calls still associated with this service.
+ handleConnectionServiceDeath();
+ mCallsManager.handleConnectionServiceDeath(this);
+ mServiceInterface = null;
}
private void handleCreateConnectionComplete(
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index c89a896..22ec3c6 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -96,6 +96,7 @@
private final Context mContext;
private CreateConnectionTimeout mTimeout;
private ConnectionServiceWrapper mService;
+ private int mConnectionAttempt;
@VisibleForTesting
public CreateConnectionProcessor(
@@ -107,6 +108,7 @@
mCallResponse = response;
mPhoneAccountRegistrar = phoneAccountRegistrar;
mContext = context;
+ mConnectionAttempt = 0;
}
boolean isProcessingComplete() {
@@ -117,6 +119,10 @@
return mTimeout != null && mTimeout.isCallTimedOut();
}
+ public int getConnectionAttempt() {
+ return mConnectionAttempt;
+ }
+
@VisibleForTesting
public void process() {
Log.v(this, "process");
@@ -200,6 +206,7 @@
Log.i(this, "Found no connection service for attempt %s", attempt);
attemptNextPhoneAccount();
} else {
+ mConnectionAttempt++;
mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
mCall.setConnectionService(mService);
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index c6f42d2..1432373 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -589,7 +589,7 @@
*/
private ComponentName mInCallUIComponentName;
- private final CallIdMapper mCallIdMapper = new CallIdMapper();
+ private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getId);
/** The {@link ComponentName} of the default InCall UI. */
private final ComponentName mSystemInCallComponentName;
diff --git a/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java b/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
index 41284cb..8e59a64 100644
--- a/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
+++ b/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
@@ -24,6 +24,7 @@
* refactoring.
*/
public interface PhoneNumberUtilsAdapter {
+ boolean isLocalEmergencyNumber(Context context, String number);
boolean isPotentialLocalEmergencyNumber(Context context, String number);
boolean isUriNumber(String number);
String getNumberFromIntent(Intent intent, Context context);
diff --git a/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
index 640d814..fa316a5 100644
--- a/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
+++ b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
@@ -22,6 +22,11 @@
public class PhoneNumberUtilsAdapterImpl implements PhoneNumberUtilsAdapter {
@Override
+ public boolean isLocalEmergencyNumber(Context context, String number) {
+ return PhoneNumberUtils.isLocalEmergencyNumber(context, number);
+ }
+
+ @Override
public boolean isPotentialLocalEmergencyNumber(Context context, String number) {
return PhoneNumberUtils.isPotentialLocalEmergencyNumber(context, number);
}
diff --git a/src/com/android/server/telecom/ServiceBinder.java b/src/com/android/server/telecom/ServiceBinder.java
index 18d6581..9a0f7b4 100644
--- a/src/com/android/server/telecom/ServiceBinder.java
+++ b/src/com/android/server/telecom/ServiceBinder.java
@@ -336,22 +336,28 @@
*/
private void setBinder(IBinder binder) {
if (mBinder != binder) {
- mBinder = binder;
-
- setServiceInterface(binder);
-
if (binder == null) {
+ removeServiceInterface();
+ mBinder = null;
for (Listener l : mListeners) {
l.onUnbind(this);
}
+ } else {
+ mBinder = binder;
+ setServiceInterface(binder);
}
}
}
/**
- * Sets the service interface after the service is bound or unbound.
+ * Sets the service interface after the service is bound.
*
- * @param binder The actual bound service implementation.
+ * @param binder The new binder interface that is being set.
*/
protected abstract void setServiceInterface(IBinder binder);
+
+ /**
+ * Removes the service interface before the service is unbound.
+ */
+ protected abstract void removeServiceInterface();
}
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 304f13b..67321d4 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -154,7 +154,8 @@
BluetoothPhoneServiceImplFactory
bluetoothPhoneServiceImplFactory,
Timeouts.Adapter timeoutsAdapter,
- AsyncRingtonePlayer asyncRingtonePlayer) {
+ AsyncRingtonePlayer asyncRingtonePlayer,
+ PhoneNumberUtilsAdapter phoneNumberUtilsAdapter) {
mContext = context.getApplicationContext();
Log.setContext(mContext);
Log.initMd5Sum();
@@ -196,7 +197,8 @@
systemStateProvider,
defaultDialerAdapter,
timeoutsAdapter,
- asyncRingtonePlayer);
+ asyncRingtonePlayer,
+ phoneNumberUtilsAdapter);
mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index bef8453..a5790fd 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -36,6 +36,7 @@
import com.android.server.telecom.InCallWakeLockControllerFactory;
import com.android.server.telecom.CallAudioManager;
import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
import com.android.server.telecom.ProximitySensorManagerFactory;
import com.android.server.telecom.InCallWakeLockController;
import com.android.server.telecom.Log;
@@ -148,7 +149,8 @@
}
},
new Timeouts.Adapter(),
- new AsyncRingtonePlayer()
+ new AsyncRingtonePlayer(),
+ new PhoneNumberUtilsAdapterImpl()
));
}
if (BluetoothAdapter.getDefaultAdapter() != null) {
diff --git a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
index f9d2793..1e97695 100644
--- a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
+++ b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
@@ -600,7 +600,7 @@
// Convert the data to a call object
Call call = new Call(Call.CALL_ID_UNKNOWN, mContext, callsManager,
lock, null, contactsAsyncHelper,
- callerInfoAsyncQueryFactory, null, null, null, null,
+ callerInfoAsyncQueryFactory, null, null, null, null, null,
Call.CALL_DIRECTION_INCOMING, false, false);
call.setDisconnectCause(
new DisconnectCause(DisconnectCause.MISSED));
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 9211d05..ceec91b 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -122,7 +122,7 @@
telecomManager.acceptRingingCall();
verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
- .answer(ids.mCallId);
+ .answer(ids.mConnectionId);
mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
@@ -150,7 +150,7 @@
// Answer video API should be called
verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
- .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
+ .answerVideo(eq(ids.mConnectionId), eq(VideoProfile.STATE_BIDIRECTIONAL));
mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
@@ -177,7 +177,7 @@
// The generic answer method on the ConnectionService is used to answer audio-only calls.
verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
- .answer(eq(ids.mCallId));
+ .answer(eq(ids.mConnectionId));
mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
@@ -205,7 +205,7 @@
// Answer video API should be called
verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
- .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
+ .answerVideo(eq(ids.mConnectionId), eq(VideoProfile.STATE_BIDIRECTIONAL));
mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
}
@@ -643,7 +643,7 @@
mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT, null);
verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
- .sendCallEvent(ids.mCallId, TEST_EVENT, null);
+ .sendCallEvent(ids.mConnectionId, TEST_EVENT, null);
}
/**
@@ -664,7 +664,7 @@
mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT,
testBundle);
verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
- .sendCallEvent(eq(ids.mCallId), eq(TEST_EVENT),
+ .sendCallEvent(eq(ids.mConnectionId), eq(TEST_EVENT),
bundleArgumentCaptor.capture());
assert (bundleArgumentCaptor.getValue().containsKey(TEST_BUNDLE_KEY));
}
@@ -762,7 +762,7 @@
// Attempt to pull the call and verify the API call makes it through
mInCallServiceFixtureX.mInCallAdapter.pullExternalCall(ids.mCallId);
verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
- .pullExternalCall(ids.mCallId);
+ .pullExternalCall(ids.mConnectionId);
}
/**
@@ -786,7 +786,7 @@
// Attempt to pull the call and verify the API call makes it through
mInCallServiceFixtureX.mInCallAdapter.pullExternalCall(ids.mCallId);
verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT).never())
- .pullExternalCall(ids.mCallId);
+ .pullExternalCall(ids.mConnectionId);
}
public void testMergeFailedAndNotifyInCallUi() throws Exception {
@@ -818,4 +818,24 @@
verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT)).onConnectionEvent(
eq(testCall2.mCallId), eq(Connection.EVENT_CALL_MERGE_FAILED), any(Bundle.class));
}
+
+ @LargeTest
+ public void testEmergencyCallFailMoveToSecondSim() throws Exception {
+ IdPair ids = startAndMakeDialingEmergencyCall("650-555-1212",
+ mPhoneAccountE0.getAccountHandle(), mConnectionServiceFixtureA);
+ assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+ assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+ // The Emergency Call has failed on the default SIM with an ERROR Disconnect Cause. Retry
+ // with the other SIM PhoneAccount
+ IdPair newIds = triggerEmergencyRedial(mPhoneAccountE1.getAccountHandle(),
+ mConnectionServiceFixtureA, ids);
+
+ // Call should be active on the E1 PhoneAccount
+ mConnectionServiceFixtureA.sendSetActive(newIds.mConnectionId);
+ assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(newIds.mCallId).getState());
+ assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(newIds.mCallId).getState());
+ assertEquals(mInCallServiceFixtureX.getCall(ids.mCallId).getAccountHandle(),
+ mPhoneAccountE1.getAccountHandle());
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index fba4fb0..5ed941e 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -519,6 +519,9 @@
for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = mServiceInfoByComponentName.get(componentName);
+ resolveInfo.serviceInfo.metaData = new Bundle();
+ resolveInfo.serviceInfo.metaData.putBoolean(
+ TelecomManager.METADATA_INCLUDE_EXTERNAL_CALLS, true);
result.add(resolveInfo);
}
return result;
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index 03ec2b2..962ab10 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -147,7 +147,7 @@
capabilities |= CAPABILITY_HOLD;
setVideoState(videoState);
setConnectionCapabilities(capabilities);
- setActive();
+ setDialing();
setAddress(address, TelecomManager.PRESENTATION_ALLOWED);
}
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 7081aed..0173ed8 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -90,7 +90,7 @@
private UserHandle mUserHandle = UserHandle.of(CURRENT_USER_ID);
private InCallController mInCallController;
- private TelecomSystem.SyncRoot mLock;
+ private TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {};
@Override
public void setUp() throws Exception {
@@ -386,36 +386,6 @@
assertEquals(DEF_CLASS, bindIntent.getComponent().getClassName());
}
- /**
- * Ensures that the {@link InCallController} will not bind to an incall service for an external
- * call if the incall service does not support it.
- */
- @MediumTest
- public void testBindToService_ExcludeExternal() throws Exception {
- setupMocks(true /* isExternalCall */);
- setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
- mInCallController.bindToServices(mMockCall);
-
- // Query for the different InCallServices
- ArgumentCaptor<Intent> queryIntentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
- queryIntentCaptor.capture(),
- eq(PackageManager.GET_META_DATA), eq(CURRENT_USER_ID));
-
- // Verify call for default dialer InCallService
- assertEquals(DEF_PKG, queryIntentCaptor.getAllValues().get(0).getPackage());
- // Verify call for car-mode InCallService
- assertEquals(null, queryIntentCaptor.getAllValues().get(1).getPackage());
- // Verify call for non-UI InCallServices
- assertEquals(null, queryIntentCaptor.getAllValues().get(2).getPackage());
-
- verify(mMockContext, never()).bindServiceAsUser(
- any(Intent.class),
- any(ServiceConnection.class),
- eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
- eq(UserHandle.CURRENT));
- }
-
private void setupMocks(boolean isExternalCall) {
when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index fc8e1a5..9c47d3f 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -50,6 +50,7 @@
import android.provider.BlockedNumberContract;
import android.telecom.Call;
import android.telecom.ConnectionRequest;
+import android.telecom.DisconnectCause;
import android.telecom.ParcelableCall;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
@@ -70,6 +71,8 @@
import com.android.server.telecom.InCallWakeLockControllerFactory;
import com.android.server.telecom.MissedCallNotifier;
import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.PhoneNumberUtilsAdapter;
+import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
import com.android.server.telecom.ProximitySensorManager;
import com.android.server.telecom.ProximitySensorManagerFactory;
import com.android.server.telecom.TelecomSystem;
@@ -145,6 +148,19 @@
}
MissedCallNotifierFakeImpl mMissedCallNotifier = new MissedCallNotifierFakeImpl();
+ private class EmergencyNumberUtilsAdapter extends PhoneNumberUtilsAdapterImpl {
+
+ @Override
+ public boolean isLocalEmergencyNumber(Context context, String number) {
+ return mIsEmergencyCall;
+ }
+
+ @Override
+ public boolean isPotentialLocalEmergencyNumber(Context context, String number) {
+ return mIsEmergencyCall;
+ }
+ }
+ PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter = new EmergencyNumberUtilsAdapter();
@Mock HeadsetMediaButton mHeadsetMediaButton;
@Mock ProximitySensorManager mProximitySensorManager;
@Mock InCallWakeLockController mInCallWakeLockController;
@@ -205,6 +221,31 @@
PhoneAccount.CAPABILITY_CALL_PROVIDER |
PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
.build();
+ final PhoneAccount mPhoneAccountE0 =
+ PhoneAccount.builder(
+ new PhoneAccountHandle(
+ mConnectionServiceComponentNameA,
+ "id E 0"),
+ "Phone account service E ID 0")
+ .addSupportedUriScheme("tel")
+ .setCapabilities(
+ PhoneAccount.CAPABILITY_CALL_PROVIDER |
+ PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
+ PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
+ .build();
+
+ final PhoneAccount mPhoneAccountE1 =
+ PhoneAccount.builder(
+ new PhoneAccountHandle(
+ mConnectionServiceComponentNameA,
+ "id E 1"),
+ "Phone account service E ID 1")
+ .addSupportedUriScheme("tel")
+ .setCapabilities(
+ PhoneAccount.CAPABILITY_CALL_PROVIDER |
+ PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
+ PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
+ .build();
ConnectionServiceFixture mConnectionServiceFixtureA;
ConnectionServiceFixture mConnectionServiceFixtureB;
@@ -220,6 +261,8 @@
private int mNumOutgoingCallsMade;
+ private boolean mIsEmergencyCall;
+
class IdPair {
final String mConnectionId;
final String mCallId;
@@ -238,6 +281,8 @@
mNumOutgoingCallsMade = 0;
+ mIsEmergencyCall = false;
+
// First set up information about the In-Call services in the mock Context, since
// Telecom will search for these as soon as it is instantiated
setupInCallServices();
@@ -327,7 +372,8 @@
}
},
mTimeoutsAdapter,
- mAsyncRingtonePlayer);
+ mAsyncRingtonePlayer,
+ mPhoneNumberUtilsAdapter);
mComponentContextFixture.setTelecomManager(new TelecomManager(
mComponentContextFixture.getTestDouble(),
@@ -357,6 +403,8 @@
mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
+ mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE0);
+ mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE1);
mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
mPhoneAccountA0.getAccountHandle(), Process.myUserHandle());
@@ -460,10 +508,46 @@
phoneAccountHandle, connectionServiceFixture);
}
- protected String startOutgoingPhoneCallPendingCreateConnection(String number,
+ protected IdPair triggerEmergencyRedial(PhoneAccountHandle phoneAccountHandle,
+ ConnectionServiceFixture connectionServiceFixture, IdPair emergencyIds)
+ throws Exception {
+ int startingNumConnections = connectionServiceFixture.mConnectionById.size();
+ int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
+
+ // Send the message to disconnect the Emergency call due to an error.
+ // CreateConnectionProcessor should now try the second SIM account
+ connectionServiceFixture.sendSetDisconnected(emergencyIds.mConnectionId,
+ DisconnectCause.ERROR);
+ waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+ assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(
+ emergencyIds.mCallId).getState());
+ assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(
+ emergencyIds.mCallId).getState());
+
+ return redialingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
+ phoneAccountHandle, connectionServiceFixture);
+ }
+
+ protected IdPair startOutgoingEmergencyCall(String number,
PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
int videoState) throws Exception {
+ int startingNumConnections = connectionServiceFixture.mConnectionById.size();
+ int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
+
+ mIsEmergencyCall = true;
+ // Call will not use the ordered broadcaster, since it is an Emergency Call
+ startOutgoingPhoneCallWaitForBroadcaster(number, phoneAccountHandle,
+ connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/);
+
+ return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
+ phoneAccountHandle, connectionServiceFixture);
+ }
+
+ protected void startOutgoingPhoneCallWaitForBroadcaster(String number,
+ PhoneAccountHandle phoneAccountHandle,
+ ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
+ int videoState, boolean isEmergency) throws Exception {
reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
mInCallServiceFixtureY.getTestDouble());
@@ -479,7 +563,11 @@
Intent actionCallIntent = new Intent();
actionCallIntent.setData(Uri.parse("tel:" + number));
actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
- actionCallIntent.setAction(Intent.ACTION_CALL);
+ if(isEmergency) {
+ actionCallIntent.setAction(Intent.ACTION_CALL_EMERGENCY);
+ } else {
+ actionCallIntent.setAction(Intent.ACTION_CALL);
+ }
if (phoneAccountHandle != null) {
actionCallIntent.putExtra(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
@@ -507,6 +595,14 @@
.setInCallAdapter(
any(IInCallAdapter.class));
}
+ }
+
+ protected String startOutgoingPhoneCallPendingCreateConnection(String number,
+ PhoneAccountHandle phoneAccountHandle,
+ ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
+ int videoState) throws Exception {
+ startOutgoingPhoneCallWaitForBroadcaster(number,phoneAccountHandle,
+ connectionServiceFixture, initiatingUser, videoState, false /*isEmergency*/);
ArgumentCaptor<Intent> newOutgoingCallIntent =
ArgumentCaptor.forClass(Intent.class);
@@ -538,6 +634,30 @@
return mInCallServiceFixtureX.mLatestCallId;
}
+ // When Telecom is redialing due to an error, we need to make sure the number of connections
+ // increase, but not the number of Calls in the InCallService.
+ protected IdPair redialingCallCreateConnectionComplete(int startingNumConnections,
+ int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
+ ConnectionServiceFixture connectionServiceFixture) throws Exception {
+
+ assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
+
+ verify(connectionServiceFixture.getTestDouble())
+ .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class),
+ eq(false)/*isIncoming*/, anyBoolean());
+ // Wait for handleCreateConnectionComplete
+ waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+
+ // Make sure the number of registered InCallService Calls stays the same.
+ assertEquals(startingNumCalls, mInCallServiceFixtureX.mCallById.size());
+ assertEquals(startingNumCalls, mInCallServiceFixtureY.mCallById.size());
+
+ assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
+
+ return new IdPair(connectionServiceFixture.mLatestConnectionId,
+ mInCallServiceFixtureX.mLatestCallId);
+ }
+
protected IdPair outgoingCallCreateConnectionComplete(int startingNumConnections,
int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
ConnectionServiceFixture connectionServiceFixture) throws Exception {
@@ -736,6 +856,20 @@
return ids;
}
+ protected IdPair startAndMakeDialingEmergencyCall(
+ String number,
+ PhoneAccountHandle phoneAccountHandle,
+ ConnectionServiceFixture connectionServiceFixture) throws Exception {
+ IdPair ids = startOutgoingEmergencyCall(number, phoneAccountHandle,
+ connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
+
+ connectionServiceFixture.sendSetDialing(ids.mConnectionId);
+ assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+ assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+ return ids;
+ }
+
protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
int elapsed = 0;
while (elapsed < TEST_TIMEOUT) {