Fix NPE in TelecomManager#isOutgoingCallAllowed
There is a potential for a NPE if "excludeCall" is null. Adding null
check and unit tests to verify operation of the isOutgoingCallAllowed API,
as well as basic outgoing self-managed calls (which rely on this API).
Test: Added unit tests.
Change-Id: Ia5e92a7cb418af0d131daa2ca88b7cd4d36ab057
Fixes: 67495237
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 10c1fc3..7485841 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -2948,10 +2948,10 @@
// Only permit outgoing calls if there is no ongoing emergency calls and all other calls
// are associated with the current PhoneAccountHandle.
return !hasEmergencyCall() && (
- excludeCall.getHandoverSourceCall() != null ||
- (!hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) &&
- !hasCallsForOtherPhoneAccount(phoneAccountHandle) &&
- !hasManagedCalls()));
+ (excludeCall != null && excludeCall.getHandoverSourceCall() != null) || (
+ !hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle)
+ && !hasCallsForOtherPhoneAccount(phoneAccountHandle)
+ && !hasManagedCalls()));
}
}
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 32ac1ac..f391ee6 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -900,4 +900,59 @@
assert(!call.isVideoCallingSupported());
assertEquals(VideoProfile.STATE_AUDIO_ONLY, call.getVideoState());
}
+
+ /**
+ * Basic test to ensure that a self-managed ConnectionService can place a call.
+ * @throws Exception
+ */
+ @LargeTest
+ public void testSelfManagedOutgoing() throws Exception {
+ PhoneAccountHandle phoneAccountHandle = mPhoneAccountSelfManaged.getAccountHandle();
+ IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212", phoneAccountHandle,
+ mConnectionServiceFixtureA);
+
+ // The InCallService should not know about the call since its self-managed.
+ assertNull(mInCallServiceFixtureX.getCall(ids.mCallId));
+ }
+
+ /**
+ * Basic test to ensure that a self-managed ConnectionService can add an incoming call.
+ * @throws Exception
+ */
+ @LargeTest
+ public void testSelfManagedIncoming() throws Exception {
+ PhoneAccountHandle phoneAccountHandle = mPhoneAccountSelfManaged.getAccountHandle();
+ IdPair ids = startAndMakeActiveIncomingCall("650-555-1212", phoneAccountHandle,
+ mConnectionServiceFixtureA);
+
+ // The InCallService should not know about the call since its self-managed.
+ assertNull(mInCallServiceFixtureX.getCall(ids.mCallId));
+ }
+
+ /**
+ * Basic test to ensure that when there are no calls, we permit outgoing calls by a self managed
+ * CS.
+ * @throws Exception
+ */
+ @LargeTest
+ public void testIsOutgoingCallPermitted() throws Exception {
+ assertTrue(mTelecomSystem.getTelecomServiceImpl().getBinder()
+ .isOutgoingCallPermitted(mPhoneAccountSelfManaged.getAccountHandle()));
+ }
+
+ /**
+ * Basic test to ensure that when there are other calls, we do not permit outgoing calls by a
+ * self managed CS.
+ * @throws Exception
+ */
+ @LargeTest
+ public void testIsOutgoingCallPermittedOngoing() throws Exception {
+ // Start a regular call; the self-managed CS can't make a call now.
+ IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
+ mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+ assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+
+ assertFalse(mTelecomSystem.getTelecomServiceImpl().getBinder()
+ .isOutgoingCallPermitted(mPhoneAccountSelfManaged.getAccountHandle()));
+ }
}
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 7f81f05..475b550 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -260,6 +260,16 @@
PhoneAccount.CAPABILITY_CALL_PROVIDER |
PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
.build();
+ final PhoneAccount mPhoneAccountSelfManaged =
+ PhoneAccount.builder(
+ new PhoneAccountHandle(
+ mConnectionServiceComponentNameA,
+ "id SM"),
+ "Phone account service A SM")
+ .addSupportedUriScheme("tel")
+ .setCapabilities(
+ PhoneAccount.CAPABILITY_SELF_MANAGED)
+ .build();
final PhoneAccount mPhoneAccountB0 =
PhoneAccount.builder(
new PhoneAccountHandle(
@@ -468,6 +478,7 @@
mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA2);
+ mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountSelfManaged);
mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE0);
mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE1);
@@ -663,7 +674,8 @@
mCallerInfoAsyncQueryFactoryFixture.mRequests.forEach(
CallerInfoAsyncQueryFactoryFixture.Request::reply);
- if (!hasInCallAdapter) {
+ boolean isSelfManaged = phoneAccountHandle == mPhoneAccountSelfManaged.getAccountHandle();
+ if (!hasInCallAdapter && !isSelfManaged) {
verify(mInCallServiceFixtureX.getTestDouble())
.setInCallAdapter(
any(IInCallAdapter.class));
@@ -685,27 +697,28 @@
ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mComponentContextFixture.getTestDouble().getApplicationContext(),
- times(mNumOutgoingCallsMade))
- .sendOrderedBroadcastAsUser(
- newOutgoingCallIntent.capture(),
- any(UserHandle.class),
- anyString(),
- anyInt(),
- newOutgoingCallReceiver.capture(),
- nullable(Handler.class),
- anyInt(),
- anyString(),
- nullable(Bundle.class));
-
- // Pass on the new outgoing call Intent
- // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
- newOutgoingCallReceiver.getValue().setPendingResult(
- new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
- newOutgoingCallReceiver.getValue().setResultData(
- newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER));
- newOutgoingCallReceiver.getValue().onReceive(mComponentContextFixture.getTestDouble(),
- newOutgoingCallIntent.getValue());
+ if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
+ verify(mComponentContextFixture.getTestDouble().getApplicationContext(),
+ times(mNumOutgoingCallsMade))
+ .sendOrderedBroadcastAsUser(
+ newOutgoingCallIntent.capture(),
+ any(UserHandle.class),
+ anyString(),
+ anyInt(),
+ newOutgoingCallReceiver.capture(),
+ nullable(Handler.class),
+ anyInt(),
+ anyString(),
+ nullable(Bundle.class));
+ // Pass on the new outgoing call Intent
+ // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
+ newOutgoingCallReceiver.getValue().setPendingResult(
+ new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
+ newOutgoingCallReceiver.getValue().setResultData(
+ newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+ newOutgoingCallReceiver.getValue().onReceive(mComponentContextFixture.getTestDouble(),
+ newOutgoingCallIntent.getValue());
+ }
return mInCallServiceFixtureX.mLatestCallId;
}
@@ -752,8 +765,13 @@
verify(connectionServiceFixture.getTestDouble(), timeout(TEST_TIMEOUT))
.createConnectionComplete(anyString(), any());
- assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
- assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
+ if (phoneAccountHandle == mPhoneAccountSelfManaged.getAccountHandle()) {
+ assertEquals(startingNumCalls, mInCallServiceFixtureX.mCallById.size());
+ assertEquals(startingNumCalls, mInCallServiceFixtureY.mCallById.size());
+ } else {
+ assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
+ assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
+ }
assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
@@ -829,58 +847,59 @@
// is added, future interactions as triggered by the ConnectionService, through the various
// test fixtures, will be synchronous.
- if (!hasInCallAdapter) {
+ if (!hasInCallAdapter
+ && phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
.setInCallAdapter(any(IInCallAdapter.class));
verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
.setInCallAdapter(any(IInCallAdapter.class));
+
+ // Give the InCallService time to respond
+ assertTrueWithTimeout(new Predicate<Void>() {
+ @Override
+ public boolean apply(Void v) {
+ return mInCallServiceFixtureX.mInCallAdapter != null;
+ }
+ });
+
+ assertTrueWithTimeout(new Predicate<Void>() {
+ @Override
+ public boolean apply(Void v) {
+ return mInCallServiceFixtureY.mInCallAdapter != null;
+ }
+ });
+
+ verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
+ .addCall(any(ParcelableCall.class));
+ verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
+ .addCall(any(ParcelableCall.class));
+
+ // Give the InCallService time to respond
+
+ assertTrueWithTimeout(new Predicate<Void>() {
+ @Override
+ public boolean apply(Void v) {
+ return startingNumConnections + 1 ==
+ connectionServiceFixture.mConnectionById.size();
+ }
+ });
+ assertTrueWithTimeout(new Predicate<Void>() {
+ @Override
+ public boolean apply(Void v) {
+ return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
+ }
+ });
+ assertTrueWithTimeout(new Predicate<Void>() {
+ @Override
+ public boolean apply(Void v) {
+ return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
+ }
+ });
+
+ assertEquals(mInCallServiceFixtureX.mLatestCallId,
+ mInCallServiceFixtureY.mLatestCallId);
}
- // Give the InCallService time to respond
-
- assertTrueWithTimeout(new Predicate<Void>() {
- @Override
- public boolean apply(Void v) {
- return mInCallServiceFixtureX.mInCallAdapter != null;
- }
- });
-
- assertTrueWithTimeout(new Predicate<Void>() {
- @Override
- public boolean apply(Void v) {
- return mInCallServiceFixtureY.mInCallAdapter != null;
- }
- });
-
- verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
- .addCall(any(ParcelableCall.class));
- verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
- .addCall(any(ParcelableCall.class));
-
- // Give the InCallService time to respond
-
- assertTrueWithTimeout(new Predicate<Void>() {
- @Override
- public boolean apply(Void v) {
- return startingNumConnections + 1 ==
- connectionServiceFixture.mConnectionById.size();
- }
- });
- assertTrueWithTimeout(new Predicate<Void>() {
- @Override
- public boolean apply(Void v) {
- return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
- }
- });
- assertTrueWithTimeout(new Predicate<Void>() {
- @Override
- public boolean apply(Void v) {
- return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
- }
- });
-
- assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
-
return new IdPair(connectionServiceFixture.mLatestConnectionId,
mInCallServiceFixtureX.mLatestCallId);
}
@@ -903,17 +922,22 @@
Process.myUserHandle(), videoState);
connectionServiceFixture.sendSetDialing(ids.mConnectionId);
- assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
- assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+ if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
+ assertEquals(Call.STATE_DIALING,
+ mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+ assertEquals(Call.STATE_DIALING,
+ mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+ }
connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
when(mClockProxy.currentTimeMillis()).thenReturn(TEST_CONNECT_TIME);
when(mClockProxy.elapsedRealtime()).thenReturn(TEST_CONNECT_ELAPSED_TIME);
connectionServiceFixture.sendSetActive(ids.mConnectionId);
- assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
- assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
-
+ if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
+ assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+ assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+ }
return ids;
}
@@ -933,26 +957,32 @@
int videoState) throws Exception {
IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
- assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
- assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+ if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
+ assertEquals(Call.STATE_RINGING,
+ mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+ assertEquals(Call.STATE_RINGING,
+ mInCallServiceFixtureY.getCall(ids.mCallId).getState());
- mInCallServiceFixtureX.mInCallAdapter
- .answerCall(ids.mCallId, videoState);
+ mInCallServiceFixtureX.mInCallAdapter
+ .answerCall(ids.mCallId, videoState);
- if (!VideoProfile.isVideo(videoState)) {
- verify(connectionServiceFixture.getTestDouble())
- .answer(eq(ids.mConnectionId), any());
- } else {
- verify(connectionServiceFixture.getTestDouble())
- .answerVideo(eq(ids.mConnectionId), eq(videoState), any());
+ if (!VideoProfile.isVideo(videoState)) {
+ verify(connectionServiceFixture.getTestDouble())
+ .answer(eq(ids.mConnectionId), any());
+ } else {
+ verify(connectionServiceFixture.getTestDouble())
+ .answerVideo(eq(ids.mConnectionId), eq(videoState), any());
+ }
}
when(mClockProxy.currentTimeMillis()).thenReturn(TEST_CONNECT_TIME);
when(mClockProxy.elapsedRealtime()).thenReturn(TEST_CONNECT_ELAPSED_TIME);
connectionServiceFixture.sendSetActive(ids.mConnectionId);
- assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
- assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+ if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
+ assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+ assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+ }
return ids;
}