Merge "Add logging for some missing CallState."
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 75feeb6..02bd056 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -15,6 +15,14 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsTelecomTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 3497601..cc1607e 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -543,7 +543,7 @@
private ParcelFileDescriptor[] mConnectionServiceToInCallStreams;
/**
- * True if we're supposed to start this call with RTT, either due to the master switch or due
+ * True if we're supposed to start this call with RTT, either due to the settings switch or due
* to an extra.
*/
private boolean mDidRequestToStartWithRtt = false;
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index a562021..28f9df9 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -459,6 +459,8 @@
switch (msg.what) {
case SWITCH_EARPIECE:
case USER_SWITCH_EARPIECE:
+ case SPEAKER_ON:
+ // Ignore speakerphone state changes outside of calls.
case SPEAKER_OFF:
// Nothing to do here
return HANDLED;
@@ -484,7 +486,6 @@
return HANDLED;
case SWITCH_SPEAKER:
case USER_SWITCH_SPEAKER:
- case SPEAKER_ON:
transitionTo(mQuiescentSpeakerRoute);
return HANDLED;
case SWITCH_FOCUS:
@@ -677,12 +678,13 @@
return HANDLED;
case SWITCH_HEADSET:
case USER_SWITCH_HEADSET:
+ case SPEAKER_ON:
+ // Ignore speakerphone state changes outside of calls.
case SPEAKER_OFF:
// Nothing to do
return HANDLED;
case SWITCH_SPEAKER:
case USER_SWITCH_SPEAKER:
- case SPEAKER_ON:
transitionTo(mQuiescentSpeakerRoute);
return HANDLED;
case SWITCH_FOCUS:
@@ -1012,6 +1014,8 @@
return HANDLED;
case SWITCH_BLUETOOTH:
case USER_SWITCH_BLUETOOTH:
+ case SPEAKER_ON:
+ // Ignore speakerphone state changes outside of calls.
case SPEAKER_OFF:
// Nothing to do
return HANDLED;
@@ -1025,7 +1029,6 @@
return HANDLED;
case SWITCH_SPEAKER:
case USER_SWITCH_SPEAKER:
- case SPEAKER_ON:
transitionTo(mQuiescentSpeakerRoute);
return HANDLED;
case SWITCH_FOCUS:
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 7a5af14..b19e269 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -605,7 +605,8 @@
for (int i = 0; i < result.length; i++) {
Uri uri = result[i];
/*
- Performs a simple sanity check to make sure the call was written in the database.
+ Performs a simple correctness check to make sure the call was written in the
+ database.
Typically there is only one result per call so it is easy to identify which one
failed.
*/
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index e4048b1..f060a0d 100755
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -3924,7 +3924,7 @@
+ " livecall = " + liveCall);
if (emergencyCall == liveCall) {
- // Not likely, but a good sanity check.
+ // Not likely, but a good correctness check.
return true;
}
@@ -3938,7 +3938,7 @@
return true;
}
if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) {
- // Sanity check: if there is an orphaned emergency call in the
+ // Correctness check: if there is an orphaned emergency call in the
// {@link CallState#SELECT_PHONE_ACCOUNT} state, just disconnect it since the user
// has explicitly started a new call.
emergencyCall.getAnalytics().setCallIsAdditional(true);
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index bfd625f..a4b64a4 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -436,7 +436,10 @@
mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
mCall.getHandle() == null
? null : mCall.getHandle().getScheme()));
- if (!mAttemptRecords.contains(callAttemptRecord)) {
+ // If the target phone account is null, we'll run into a NPE during the retry
+ // process, so skip it now if it's null.
+ if (callAttemptRecord.targetPhoneAccount != null
+ && !mAttemptRecords.contains(callAttemptRecord)) {
Log.i(this, "Will try Connection Manager account %s for emergency",
callManager);
mAttemptRecords.add(callAttemptRecord);
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index ff09710..0b6b55e 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -1626,7 +1626,11 @@
inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
} catch (RemoteException ignored) {
}
- mBindingFuture.complete(true);
+ // Don't complete the binding future for non-ui incalls
+ if (info.getType() != IN_CALL_SERVICE_TYPE_NON_UI) {
+ mBindingFuture.complete(true);
+ }
+
Log.i(this, "%s calls sent to InCallService.", numCallsSent);
return true;
}
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 88b1c29..9421639 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -1721,6 +1721,32 @@
}
}
+ /**
+ * A method intended for use in testing to clean up any calls that get stuck in the
+ * {@link CallState#DISCONNECTED} or {@link CallState#DISCONNECTING} states. Stuck calls
+ * during CTS cause cascading failures, so if the CTS test detects such a state, it should
+ * call this method via a shell command to clean up before moving on to the next test.
+ */
+ @Override
+ public void cleanupStuckCalls() {
+ Log.startSession("TCI.cSC");
+ try {
+ synchronized (mLock) {
+ enforceShellOnly(Binder.getCallingUid(), "cleanupStuckCalls");
+ Binder.withCleanCallingIdentity(() -> {
+ for (Call call : mCallsManager.getCalls()) {
+ if (call.getState() == CallState.DISCONNECTED
+ || call.getState() == CallState.DISCONNECTING) {
+ mCallsManager.markCallAsRemoved(call);
+ }
+ }
+ });
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
@Override
public void setTestDefaultCallRedirectionApp(String packageName) {
try {
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java
index abb9108..5c78d52 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionManager.java
@@ -37,7 +37,6 @@
/**
* Service which acts as a fake ConnectionManager if so configured.
- * TODO(santoscordon): Rename all classes in the directory to Dummy* (e.g., DummyConnectionService).
*/
public class TestConnectionManager extends ConnectionService {
public final class TestManagedConnection extends Connection {
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
index f6fa116..3d1bc70 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
@@ -53,7 +53,6 @@
/**
* Service which provides fake calls to test the ConnectionService interface.
- * TODO: Rename all classes in the directory to Dummy* (e.g., DummyConnectionService).
*/
public class TestConnectionService extends ConnectionService {
/**
@@ -430,9 +429,9 @@
int videoState = extras.getInt(EXTRA_START_VIDEO_STATE, VideoProfile.STATE_AUDIO_ONLY);
Uri providedHandle = extras.getParcelable(EXTRA_HANDLE);
- // Use dummy number for testing incoming calls.
+ // Use test number for testing incoming calls.
Uri address = providedHandle == null ?
- Uri.fromParts(PhoneAccount.SCHEME_TEL, getDummyNumber(
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, getRandomNumber(
VideoProfile.isVideo(videoState)), null)
: providedHandle;
connection.setVideoState(videoState);
@@ -480,7 +479,7 @@
final Uri providedHandle = extras.getParcelable(EXTRA_HANDLE);
Uri handle = providedHandle == null ?
- Uri.fromParts(PhoneAccount.SCHEME_TEL, getDummyNumber(false), null)
+ Uri.fromParts(PhoneAccount.SCHEME_TEL, getRandomNumber(false), null)
: providedHandle;
connection.setAddress(handle, TelecomManager.PRESENTATION_ALLOWED);
@@ -613,7 +612,7 @@
* @param isVideo {@code True} if the call is a video call.
* @return The phone number.
*/
- private String getDummyNumber(boolean isVideo) {
+ private String getRandomNumber(boolean isVideo) {
int videoDigit = isVideo ? 1 : 0;
int number = mRandom.nextInt(999);
return String.format("555%s%03d", videoDigit, number);
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 3fb2a84..02b35a4 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -27,4 +27,10 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
+ <object type="module_controller"
+ class="com.android.tradefed.testtype.suite.module.TestFailureModuleController">
+ <option name="screenshot-on-failure" value="false" />
+ <option name="bugreportz-on-failure" value="false" />
+ <option name="logcat-on-failure" value="true" />
+ </object>
</configuration>
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
index 58f1ee7..bf105e5 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
@@ -384,8 +384,12 @@
// rest of the system
verifyNoSystemAudioChanges();
+ // Special case for SPEAKER_ON -- we don't expect any route transitions to happen when
+ // there are no calls, so set the expected state to the initial route.
+ int expectedRoute = (mParams.action == CallAudioRouteStateMachine.SPEAKER_ON)
+ ? mParams.initialRoute : mParams.expectedRoute;
// Verify the end state
- CallAudioState expectedState = new CallAudioState(false, mParams.expectedRoute,
+ CallAudioState expectedState = new CallAudioState(false, expectedRoute,
mParams.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER,
mParams.expectedBluetoothDevice, mParams.availableBluetoothDevices);
assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 132d01a..4796ed2 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -1018,7 +1018,7 @@
@SmallTest
@Test
public void testHangupActiveCallWhenHeadsetMediaButtonLongPressDuringTwoCalls() {
- // GIVEN an ongoing call
+ // GIVEN an ongoing call
Call ongoingCall = addSpyCall();
doReturn(CallState.ACTIVE).when(ongoingCall).getState();
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index c31761f..2b461f4 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.ArgumentMatchers.nullable;
@@ -98,6 +99,7 @@
import java.util.Collections;
import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
@RunWith(JUnit4.class)
@@ -133,6 +135,10 @@
private static final String CAR2_CLASS = "carcls";
private static final int CAR_UID = 4;
private static final int CAR2_UID = 5;
+ private static final String NONUI_PKG = "nonui_pkg";
+ private static final String NONUI_CLASS = "nonui_cls";
+ private static final int NONUI_UID = 6;
+
private static final PhoneAccountHandle PA_HANDLE =
new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"), "pa_id");
@@ -177,6 +183,8 @@
return new String[] { CAR_PKG };
case CAR2_UID:
return new String[] { CAR2_PKG };
+ case NONUI_UID:
+ return new String[] { NONUI_PKG };
}
return null;
}).when(mMockPackageManager).getPackagesForUid(anyInt());
@@ -189,6 +197,9 @@
when(mMockPackageManager.checkPermission(
matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
matches(CAR2_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mMockPackageManager.checkPermission(
+ matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
+ matches(NONUI_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED);
}
@Override
@@ -889,13 +900,15 @@
nullable(ContentResolver.class))).thenReturn(500L);
when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall));
- setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+ setupMockPackageManager(true /* default */, true /* nonui */, true /* system */,
+ false /* external calls */,
+ false /* self mgd in default*/, false /* self mgd in car*/);
mInCallController.bindToServices(mMockCall);
ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
ArgumentCaptor<ServiceConnection> serviceConnectionCaptor =
ArgumentCaptor.forClass(ServiceConnection.class);
- verify(mMockContext, times(1)).bindServiceAsUser(
+ verify(mMockContext, times(2)).bindServiceAsUser(
bindIntentCaptor.capture(),
serviceConnectionCaptor.capture(),
eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
@@ -908,13 +921,39 @@
// Start the connection, make sure we don't unbind, and make sure that we don't send
// anything to the in-call service yet.
- ServiceConnection serviceConnection = serviceConnectionCaptor.getValue();
+ List<ServiceConnection> serviceConnections = serviceConnectionCaptor.getAllValues();
+ List<Intent> intents = bindIntentCaptor.getAllValues();
+
+ // Find the non-ui service and have it connect first.
+ int nonUiIdx = findFirstIndexMatching(intents,
+ i -> NONUI_PKG.equals(i.getComponent().getPackageName()));
+ if (nonUiIdx < 0) {
+ fail("Did not bind to non-ui incall");
+ }
+
+ {
+ ComponentName nonUiComponentName = new ComponentName(NONUI_PKG, NONUI_CLASS);
+ IBinder mockBinder = mock(IBinder.class);
+ IInCallService mockInCallService = mock(IInCallService.class);
+ when(mockBinder.queryLocalInterface(anyString())).thenReturn(mockInCallService);
+ serviceConnections.get(nonUiIdx).onServiceConnected(nonUiComponentName, mockBinder);
+
+ // Make sure the non-ui binding didn't trigger the future.
+ assertFalse(bindTimeout.isDone());
+ }
+
+ int defDialerIdx = findFirstIndexMatching(intents,
+ i -> DEF_PKG.equals(i.getComponent().getPackageName()));
+ if (defDialerIdx < 0) {
+ fail("Did not bind to default dialer incall");
+ }
+
ComponentName defDialerComponentName = new ComponentName(DEF_PKG, DEF_CLASS);
IBinder mockBinder = mock(IBinder.class);
IInCallService mockInCallService = mock(IInCallService.class);
when(mockBinder.queryLocalInterface(anyString())).thenReturn(mockInCallService);
- serviceConnection.onServiceConnected(defDialerComponentName, mockBinder);
+ serviceConnections.get(defDialerIdx).onServiceConnected(defDialerComponentName, mockBinder);
verify(mockInCallService).setInCallAdapter(nullable(IInCallAdapter.class));
// Make sure that the future completed without timing out.
@@ -1110,9 +1149,21 @@
}};
}
+ private ResolveInfo getNonUiResolveinfo() {
+ return new ResolveInfo() {{
+ serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = NONUI_PKG;
+ serviceInfo.name = NONUI_CLASS;
+ serviceInfo.applicationInfo = new ApplicationInfo();
+ serviceInfo.applicationInfo.uid = NONUI_UID;
+ serviceInfo.enabled = true;
+ serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
+ }};
+ }
+
private void setupMockPackageManager(final boolean useDefaultDialer,
final boolean useSystemDialer, final boolean includeExternalCalls) {
- setupMockPackageManager(useDefaultDialer, useSystemDialer, includeExternalCalls,
+ setupMockPackageManager(useDefaultDialer, false, useSystemDialer, includeExternalCalls,
false /* self mgd */, false /* self mgd */);
}
@@ -1120,6 +1171,16 @@
final boolean useSystemDialer, final boolean includeExternalCalls,
final boolean includeSelfManagedCallsInDefaultDialer,
final boolean includeSelfManagedCallsInCarModeDialer) {
+ setupMockPackageManager(useDefaultDialer, false /* nonui */, useSystemDialer,
+ includeExternalCalls, includeSelfManagedCallsInDefaultDialer,
+ includeSelfManagedCallsInCarModeDialer);
+ }
+
+ private void setupMockPackageManager(final boolean useDefaultDialer,
+ final boolean useNonUiInCalls,
+ final boolean useSystemDialer, final boolean includeExternalCalls,
+ final boolean includeSelfManagedCallsInDefaultDialer,
+ final boolean includeSelfManagedCallsInCarModeDialer) {
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
@@ -1154,7 +1215,13 @@
resolveInfo.add(getCarModeResolveinfo(CAR2_PKG, CAR2_CLASS,
includeExternalCalls, includeSelfManagedCallsInCarModeDialer));
}
+ } else {
+ // InCallController uses a blank package name when querying for non-ui incalls
+ if (useNonUiInCalls) {
+ resolveInfo.add(getNonUiResolveinfo());
+ }
}
+
return resolveInfo;
}
}).when(mMockPackageManager).queryIntentServicesAsUser(
diff --git a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
index b0b1ec0..264e087 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
@@ -25,8 +25,10 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
public abstract class TelecomTestCase {
protected static final String TESTING_TAG = "Telecom-TEST";
@@ -75,4 +77,13 @@
}
}
}
+
+ protected static <T> int findFirstIndexMatching(List<T> items, Predicate<T> matcher) {
+ for (int i = 0; i < items.size(); i++) {
+ if (matcher.test(items.get(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
}