diff options
| author | Xin Li <delphij@google.com> | 2021-10-07 23:50:24 +0000 |
|---|---|---|
| committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-10-07 23:50:24 +0000 |
| commit | 4dffc62f2b5462ddcd01051c9b479e8e91055376 (patch) | |
| tree | 80afdab2635af0425aca44dc2df05dd652ebcaa7 /tests | |
| parent | 66273f82ba3a6097a4189d453df35aac4b1407b5 (diff) | |
| parent | a920c1b1ca4e2a7d34ea0a08fb06b7cca0a3e8fd (diff) | |
| download | platform_packages_services_Telecomm-master.tar.gz platform_packages_services_Telecomm-master.tar.bz2 platform_packages_services_Telecomm-master.zip | |
Diffstat (limited to 'tests')
22 files changed, 963 insertions, 141 deletions
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 22f5348da..e8c69d419 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -26,6 +26,7 @@ <!-- TODO: Needed because we call BluetoothAdapter.getDefaultAdapter() statically, and BluetoothAdapter is a final class. --> <uses-permission android:name="android.permission.BLUETOOTH" /> + <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" /> <!-- TODO: Needed because we call ActivityManager.getCurrentUser() statically. --> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> @@ -36,6 +37,13 @@ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Used to access Projection State APIs --> + <uses-permission android:name="android.permission.READ_PROJECTION_STATE"/> + + <!-- Used to access PlatformCompat APIs --> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> + <application android:label="@string/app_name" android:debuggable="true"> <uses-library android:name="android.test.runner" /> diff --git a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java index a4cc521d4..2a304a43d 100644 --- a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java +++ b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.content.ContentResolver; import android.content.Context; import android.os.Build; import android.telecom.CallAudioState; @@ -185,6 +186,8 @@ public class AnalyticsTests extends TelecomSystemTest { @MediumTest @Test public void testAnalyticsTwoCalls() throws Exception { + when(mTimeoutsAdapter.getCallScreeningTimeoutMillis(any(ContentResolver.class))) + .thenReturn((long) TEST_TIMEOUT); IdPair testCall1 = startAndMakeActiveIncomingCall( "650-555-1212", mPhoneAccountA0.getAccountHandle(), diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java index a8e1c00b4..a3b8654f5 100644 --- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java +++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java @@ -63,6 +63,7 @@ import com.google.common.base.Predicate; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -473,6 +474,7 @@ public class BasicCallTests extends TelecomSystemTest { @LargeTest @Test @FlakyTest + @Ignore("b/189904580") public void testIncomingCallFromBlockedNumberIsRejected() throws Exception { String phoneNumber = "650-555-1212"; blockNumber(phoneNumber); @@ -492,6 +494,7 @@ public class BasicCallTests extends TelecomSystemTest { waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(), TEST_TIMEOUT); + assertEquals(1, mCallerInfoAsyncQueryFactoryFixture.mRequests.size()); for (CallerInfoAsyncQueryFactoryFixture.Request request : mCallerInfoAsyncQueryFactoryFixture.mRequests) { @@ -827,8 +830,7 @@ public class BasicCallTests extends TelecomSystemTest { private void blockNumberWithAnswer(String phoneNumber, Answer answer) throws Exception { when(getBlockedNumberProvider().call( - anyString(), - nullable(String.class), + any(), anyString(), eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER), eq(phoneNumber), diff --git a/tests/src/com/android/server/telecom/tests/BlockedNumbersUtilTests.java b/tests/src/com/android/server/telecom/tests/BlockedNumbersUtilTests.java new file mode 100644 index 000000000..56cb73570 --- /dev/null +++ b/tests/src/com/android/server/telecom/tests/BlockedNumbersUtilTests.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.telecom.tests; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.verify; + +import android.app.Notification; +import android.app.NotificationManager; +import android.os.UserHandle; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.server.telecom.settings.BlockedNumbersUtil; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class BlockedNumbersUtilTests extends TelecomTestCase { + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + } + + @SmallTest + @Test + public void testPostNotification() { + BlockedNumbersUtil.updateEmergencyCallNotification(mContext, true); + NotificationManager mgr = mComponentContextFixture.getNotificationManager(); + verify(mgr).notifyAsUser(isNull(), anyInt(), any(Notification.class), + any(UserHandle.class)); + } + + @SmallTest + @Test + public void testDismissNotification() { + BlockedNumbersUtil.updateEmergencyCallNotification(mContext, false); + NotificationManager mgr = mComponentContextFixture.getNotificationManager(); + verify(mgr).cancelAsUser(isNull(), anyInt(), any(UserHandle.class)); + } +} diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java index bfa7a75bb..02fdecc3c 100644 --- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java +++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java @@ -26,8 +26,6 @@ import android.content.Intent; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; -import com.android.server.telecom.BluetoothAdapterProxy; -import com.android.server.telecom.BluetoothHeadsetProxy; import com.android.server.telecom.bluetooth.BluetoothDeviceManager; import com.android.server.telecom.bluetooth.BluetoothRouteManager; import com.android.server.telecom.bluetooth.BluetoothStateReceiver; @@ -54,8 +52,8 @@ import java.util.Arrays; @RunWith(JUnit4.class) public class BluetoothDeviceManagerTest extends TelecomTestCase { @Mock BluetoothRouteManager mRouteManager; - @Mock BluetoothHeadsetProxy mHeadsetProxy; - @Mock BluetoothAdapterProxy mAdapterProxy; + @Mock BluetoothHeadset mBluetoothHeadset; + @Mock BluetoothAdapter mAdapter; @Mock BluetoothHearingAid mBluetoothHearingAid; BluetoothDeviceManager mBluetoothDeviceManager; @@ -82,18 +80,18 @@ public class BluetoothDeviceManagerTest extends TelecomTestCase { when(mBluetoothHearingAid.getHiSyncId(device4)).thenReturn(100L); mContext = mComponentContextFixture.getTestDouble().getApplicationContext(); - mBluetoothDeviceManager = new BluetoothDeviceManager(mContext, mAdapterProxy); + mBluetoothDeviceManager = new BluetoothDeviceManager(mContext, mAdapter); mBluetoothDeviceManager.setBluetoothRouteManager(mRouteManager); ArgumentCaptor<BluetoothProfile.ServiceListener> serviceCaptor = ArgumentCaptor.forClass(BluetoothProfile.ServiceListener.class); - verify(mAdapterProxy).getProfileProxy(eq(mContext), + verify(mAdapter).getProfileProxy(eq(mContext), serviceCaptor.capture(), eq(BluetoothProfile.HEADSET)); serviceListenerUnderTest = serviceCaptor.getValue(); receiverUnderTest = new BluetoothStateReceiver(mBluetoothDeviceManager, mRouteManager); - mBluetoothDeviceManager.setHeadsetServiceForTesting(mHeadsetProxy); + mBluetoothDeviceManager.setHeadsetServiceForTesting(mBluetoothHeadset); mBluetoothDeviceManager.setHearingAidServiceForTesting(mBluetoothHearingAid); } @@ -127,7 +125,7 @@ public class BluetoothDeviceManagerTest extends TelecomTestCase { assertEquals(0, mBluetoothDeviceManager.getNumConnectedDevices()); } - + @SmallTest @Test public void testMultiDeviceConnectAndDisconnect() { @@ -173,7 +171,7 @@ public class BluetoothDeviceManagerTest extends TelecomTestCase { verify(mRouteManager).onDeviceLost(device1.getAddress()); verify(mRouteManager).onDeviceLost(device3.getAddress()); verify(mRouteManager, never()).onDeviceLost(device2.getAddress()); - assertNull(mBluetoothDeviceManager.getHeadsetService()); + assertNull(mBluetoothDeviceManager.getBluetoothHeadset()); assertEquals(1, mBluetoothDeviceManager.getNumConnectedDevices()); } @@ -192,7 +190,7 @@ public class BluetoothDeviceManagerTest extends TelecomTestCase { verify(mRouteManager).onDeviceLost(device2.getAddress()); verify(mRouteManager, never()).onDeviceLost(device1.getAddress()); verify(mRouteManager, never()).onDeviceLost(device3.getAddress()); - assertNull(mBluetoothDeviceManager.getHearingAidService()); + assertNull(mBluetoothDeviceManager.getBluetoothHearingAid()); assertEquals(2, mBluetoothDeviceManager.getNumConnectedDevices()); } @@ -216,12 +214,14 @@ public class BluetoothDeviceManagerTest extends TelecomTestCase { public void testConnectDisconnectAudioHeadset() { receiverUnderTest.onReceive(mContext, buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false)); - when(mAdapterProxy.setActiveDevice(nullable(BluetoothDevice.class), eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true); + when(mAdapter.setActiveDevice(nullable(BluetoothDevice.class), + eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true); mBluetoothDeviceManager.connectAudio(device1.getAddress()); - verify(mAdapterProxy).setActiveDevice(device1, BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL); - verify(mAdapterProxy, never()).setActiveDevice(nullable(BluetoothDevice.class), eq(BluetoothAdapter.ACTIVE_DEVICE_ALL)); + verify(mAdapter).setActiveDevice(device1, BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL); + verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class), + eq(BluetoothAdapter.ACTIVE_DEVICE_ALL)); mBluetoothDeviceManager.disconnectAudio(); - verify(mHeadsetProxy).disconnectAudio(); + verify(mBluetoothHeadset).disconnectAudio(); } @SmallTest @@ -230,15 +230,17 @@ public class BluetoothDeviceManagerTest extends TelecomTestCase { receiverUnderTest.onReceive(mContext, buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true)); mBluetoothDeviceManager.connectAudio(device2.getAddress()); - verify(mAdapterProxy).setActiveDevice(device2, BluetoothAdapter.ACTIVE_DEVICE_ALL); - verify(mHeadsetProxy, never()).connectAudio(); - verify(mAdapterProxy, never()).setActiveDevice(nullable(BluetoothDevice.class), eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL)); + verify(mAdapter).setActiveDevice(device2, BluetoothAdapter.ACTIVE_DEVICE_ALL); + verify(mBluetoothHeadset, never()).connectAudio(); + verify(mAdapter, never()).setActiveDevice(nullable(BluetoothDevice.class), + eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL)); - when(mBluetoothHearingAid.getActiveDevices()).thenReturn(Arrays.asList(device2, null)); + when(mAdapter.getActiveDevices(eq(BluetoothProfile.HEARING_AID))) + .thenReturn(Arrays.asList(device2, null)); mBluetoothDeviceManager.disconnectAudio(); - verify(mAdapterProxy).setActiveDevice(null, BluetoothAdapter.ACTIVE_DEVICE_ALL); - verify(mHeadsetProxy).disconnectAudio(); + verify(mAdapter).setActiveDevice(null, BluetoothAdapter.ACTIVE_DEVICE_ALL); + verify(mBluetoothHeadset).disconnectAudio(); } private Intent buildConnectionActionIntent(int state, BluetoothDevice device, diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java index b20ecfb22..28f69668e 100644 --- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java +++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java @@ -16,16 +16,17 @@ package com.android.server.telecom.tests; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothProfile; import android.content.ContentResolver; import android.os.Parcel; import android.telecom.Log; import android.test.suitebuilder.annotation.SmallTest; import com.android.internal.os.SomeArgs; -import com.android.server.telecom.BluetoothHeadsetProxy; import com.android.server.telecom.TelecomSystem; import com.android.server.telecom.Timeouts; import com.android.server.telecom.bluetooth.BluetoothDeviceManager; @@ -60,8 +61,9 @@ public class BluetoothRouteManagerTest extends TelecomTestCase { static final BluetoothDevice DEVICE3 = makeBluetoothDevice("00:00:00:00:00:03"); static final BluetoothDevice HEARING_AID_DEVICE = makeBluetoothDevice("00:00:00:00:00:04"); + @Mock private BluetoothAdapter mBluetoothAdapter; @Mock private BluetoothDeviceManager mDeviceManager; - @Mock private BluetoothHeadsetProxy mHeadsetProxy; + @Mock private BluetoothHeadset mBluetoothHeadset; @Mock private BluetoothHearingAid mBluetoothHearingAid; @Mock private Timeouts.Adapter mTimeoutsAdapter; @Mock private BluetoothRouteManager.BluetoothStateListener mListener; @@ -86,7 +88,7 @@ public class BluetoothRouteManagerTest extends TelecomTestCase { setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null, null, null); when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis( nullable(ContentResolver.class))).thenReturn(0L); - when(mHeadsetProxy.connectAudio()).thenReturn(false); + when(mBluetoothHeadset.connectAudio()).thenReturn(false); executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, DEVICE1.getAddress()); // Wait 3 times: for the first connection attempt, the retry attempt, // the second retry, and once more to make sure there are only three attempts. @@ -125,7 +127,7 @@ public class BluetoothRouteManagerTest extends TelecomTestCase { BluetoothRouteManager sm = setupStateMachine( BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1); setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null, DEVICE1, null); - when(mHeadsetProxy.getAudioState(DEVICE1)) + when(mBluetoothHeadset.getAudioState(DEVICE1)) .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); executeRoutingAction(sm, BluetoothRouteManager.BT_AUDIO_LOST, DEVICE1.getAddress()); @@ -143,7 +145,7 @@ public class BluetoothRouteManagerTest extends TelecomTestCase { setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null, null, null); when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis( nullable(ContentResolver.class))).thenReturn(0L); - when(mHeadsetProxy.connectAudio()).thenReturn(false); + when(mBluetoothHeadset.connectAudio()).thenReturn(false); executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, DEVICE2.getAddress()); // Wait 3 times: the first connection attempt is accounted for in executeRoutingAction, // so wait twice for the retry attempt, again to make sure there are only three attempts, @@ -185,14 +187,15 @@ public class BluetoothRouteManagerTest extends TelecomTestCase { .collect(Collectors.toList()); when(mDeviceManager.getConnectedDevices()).thenReturn(allDevices); - when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(hfpDevices)); - when(mHeadsetProxy.getActiveDevice()).thenReturn(hfpActiveDevice); - when(mHeadsetProxy.getAudioState(hfpActiveDevice)) + when(mBluetoothHeadset.getConnectedDevices()).thenReturn(Arrays.asList(hfpDevices)); + when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET))) + .thenReturn(Arrays.asList(hfpActiveDevice)); + when(mBluetoothHeadset.getAudioState(hfpActiveDevice)) .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED); when(mBluetoothHearingAid.getConnectedDevices()) .thenReturn(Arrays.asList(hearingAidDevices)); - when(mBluetoothHearingAid.getActiveDevices()) + when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEARING_AID))) .thenReturn(Arrays.asList(hearingAidActiveDevice, null)); } @@ -215,11 +218,12 @@ public class BluetoothRouteManagerTest extends TelecomTestCase { } private void resetMocks() { - reset(mDeviceManager, mListener, mHeadsetProxy, mTimeoutsAdapter); - when(mDeviceManager.getHeadsetService()).thenReturn(mHeadsetProxy); - when(mDeviceManager.getHearingAidService()).thenReturn(mBluetoothHearingAid); - when(mHeadsetProxy.connectAudio()).thenReturn(true); - when(mHeadsetProxy.setActiveDevice(nullable(BluetoothDevice.class))).thenReturn(true); + reset(mDeviceManager, mListener, mBluetoothHeadset, mTimeoutsAdapter); + when(mDeviceManager.getBluetoothHeadset()).thenReturn(mBluetoothHeadset); + when(mDeviceManager.getBluetoothHearingAid()).thenReturn(mBluetoothHearingAid); + when(mDeviceManager.getBluetoothAdapter()).thenReturn(mBluetoothAdapter); + when(mBluetoothHeadset.connectAudio()).thenReturn(true); + when(mBluetoothHeadset.setActiveDevice(nullable(BluetoothDevice.class))).thenReturn(true); when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis( nullable(ContentResolver.class))).thenReturn(100000L); when(mTimeoutsAdapter.getBluetoothPendingTimeoutMillis( diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java index b36d74bf1..d96b68746 100644 --- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java +++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java @@ -16,15 +16,16 @@ package com.android.server.telecom.tests; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothProfile; import android.content.ContentResolver; import android.telecom.Log; import android.test.suitebuilder.annotation.SmallTest; import com.android.internal.os.SomeArgs; -import com.android.server.telecom.BluetoothHeadsetProxy; import com.android.server.telecom.TelecomSystem; import com.android.server.telecom.Timeouts; import com.android.server.telecom.bluetooth.BluetoothDeviceManager; @@ -76,7 +77,7 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase { private BluetoothDevice expectedConnectionDevice; private String expectedFinalStateName; private BluetoothDevice[] connectedDevices; - // the active device as returned by BluetoothHeadset#getActiveDevice + // the active device as returned by BluetoothAdapter#getActiveDevices private BluetoothDevice activeDevice = null; private List<BluetoothDevice> hearingAidBtDevices = Collections.emptyList(); @@ -183,7 +184,7 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase { public BluetoothDevice expectedConnectionDevice; // Expected device to connect to. public String expectedFinalStateName; // Expected name of the final state. public BluetoothDevice[] connectedDevices; // array of connected devices - // the active device as returned by BluetoothHeadset#getActiveDevice + // the active device as returned by BluetoothAdapter#getActiveDevices private BluetoothDevice activeDevice = null; private List<BluetoothDevice> hearingAidBtDevices; @@ -236,7 +237,8 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase { private final BluetoothRouteTestParameters mParams; @Mock private BluetoothDeviceManager mDeviceManager; - @Mock private BluetoothHeadsetProxy mHeadsetProxy; + @Mock private BluetoothAdapter mBluetoothAdapter; + @Mock private BluetoothHeadset mBluetoothHeadset; @Mock private BluetoothHearingAid mBluetoothHearingAid; @Mock private Timeouts.Adapter mTimeoutsAdapter; @Mock private BluetoothRouteManager.BluetoothStateListener mListener; @@ -245,6 +247,7 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase { @Before public void setUp() throws Exception { super.setUp(); + when(mDeviceManager.getBluetoothAdapter()).thenReturn(mBluetoothAdapter); } @Override @@ -272,7 +275,8 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase { SomeArgs args = SomeArgs.obtain(); args.arg1 = Log.createSubsession(); args.arg2 = mParams.initialDevice.getAddress(); - when(mHeadsetProxy.getActiveDevice()).thenReturn(null); + when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET))) + .thenReturn(Arrays.asList((BluetoothDevice) null)); sm.sendMessage(BluetoothRouteManager.BT_AUDIO_LOST, args); return true; }).when(mDeviceManager).disconnectAudio(); @@ -287,9 +291,11 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase { sm.onActiveDeviceChanged(null, mParams.hearingAidBtDevices.contains(mParams.messageDevice)); if (mParams.hearingAidBtDevices.contains(mParams.messageDevice)) { - when(mBluetoothHearingAid.getActiveDevices()).thenReturn(Arrays.asList(null, null)); + when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEARING_AID))) + .thenReturn(Arrays.asList(null, null)); } else { - when(mHeadsetProxy.getActiveDevice()).thenReturn(null); + when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET))) + .thenReturn(Arrays.asList((BluetoothDevice) null)); } sm.onDeviceLost(mParams.messageDevice.getAddress()); } else { @@ -344,13 +350,15 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase { BluetoothDevice audioOnDevice, BluetoothDevice activeDevice) { when(mDeviceManager.getNumConnectedDevices()).thenReturn(devices.length); when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices)); - when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices)); - when(mHeadsetProxy.getActiveDevice()).thenReturn(activeDevice); - when(mHeadsetProxy.getAudioState(nullable(BluetoothDevice.class))) + when(mBluetoothHeadset.getConnectedDevices()).thenReturn(Arrays.asList(devices)); + when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET))) + .thenReturn(Arrays.asList(activeDevice)); + when(mBluetoothHeadset.getAudioState(nullable(BluetoothDevice.class))) .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); if (audioOnDevice != null) { - when(mHeadsetProxy.getActiveDevice()).thenReturn(audioOnDevice); - when(mHeadsetProxy.getAudioState(audioOnDevice)) + when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.HEADSET))) + .thenReturn(Arrays.asList(audioOnDevice)); + when(mBluetoothHeadset.getAudioState(audioOnDevice)) .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED); } } @@ -358,8 +366,8 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase { private BluetoothRouteManager setupStateMachine(String initialState, BluetoothDevice initialDevice) { resetMocks(); - when(mDeviceManager.getHeadsetService()).thenReturn(mHeadsetProxy); - when(mDeviceManager.getHearingAidService()).thenReturn(mBluetoothHearingAid); + when(mDeviceManager.getBluetoothHeadset()).thenReturn(mBluetoothHeadset); + when(mDeviceManager.getBluetoothHearingAid()).thenReturn(mBluetoothHearingAid); when(mDeviceManager.connectAudio(nullable(String.class))).thenReturn(true); when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis( nullable(ContentResolver.class))).thenReturn(100000L); @@ -375,7 +383,7 @@ public class BluetoothRouteTransitionTests extends TelecomTestCase { } private void resetMocks() { - clearInvocations(mDeviceManager, mListener, mHeadsetProxy, mTimeoutsAdapter); + clearInvocations(mDeviceManager, mListener, mBluetoothHeadset, mTimeoutsAdapter); } @Parameterized.Parameters(name = "{0}") diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java index 976a4dae8..91ec7f35f 100644 --- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java +++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java @@ -17,7 +17,10 @@ package com.android.server.telecom.tests; import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.media.AudioManager; import android.media.IAudioService; import android.os.HandlerThread; @@ -151,6 +154,49 @@ public class CallAudioRouteStateMachineTest extends TelecomTestCase { @MediumTest @Test + public void testStreamRingMuteChange() { + CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( + mContext, + mockCallsManager, + mockBluetoothRouteManager, + mockWiredHeadsetManager, + mockStatusBarNotifier, + mAudioServiceFactory, + CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED, + mThreadHandler.getLooper()); + stateMachine.setCallAudioManager(mockCallAudioManager); + CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER, + CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER); + stateMachine.initialize(initState); + + // Make sure we register a receiver for the STREAM_MUTE_CHANGED_ACTION so we can see if the + // ring stream unmutes. + ArgumentCaptor<BroadcastReceiver> brCaptor = ArgumentCaptor.forClass( + BroadcastReceiver.class); + ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class); + verify(mContext, times(3)).registerReceiver(brCaptor.capture(), filterCaptor.capture()); + boolean foundValid = false; + for (int ix = 0; ix < brCaptor.getAllValues().size(); ix++) { + BroadcastReceiver receiver = brCaptor.getAllValues().get(ix); + IntentFilter filter = filterCaptor.getAllValues().get(ix); + if (!filter.hasAction(AudioManager.STREAM_MUTE_CHANGED_ACTION)) { + continue; + } + + // Fake out a call to the broadcast receiver and make sure we call into audio manager + // to trigger re-evaluation of ringing. + Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION); + intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false); + intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_RING); + receiver.onReceive(mContext, intent); + verify(mockCallAudioManager).onRingerModeChange(); + foundValid = true; + } + assertTrue(foundValid); + } + + @MediumTest + @Test public void testSpeakerPersistence() { CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine( mContext, diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java index dfc32588b..b9f5667ab 100644 --- a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java +++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java @@ -18,6 +18,7 @@ package com.android.server.telecom.tests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -40,7 +41,9 @@ import android.content.res.Resources; import android.location.Country; import android.location.CountryDetector; import android.location.CountryListener; +import android.location.Location; import android.net.Uri; +import android.os.Bundle; import android.os.Looper; import android.os.PersistableBundle; import android.os.SystemClock; @@ -52,6 +55,7 @@ import android.telecom.Connection; import android.telecom.DisconnectCause; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; import android.telecom.VideoProfile; import android.telephony.CarrierConfigManager; import android.telephony.PhoneNumberUtils; @@ -172,7 +176,7 @@ public class CallLogManagerTest extends TelecomTestCase { when(userManager.isUserUnlocked(any(UserHandle.class))).thenReturn(true); when(userManager.hasUserRestriction(any(String.class), any(UserHandle.class))) .thenReturn(false); - when(userManager.getUsers(any(Boolean.class))) + when(userManager.getAliveUsers()) .thenReturn(Arrays.asList(userInfo, otherUserInfo, managedProfileUserInfo)); when(userManager.getUserInfo(eq(CURRENT_USER_ID))).thenReturn(userInfo); when(userManager.getUserInfo(eq(OTHER_USER_ID))).thenReturn(otherUserInfo); @@ -819,6 +823,54 @@ public class CallLogManagerTest extends TelecomTestCase { @SmallTest @Test + public void testCallComposerElements() { + Call fakeCall = makeFakeCall( + DisconnectCause.LOCAL, // disconnectCauseCode + false, // isConference + true, // isIncoming + 1L, // creationTimeMillis + 1000L, // ageMillis + TEL_PHONEHANDLE, // callHandle + mDefaultAccountHandle, // phoneAccountHandle + NO_VIDEO_STATE, // callVideoState + POST_DIAL_STRING, // postDialDigits + VIA_NUMBER_STRING, // viaNumber + UserHandle.of(CURRENT_USER_ID) + ); + String subject = "segmentation fault"; + // =) + double lat = 40.649723; + double lon = -80.082090; + Location location = new Location(""); + location.setLatitude(lat); + location.setLongitude(lon); + + Uri fakeProviderUri = Uri.parse("content://nothing_to_see_here/12345"); + + Bundle extras = new Bundle(); + extras.putInt(TelecomManager.EXTRA_PRIORITY, TelecomManager.PRIORITY_URGENT); + extras.putString(TelecomManager.EXTRA_CALL_SUBJECT, subject); + extras.putParcelable(TelecomManager.EXTRA_LOCATION, location); + extras.putParcelable(TelecomManager.EXTRA_PICTURE_URI, fakeProviderUri); + when(fakeCall.getIntentExtras()).thenReturn(extras); + + mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, + CallState.DISCONNECTED); + ContentValues locationValues = verifyLocationInsertionWithCapture(CURRENT_USER_ID); + assertEquals(lat, locationValues.getAsDouble(CallLog.Locations.LATITUDE), 0); + assertEquals(lon, locationValues.getAsDouble(CallLog.Locations.LONGITUDE), 0); + + ContentValues callLogValues = verifyInsertionWithCapture(CURRENT_USER_ID); + assertEquals(subject, callLogValues.getAsString(Calls.SUBJECT)); + assertEquals(fakeProviderUri.toString(), + callLogValues.getAsString(Calls.COMPOSER_PHOTO_URI)); + assertEquals(TelecomManager.PRIORITY_URGENT, + (int) callLogValues.getAsInteger(Calls.PRIORITY)); + assertNotNull(callLogValues.getAsString(Calls.LOCATION)); + } + + @SmallTest + @Test public void testDoNotLogConferenceWithNoChildren() { Call fakeCall = makeFakeCall( DisconnectCause.LOCAL, // disconnectCauseCode @@ -976,6 +1028,14 @@ public class CallLogManagerTest extends TelecomTestCase { return captor.getValue(); } + private ContentValues verifyLocationInsertionWithCapture(int userId) { + Uri uri = ContentProvider.maybeAddUserId(CallLog.Locations.CONTENT_URI, userId); + ArgumentCaptor<ContentValues> captor = ArgumentCaptor.forClass(ContentValues.class); + verify(mContentProvider, timeout(TEST_TIMEOUT_MILLIS).times(1)).insert( + eq(uri), captor.capture()); + return captor.getValue(); + } + private Call makeFakeCall(int disconnectCauseCode, boolean isConference, boolean isIncoming, long creationTimeMillis, long ageMillis, Uri callHandle, PhoneAccountHandle phoneAccountHandle, int callVideoState, diff --git a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java index ff16880cc..0a896a8ce 100644 --- a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java +++ b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java @@ -65,8 +65,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; - @RunWith(JUnit4.class) public class CallRedirectionProcessorTest extends TelecomTestCase { @Mock private Context mContext; @@ -151,7 +149,7 @@ public class CallRedirectionProcessorTest extends TelecomTestCase { } private void setIsInCarMode(boolean isInCarMode) { - when(mSystemStateHelper.isCarMode()).thenReturn(isInCarMode); + when(mSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(isInCarMode); } private void enableUserDefinedCallRedirectionService() { diff --git a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java index c7b3a7eb5..68caf6776 100644 --- a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java +++ b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java @@ -93,6 +93,16 @@ public class CallScreeningServiceFilterTest extends TelecomTestCase { .setShouldShowNotification(true) .build(); + private static final CallFilteringResult PASS_RESULT_WITH_NAME = + new CallFilteringResult.Builder() + .setShouldAllowCall(true) + .setShouldReject(false) + .setShouldAddToCallLog(true) + .setShouldShowNotification(true) + .setCallScreeningAppName(APP_NAME) + .setCallScreeningComponentName(COMPONENT_NAME.flattenToString()) + .build(); + @Override @Before public void setUp() throws Exception { @@ -235,8 +245,14 @@ public class CallScreeningServiceFilterTest extends TelecomTestCase { serviceConnection.onServiceConnected(COMPONENT_NAME, mBinder); ICallScreeningAdapter csAdapter = getCallScreeningAdapter(); - csAdapter.allowCall(CALL_ID); - assertEquals(PASS_RESULT, + CallScreeningService.CallResponse allowCallResponse = + new CallScreeningService.CallResponse.Builder() + .setDisallowCall(false) + .setRejectCall(false) + .setSilenceCall(false) + .build(); + csAdapter.onScreeningResponse(CALL_ID, COMPONENT_NAME, allowCallResponse.toParcelable()); + assertEquals(PASS_RESULT_WITH_NAME, resultFuture.toCompletableFuture().get( CallScreeningServiceFilter.CALL_SCREENING_FILTER_TIMEOUT, TimeUnit.MILLISECONDS)); @@ -264,12 +280,16 @@ public class CallScreeningServiceFilterTest extends TelecomTestCase { ServiceConnection serviceConnection = verifyBindingIntent(); serviceConnection.onServiceConnected(COMPONENT_NAME, mBinder); + CallScreeningService.CallResponse disallowCallResponse = + new CallScreeningService.CallResponse.Builder() + .setDisallowCall(true) + .setRejectCall(true) + .setSkipCallLog(false) + .setSkipNotification(false) + .build(); ICallScreeningAdapter csAdapter = getCallScreeningAdapter(); - csAdapter.disallowCall(CALL_ID, - true, // shouldReject - true, //shouldAddToCallLog - true, // shouldShowNotification - COMPONENT_NAME); + csAdapter.onScreeningResponse(CALL_ID, COMPONENT_NAME, disallowCallResponse.toParcelable()); + assertEquals(expectedResult, resultFuture.toCompletableFuture().get( CallScreeningServiceFilter.CALL_SCREENING_FILTER_TIMEOUT, @@ -286,6 +306,8 @@ public class CallScreeningServiceFilterTest extends TelecomTestCase { .setShouldSilence(true) .setShouldAddToCallLog(true) .setShouldShowNotification(true) + .setCallScreeningAppName(APP_NAME) + .setCallScreeningComponentName(COMPONENT_NAME.flattenToString()) .build(); CallScreeningServiceFilter filter = new CallScreeningServiceFilter(mCall, PKG_NAME, CallScreeningServiceFilter.PACKAGE_TYPE_CARRIER, mContext, mCallsManager, @@ -296,7 +318,13 @@ public class CallScreeningServiceFilterTest extends TelecomTestCase { serviceConnection.onServiceConnected(COMPONENT_NAME, mBinder); ICallScreeningAdapter csAdapter = getCallScreeningAdapter(); - csAdapter.silenceCall(CALL_ID); + CallScreeningService.CallResponse silenceCallResponse = + new CallScreeningService.CallResponse.Builder() + .setDisallowCall(false) + .setRejectCall(false) + .setSilenceCall(true) + .build(); + csAdapter.onScreeningResponse(CALL_ID, COMPONENT_NAME, silenceCallResponse.toParcelable()); assertEquals(expectedResult, resultFuture.toCompletableFuture().get( CallScreeningServiceFilter.CALL_SCREENING_FILTER_TIMEOUT, @@ -314,6 +342,7 @@ public class CallScreeningServiceFilterTest extends TelecomTestCase { .setShouldSilence(false) .setShouldScreenViaAudio(true) .setCallScreeningAppName(APP_NAME) + .setCallScreeningComponentName(COMPONENT_NAME.flattenToString()) .build(); CallScreeningServiceFilter filter = new CallScreeningServiceFilter(mCall, PKG_NAME, CallScreeningServiceFilter.PACKAGE_TYPE_DEFAULT_DIALER, mContext, mCallsManager, @@ -323,8 +352,17 @@ public class CallScreeningServiceFilterTest extends TelecomTestCase { ServiceConnection serviceConnection = verifyBindingIntent(); serviceConnection.onServiceConnected(COMPONENT_NAME, mBinder); + + CallScreeningService.CallResponse additionalScreeningResponse = + new CallScreeningService.CallResponse.Builder() + .setDisallowCall(false) + .setRejectCall(false) + .setSilenceCall(false) + .setShouldScreenCallViaAudioProcessing(true) + .build(); ICallScreeningAdapter csAdapter = getCallScreeningAdapter(); - csAdapter.screenCallFurther(CALL_ID); + csAdapter.onScreeningResponse(CALL_ID, COMPONENT_NAME, + additionalScreeningResponse.toParcelable()); assertEquals(expectedResult, resultFuture.toCompletableFuture().get( CallScreeningServiceFilter.CALL_SCREENING_FILTER_TIMEOUT, diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java index 08f353668..da7293330 100644 --- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java +++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java @@ -21,6 +21,7 @@ import static junit.framework.TestCase.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -36,11 +37,14 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -52,6 +56,8 @@ import android.os.SystemClock; import android.os.UserHandle; import android.telecom.CallerInfo; import android.telecom.Connection; +import android.telecom.DisconnectCause; +import android.telecom.Log; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; @@ -75,6 +81,7 @@ import com.android.server.telecom.CallsManagerListenerBase; import com.android.server.telecom.ClockProxy; import com.android.server.telecom.ConnectionServiceFocusManager; import com.android.server.telecom.ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory; +import com.android.server.telecom.ConnectionServiceWrapper; import com.android.server.telecom.DefaultDialerCache; import com.android.server.telecom.EmergencyCallHelper; import com.android.server.telecom.HeadsetMediaButton; @@ -228,6 +235,8 @@ public class CallsManagerTest extends TelecomTestCase { doNothing().when(mRoleManagerAdapter).setCurrentUserHandle(any()); when(mDisconnectedCallNotifierFactory.create(any(Context.class),any(CallsManager.class))) .thenReturn(mDisconnectedCallNotifier); + when(mTimeoutsAdapter.getCallDiagnosticServiceTimeoutMillis(any(ContentResolver.class))) + .thenReturn(2000L); mCallsManager = new CallsManager( mComponentContextFixture.getTestDouble().getApplicationContext(), mLock, @@ -1106,6 +1115,46 @@ public class CallsManagerTest extends TelecomTestCase { assertFalse(mCallsManager.isInEmergencyCall()); } + + @SmallTest + @Test + public void testBlockNonEmergencyCallDuringEmergencyCall() throws Exception { + // Setup a call which the network identified as an emergency call. + Call ongoingCall = addSpyCall(); + ongoingCall.setConnectionProperties(Connection.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL); + assertTrue(mCallsManager.isInEmergencyCall()); + + Call newCall = addSpyCall(CallState.NEW); + ConnectionServiceWrapper service = mock(ConnectionServiceWrapper.class); + doReturn(SIM_2_HANDLE.getComponentName()).when(service).getComponentName(); + + // Ensure contact info lookup succeeds + doAnswer(invocation -> { + Uri handle = invocation.getArgument(0); + CallerInfo info = new CallerInfo(); + CompletableFuture<Pair<Uri, CallerInfo>> callerInfoFuture = new CompletableFuture<>(); + callerInfoFuture.complete(new Pair<>(handle, info)); + return callerInfoFuture; + }).when(mCallerInfoLookupHelper).startLookup(any(Uri.class)); + + // Ensure we have candidate phone account handle info. + when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn( + SIM_1_HANDLE); + when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(), + any(), anyInt(), anyInt())).thenReturn( + new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE))); + mCallsManager.addConnectionServiceRepositoryCache(SIM_2_HANDLE.getComponentName(), + SIM_2_HANDLE.getUserHandle(), service); + + CompletableFuture<Call> callFuture = mCallsManager.startOutgoingCall( + newCall.getHandle(), newCall.getTargetPhoneAccount(), new Bundle(), + UserHandle.CURRENT, new Intent(), "com.test.stuff"); + + verify(service, timeout(TEST_TIMEOUT)).createConnectionFailed(any()); + Call result = callFuture.get(TEST_TIMEOUT, TimeUnit.MILLISECONDS); + assertNull(result); + } + @SmallTest @Test public void testHasEmergencyCallIncomingCallPermitted() { @@ -1204,6 +1253,21 @@ public class CallsManagerTest extends TelecomTestCase { verify(ringingCall).reject(anyBoolean(), any(), any()); } + @SmallTest + @Test + public void testMakeRoomForOutgoingCallConnecting() { + Call ongoingCall = addSpyCall(SIM_2_HANDLE, CallState.CONNECTING); + + Call newCall = createCall(SIM_1_HANDLE, CallState.NEW); + when(mComponentContextFixture.getTelephonyManager().isEmergencyNumber(any())) + .thenReturn(false); + newCall.setHandle(Uri.fromParts("tel", "5551213", null), + TelecomManager.PRESENTATION_ALLOWED); + + assertTrue(mCallsManager.makeRoomForOutgoingCall(newCall)); + verify(ongoingCall).disconnect(anyLong(), anyString()); + } + /** * Verifies that changes to a {@link PhoneAccount}'s * {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} capability will be reflected on a call. @@ -1508,6 +1572,59 @@ public class CallsManagerTest extends TelecomTestCase { eq(CallState.ACTIVE)); } + /** + * Verifies where a call diagnostic service is NOT in use that we don't try to relay to the + * CallDiagnosticService and that we get a synchronous disconnect. + * @throws Exception + */ + @MediumTest + @Test + public void testDisconnectCallSynchronous() throws Exception { + Call callSpy = addSpyCall(); + callSpy.setIsSimCall(true); + when(mCallDiagnosticServiceController.isConnected()).thenReturn(false); + mCallsManager.markCallAsDisconnected(callSpy, new DisconnectCause(DisconnectCause.ERROR)); + + verify(mCallDiagnosticServiceController, never()).onCallDisconnected(any(Call.class), + any(DisconnectCause.class)); + verify(callSpy).setDisconnectCause(any(DisconnectCause.class)); + } + + @MediumTest + @Test + public void testDisconnectCallAsynchronous() throws Exception { + Call callSpy = addSpyCall(); + callSpy.setIsSimCall(true); + when(mCallDiagnosticServiceController.isConnected()).thenReturn(true); + when(mCallDiagnosticServiceController.onCallDisconnected(any(Call.class), + any(DisconnectCause.class))).thenReturn(true); + mCallsManager.markCallAsDisconnected(callSpy, new DisconnectCause(DisconnectCause.ERROR)); + + verify(mCallDiagnosticServiceController).onCallDisconnected(any(Call.class), + any(DisconnectCause.class)); + verify(callSpy, never()).setDisconnectCause(any(DisconnectCause.class)); + } + + /** + * Verifies that if call state goes from DIALING to DISCONNECTED, and a call diagnostic service + * IS in use, it would call onCallDisconnected of the CallDiagnosticService + * @throws Exception + */ + @MediumTest + @Test + public void testDisconnectDialingCall() throws Exception { + Call callSpy = addSpyCall(CallState.DIALING); + callSpy.setIsSimCall(true); + when(mCallDiagnosticServiceController.isConnected()).thenReturn(true); + when(mCallDiagnosticServiceController.onCallDisconnected(any(Call.class), + any(DisconnectCause.class))).thenReturn(true); + mCallsManager.markCallAsDisconnected(callSpy, new DisconnectCause(DisconnectCause.ERROR)); + + verify(mCallDiagnosticServiceController).onCallDisconnected(any(Call.class), + any(DisconnectCause.class)); + verify(callSpy, never()).setDisconnectCause(any(DisconnectCause.class)); + } + private Call addSpyCall() { return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE); } diff --git a/tests/src/com/android/server/telecom/tests/CarModeTrackerTest.java b/tests/src/com/android/server/telecom/tests/CarModeTrackerTest.java index dbfcdb12a..4ad46ae52 100644 --- a/tests/src/com/android/server/telecom/tests/CarModeTrackerTest.java +++ b/tests/src/com/android/server/telecom/tests/CarModeTrackerTest.java @@ -21,9 +21,6 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertNull; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; - import android.app.UiModeManager; import com.android.server.telecom.CarModeTracker; @@ -33,7 +30,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -import org.mockito.Mock; @RunWith(JUnit4.class) public class CarModeTrackerTest extends TelecomTestCase { @@ -110,7 +106,7 @@ public class CarModeTrackerTest extends TelecomTestCase { @Test public void testForceExitCarMode() { testEnterCarModeBasic(); - mCarModeTracker.forceExitCarMode(CAR_MODE_APP1_PACKAGE_NAME); + mCarModeTracker.forceRemove(CAR_MODE_APP1_PACKAGE_NAME); assertFalse(mCarModeTracker.isInCarMode()); assertNull(mCarModeTracker.getCurrentCarModePackage()); } @@ -226,4 +222,109 @@ public class CarModeTrackerTest extends TelecomTestCase { CAR_MODE_APP3_PACKAGE_NAME); assertNull(mCarModeTracker.getCurrentCarModePackage()); } + + /** + * Verifies that setting automotive projection by itself works. + */ + @Test + public void testSetAutomotiveProjectionBasic() { + mCarModeTracker.handleSetAutomotiveProjection(CAR_MODE_APP1_PACKAGE_NAME); + assertEquals(CAR_MODE_APP1_PACKAGE_NAME, mCarModeTracker.getCurrentCarModePackage()); + // We should be tracking our car mode app. + assertEquals(1, mCarModeTracker.getCarModeApps().size()); + assertTrue(mCarModeTracker.isInCarMode()); + } + + /** + * Verifies that if we set automotive projection more than once with the same package, nothing + * changes. + */ + @Test + public void testSetAutomotiveProjectionMultipleTimes() { + mCarModeTracker.handleSetAutomotiveProjection(CAR_MODE_APP1_PACKAGE_NAME); + mCarModeTracker.handleSetAutomotiveProjection(CAR_MODE_APP1_PACKAGE_NAME); + // Should still only have one app. + assertEquals(1, mCarModeTracker.getCarModeApps().size()); + assertTrue(mCarModeTracker.isInCarMode()); + // It should be the same one. + assertEquals(CAR_MODE_APP1_PACKAGE_NAME, mCarModeTracker.getCurrentCarModePackage()); + } + + /** + * Verifies that if we set automotive projection more than once, the new package overrides. + */ + @Test + public void testSetAutomotiveProjectionMultipleTimesDifferentPackages() { + mCarModeTracker.handleSetAutomotiveProjection(CAR_MODE_APP1_PACKAGE_NAME); + mCarModeTracker.handleSetAutomotiveProjection(CAR_MODE_APP2_PACKAGE_NAME); + // Should still only have one app. + assertEquals(1, mCarModeTracker.getCarModeApps().size()); + assertTrue(mCarModeTracker.isInCarMode()); + // It should be the newer one. + assertEquals(CAR_MODE_APP2_PACKAGE_NAME, mCarModeTracker.getCurrentCarModePackage()); + } + + /** + * Verifies that releasing automotive projection works as expected. + */ + @Test + public void testReleaseAutomotiveProjectionBasic() { + // Releasing before something's set shouldn't break anything. + mCarModeTracker.handleReleaseAutomotiveProjection(); + assertEquals(0, mCarModeTracker.getCarModeApps().size()); + assertFalse(mCarModeTracker.isInCarMode()); + + mCarModeTracker.handleSetAutomotiveProjection(CAR_MODE_APP1_PACKAGE_NAME); + mCarModeTracker.handleReleaseAutomotiveProjection(); + // Should be gone now. + assertEquals(0, mCarModeTracker.getCarModeApps().size()); + assertFalse(mCarModeTracker.isInCarMode()); + } + + /** + * Verifies that setting automotive projection overrides but doesn't overwrite car mode apps. + */ + @Test + public void testAutomotiveProjectionOverridesCarMode() { + mCarModeTracker.handleEnterCarMode(50, CAR_MODE_APP1_PACKAGE_NAME); + mCarModeTracker.handleSetAutomotiveProjection(CAR_MODE_APP4_PACKAGE_NAME); + + // Should have two apps now, the car mode and the automotive projection one. + assertEquals(2, mCarModeTracker.getCarModeApps().size()); + assertTrue(mCarModeTracker.isInCarMode()); + + // Automotive projection takes priority. + assertEquals(CAR_MODE_APP4_PACKAGE_NAME, mCarModeTracker.getCurrentCarModePackage()); + + // If we add another car mode app, automotive projection still has priority. + mCarModeTracker.handleEnterCarMode(Integer.MAX_VALUE, CAR_MODE_APP2_PACKAGE_NAME); + assertEquals(3, mCarModeTracker.getCarModeApps().size()); + assertTrue(mCarModeTracker.isInCarMode()); + assertEquals(CAR_MODE_APP4_PACKAGE_NAME, mCarModeTracker.getCurrentCarModePackage()); + + // If we release automotive projection, we go back to the prioritized list of plain car + // mode apps. + mCarModeTracker.handleReleaseAutomotiveProjection(); + assertEquals(2, mCarModeTracker.getCarModeApps().size()); + assertTrue(mCarModeTracker.isInCarMode()); + assertEquals(CAR_MODE_APP2_PACKAGE_NAME, mCarModeTracker.getCurrentCarModePackage()); + + // Make sure we didn't mess with the first app that was added. + mCarModeTracker.handleExitCarMode(Integer.MAX_VALUE, CAR_MODE_APP2_PACKAGE_NAME); + assertEquals(1, mCarModeTracker.getCarModeApps().size()); + assertTrue(mCarModeTracker.isInCarMode()); + assertEquals(CAR_MODE_APP1_PACKAGE_NAME, mCarModeTracker.getCurrentCarModePackage()); + } + + /** + * Verifies that releasing automotive projection doesn't interfere with plain car mode apps. + */ + @Test + public void testReleaseAutomotiveProjectionNoopForCarModeApps() { + mCarModeTracker.handleEnterCarMode(50, CAR_MODE_APP1_PACKAGE_NAME); + mCarModeTracker.handleReleaseAutomotiveProjection(); + assertEquals(1, mCarModeTracker.getCarModeApps().size()); + assertTrue(mCarModeTracker.isInCarMode()); + assertEquals(CAR_MODE_APP1_PACKAGE_NAME, mCarModeTracker.getCurrentCarModePackage()); + } } diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java index ae8d943e2..ebb336e12 100644 --- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java +++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java @@ -30,7 +30,10 @@ import android.Manifest; import android.app.AppOpsManager; import android.app.NotificationManager; import android.app.StatusBarManager; +import android.app.UiModeManager; import android.app.role.RoleManager; +import android.content.AttributionSource; +import android.content.AttributionSourceState; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -47,6 +50,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.hardware.SensorPrivacyManager; import android.location.Country; import android.location.CountryDetector; import android.media.AudioManager; @@ -55,8 +59,11 @@ import android.os.Handler; import android.os.IInterface; import android.os.PersistableBundle; import android.os.PowerWhitelistManager; +import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.os.VibratorManager; +import android.permission.PermissionCheckerManager; import android.telecom.CallAudioState; import android.telecom.ConnectionService; import android.telecom.Log; @@ -68,6 +75,7 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.TelephonyRegistryManager; import android.test.mock.MockContext; +import android.util.DisplayMetrics; import java.io.File; import java.io.IOException; @@ -79,7 +87,9 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.Executor; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.matches; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; @@ -206,6 +216,14 @@ public class ComponentContextFixture implements TestFixture<Context> { return mRoleManager; case Context.TELEPHONY_REGISTRY_SERVICE: return mTelephonyRegistryManager; + case Context.UI_MODE_SERVICE: + return mUiModeManager; + case Context.VIBRATOR_MANAGER_SERVICE: + return mVibratorManager; + case Context.PERMISSION_CHECKER_SERVICE: + return mPermissionCheckerManager; + case Context.SENSOR_PRIVACY_SERVICE: + return mSensorPrivacyManager; default: return null; } @@ -227,6 +245,14 @@ public class ComponentContextFixture implements TestFixture<Context> { return Context.TELEPHONY_SUBSCRIPTION_SERVICE; } else if (svcClass == TelephonyRegistryManager.class) { return Context.TELEPHONY_REGISTRY_SERVICE; + } else if (svcClass == UiModeManager.class) { + return Context.UI_MODE_SERVICE; + } else if (svcClass == VibratorManager.class) { + return Context.VIBRATOR_MANAGER_SERVICE; + } else if (svcClass == PermissionCheckerManager.class) { + return Context.PERMISSION_CHECKER_SERVICE; + } else if (svcClass == SensorPrivacyManager.class) { + return Context.SENSOR_PRIVACY_SERVICE; } throw new UnsupportedOperationException(); } @@ -252,6 +278,11 @@ public class ComponentContextFixture implements TestFixture<Context> { } @Override + public AttributionSource getAttributionSource() { + return mAttributionSource; + } + + @Override public ContentResolver getContentResolver() { return new ContentResolver(mApplicationContextSpy) { @Override @@ -444,6 +475,10 @@ public class ComponentContextFixture implements TestFixture<Context> { } } + private static final String PACKAGE_NAME = "com.android.server.telecom.tests"; + private final AttributionSource mAttributionSource = + new AttributionSource.Builder(Process.myUid()).setPackageName(PACKAGE_NAME).build(); + private final Multimap<String, ComponentName> mComponentNamesByAction = ArrayListMultimap.create(); private final Map<ComponentName, IInterface> mServiceByComponentName = new HashMap<>(); @@ -474,6 +509,7 @@ public class ComponentContextFixture implements TestFixture<Context> { private final Resources.Theme mResourcesTheme = mock(Resources.Theme.class); private final Resources mResources = mock(Resources.class); private final Context mApplicationContextSpy = spy(mApplicationContext); + private final DisplayMetrics mDisplayMetrics = mock(DisplayMetrics.class); private final PackageManager mPackageManager = mock(PackageManager.class); private final Executor mMainExecutor = mock(Executor.class); private final AudioManager mAudioManager = spy(new FakeAudioManager(mContext)); @@ -491,7 +527,12 @@ public class ComponentContextFixture implements TestFixture<Context> { private final RoleManager mRoleManager = mock(RoleManager.class); private final TelephonyRegistryManager mTelephonyRegistryManager = mock(TelephonyRegistryManager.class); + private final VibratorManager mVibratorManager = mock(VibratorManager.class); + private final UiModeManager mUiModeManager = mock(UiModeManager.class); + private final PermissionCheckerManager mPermissionCheckerManager = + mock(PermissionCheckerManager.class); private final PermissionInfo mPermissionInfo = mock(PermissionInfo.class); + private final SensorPrivacyManager mSensorPrivacyManager = mock(SensorPrivacyManager.class); private TelecomManager mTelecomManager = mock(TelecomManager.class); @@ -500,6 +541,9 @@ public class ComponentContextFixture implements TestFixture<Context> { when(mResources.getConfiguration()).thenReturn(mResourceConfiguration); when(mResources.getString(anyInt())).thenReturn(""); when(mResources.getStringArray(anyInt())).thenReturn(new String[0]); + when(mResources.newTheme()).thenReturn(mResourcesTheme); + when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); + mDisplayMetrics.density = 3.125f; mResourceConfiguration.setLocale(Locale.TAIWAN); // TODO: Move into actual tests @@ -542,9 +586,10 @@ public class ComponentContextFixture implements TestFixture<Context> { }).when(mPackageManager).queryBroadcastReceiversAsUser((Intent) any(), anyInt(), anyInt()); // By default, tests use non-ui apps instead of 3rd party companion apps. - when(mPackageManager.checkPermission( - matches(Manifest.permission.CALL_COMPANION_APP), anyString())) - .thenReturn(PackageManager.PERMISSION_DENIED); + when(mPermissionCheckerManager.checkPermission( + matches(Manifest.permission.CALL_COMPANION_APP), any(AttributionSourceState.class), + nullable(String.class), anyBoolean(), anyBoolean(), anyBoolean(), anyInt())) + .thenReturn(PermissionCheckerManager.PERMISSION_HARD_DENIED); try { when(mPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( @@ -553,6 +598,7 @@ public class ComponentContextFixture implements TestFixture<Context> { } when(mPermissionInfo.isAppOp()).thenReturn(true); + when(mVibratorManager.getVibratorIds()).thenReturn(new int[0]); // Used in CreateConnectionProcessor to rank emergency numbers by viability. // For the test, make them all equal to INVALID so that the preferred PhoneAccount will be @@ -620,11 +666,15 @@ public class ComponentContextFixture implements TestFixture<Context> { serviceInfo.name = componentName.getClassName(); mServiceInfoByComponentName.put(componentName, serviceInfo); - // Used in InCallController to check permissions for CONTROL_INCALL_EXPERIENCE + // Used in InCallController to check permissions for CONTROL_INCALL_fvEXPERIENCE when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[] { componentName.getPackageName() }); when(mPackageManager.checkPermission(eq(Manifest.permission.CONTROL_INCALL_EXPERIENCE), eq(componentName.getPackageName()))).thenReturn(PackageManager.PERMISSION_GRANTED); + when(mPermissionCheckerManager.checkPermission( + eq(Manifest.permission.CONTROL_INCALL_EXPERIENCE), + any(AttributionSourceState.class), anyString(), anyBoolean(), anyBoolean(), + anyBoolean(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED); } public void addIntentReceiver(String action, ComponentName name) { @@ -667,6 +717,10 @@ public class ComponentContextFixture implements TestFixture<Context> { return mTelephonyManager; } + public NotificationManager getNotificationManager() { + return mNotificationManager; + } + private void addService(String action, ComponentName name, IInterface service) { mComponentNamesByAction.put(action, name); mServiceByComponentName.put(name, service); diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java index 5b4e80031..6e6646f7a 100755 --- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java +++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java @@ -34,6 +34,7 @@ import android.os.IInterface; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.telecom.CallAudioState; +import android.telecom.CallScreeningService; import android.telecom.Conference; import android.telecom.Connection; import android.telecom.ConnectionRequest; @@ -437,6 +438,10 @@ public class ConnectionServiceFixture implements TestFixture<IConnectionService> @Override public void handoverComplete(String callId, Session.Info sessionInfo) {} + + @Override + public void onCallFilteringCompleted(String callId, + Connection.CallFilteringCompletionInfo completionInfo, Session.Info sessionInfo) { } } FakeConnectionServiceDelegate mConnectionServiceDelegate; diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java index 0b926fe92..f66187835 100644 --- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java +++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java @@ -18,13 +18,16 @@ package com.android.server.telecom.tests; import static com.android.server.telecom.InCallController.IN_CALL_SERVICE_NOTIFICATION_ID; import static com.android.server.telecom.InCallController.NOTIFICATION_TAG; +import static com.android.server.telecom.tests.TelecomSystemTest.TEST_TIMEOUT; import static org.junit.Assert.assertEquals; 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.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.matches; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Matchers.any; @@ -41,14 +44,19 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationManager; import android.app.UiModeManager; +import android.content.AttributionSource; +import android.content.AttributionSourceState; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.PermissionChecker; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -61,7 +69,9 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.Process; import android.os.UserHandle; +import android.permission.PermissionCheckerManager; import android.telecom.CallAudioState; import android.telecom.InCallService; import android.telecom.ParcelableCall; @@ -72,10 +82,10 @@ import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.text.TextUtils; +import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.internal.telecom.IInCallAdapter; import com.android.internal.telecom.IInCallService; import com.android.server.telecom.Analytics; -import com.android.server.telecom.BluetoothHeadsetProxy; import com.android.server.telecom.Call; import com.android.server.telecom.CallsManager; import com.android.server.telecom.CarModeTracker; @@ -96,23 +106,27 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; import org.mockito.invocation.InvocationOnMock; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; @RunWith(JUnit4.class) public class InCallControllerTests extends TelecomTestCase { @Mock CallsManager mMockCallsManager; @Mock PhoneAccountRegistrar mMockPhoneAccountRegistrar; - @Mock BluetoothHeadsetProxy mMockBluetoothHeadset; @Mock SystemStateHelper mMockSystemStateHelper; @Mock PackageManager mMockPackageManager; + @Mock PermissionCheckerManager mMockPermissionCheckerManager; @Mock Call mMockCall; @Mock Resources mMockResources; @Mock AppOpsManager mMockAppOpsManager; @@ -178,8 +192,12 @@ public class InCallControllerTests extends TelecomTestCase { when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter); when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE))) .thenReturn(mNotificationManager); + when(mMockContext.getSystemService(eq(PermissionCheckerManager.class))) + .thenReturn(mMockPermissionCheckerManager); when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( mMockPermissionInfo); + when(mMockContext.getAttributionSource()).thenReturn(new AttributionSource(Process.myUid(), + "com.android.server.telecom.tests", null)); mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager, mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter, mEmergencyCallHelper, mCarModeTracker, mClockProxy); @@ -210,21 +228,37 @@ public class InCallControllerTests extends TelecomTestCase { } return null; }).when(mMockPackageManager).getPackagesForUid(anyInt()); - when(mMockPackageManager.checkPermission( + + when(mMockPermissionCheckerManager.checkPermission( matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), - matches(COMPANION_PKG))).thenReturn(PackageManager.PERMISSION_DENIED); - when(mMockPackageManager.checkPermission( + matchesAttributionSourcePackage(COMPANION_PKG), nullable(String.class), + anyBoolean(), anyBoolean(), anyBoolean(), anyInt())) + .thenReturn(PackageManager.PERMISSION_DENIED); + + when(mMockPermissionCheckerManager.checkPermission( matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), - matches(CAR_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED); - when(mMockPackageManager.checkPermission( + matchesAttributionSourcePackage(CAR_PKG), nullable(String.class), + anyBoolean(), anyBoolean(), anyBoolean(), anyInt())) + .thenReturn(PackageManager.PERMISSION_GRANTED); + + when(mMockPermissionCheckerManager.checkPermission( matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), - matches(CAR2_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED); - when(mMockPackageManager.checkPermission( + matchesAttributionSourcePackage(CAR2_PKG), nullable(String.class), + anyBoolean(), anyBoolean(), anyBoolean(), anyInt())) + .thenReturn(PackageManager.PERMISSION_GRANTED); + + when(mMockPermissionCheckerManager.checkPermission( matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), - matches(NONUI_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED); - when(mMockPackageManager.checkPermission( + matchesAttributionSourcePackage(NONUI_PKG), nullable(String.class), + anyBoolean(), anyBoolean(), anyBoolean(), anyInt())) + .thenReturn(PackageManager.PERMISSION_GRANTED); + + when(mMockPermissionCheckerManager.checkPermission( matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), - matches(APPOP_NONUI_PKG))).thenReturn(PackageManager.PERMISSION_DENIED); + matchesAttributionSourcePackage(APPOP_NONUI_PKG), nullable(String.class), + anyBoolean(), anyBoolean(), anyBoolean(), anyInt())) + .thenReturn(PackageManager.PERMISSION_DENIED); + when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0)); } @@ -243,14 +277,53 @@ public class InCallControllerTests extends TelecomTestCase { when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); - when(mMockSystemStateHelper.isCarMode()).thenReturn(true); + when(mMockSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(true); + + mSystemStateListener.onCarModeChanged(666, CAR_PKG, true); + verify(mCarModeTracker).handleEnterCarMode(666, CAR_PKG); + assertTrue(mCarModeTracker.isInCarMode()); + + mSystemStateListener.onPackageUninstalled(CAR_PKG); + verify(mCarModeTracker).forceRemove(CAR_PKG); + assertFalse(mCarModeTracker.isInCarMode()); + } + + /** + * Ensure that if we remove a random unrelated app we don't exit car mode. + */ + @SmallTest + @Test + public void testRandomAppRemovalInCarMode() { + setupMockPackageManager(true /* default */, true /* system */, true /* external calls */); + when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + + when(mMockSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(true); mSystemStateListener.onCarModeChanged(666, CAR_PKG, true); verify(mCarModeTracker).handleEnterCarMode(666, CAR_PKG); assertTrue(mCarModeTracker.isInCarMode()); + mSystemStateListener.onPackageUninstalled("com.foo.test"); + verify(mCarModeTracker, never()).forceRemove(CAR_PKG); + assertTrue(mCarModeTracker.isInCarMode()); + } + + @SmallTest + @Test + public void testAutomotiveProjectionAppRemoval() { + setupMockPackageManager(true /* default */, true /* system */, true /* external calls */); + when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + + when(mMockSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(true); + + mSystemStateListener.onAutomotiveProjectionStateSet(CAR_PKG); + verify(mCarModeTracker).handleSetAutomotiveProjection(CAR_PKG); + assertTrue(mCarModeTracker.isInCarMode()); + mSystemStateListener.onPackageUninstalled(CAR_PKG); - verify(mCarModeTracker).forceExitCarMode(CAR_PKG); + verify(mCarModeTracker).forceRemove(CAR_PKG); assertFalse(mCarModeTracker.isInCarMode()); } @@ -439,7 +512,7 @@ public class InCallControllerTests extends TelecomTestCase { // Pretend that the call has gone away. when(mMockCallsManager.getCalls()).thenReturn(Collections.emptyList()); mInCallController.onCallRemoved(mMockCall); - waitForHandlerAction(new Handler(Looper.getMainLooper()), TelecomSystemTest.TEST_TIMEOUT); + waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT); verify(mMockPackageManager).revokeRuntimePermission(eq(SYS_PKG), eq(Manifest.permission.ACCESS_FINE_LOCATION), eq(mUserHandle)); @@ -786,7 +859,7 @@ public class InCallControllerTests extends TelecomTestCase { setupMockPackageManager(true /* default */, true /* system */, true /* external calls */); // Enable car mode - when(mMockSystemStateHelper.isCarMode()).thenReturn(true); + when(mMockSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(true); mInCallController.handleCarModeChange(UiModeManager.DEFAULT_PRIORITY, CAR_PKG, true); // Now bind; we should only bind to one app. @@ -816,7 +889,7 @@ public class InCallControllerTests extends TelecomTestCase { matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE), matches(CAR_PKG))).thenReturn(PackageManager.PERMISSION_DENIED); // Enable car mode - when(mMockSystemStateHelper.isCarMode()).thenReturn(true); + when(mMockSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(true); // Register the fact that the invalid app entered car mode. mInCallController.handleCarModeChange(UiModeManager.DEFAULT_PRIORITY, CAR_PKG, true); @@ -841,39 +914,45 @@ public class InCallControllerTests extends TelecomTestCase { @MediumTest @Test public void testBindToService_ThirdPartyApp() throws Exception { - setupMocks(false /* isExternalCall */); - setupMockPackageManager(false /* default */, false /* nonui */, true /* appop_nonui */, - true /* system */, false /* external calls */, false /* self mgd in default */, - false /* self mgd in car*/); - - // Enable Third Party Companion App - when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn( - mMockPermissionInfo); - when(mMockPermissionInfo.isAppOp()).thenReturn(true); - when(mMockAppOpsManager.unsafeCheckOpRawNoThrow(matches( - AppOpsManager.OPSTR_MANAGE_ONGOING_CALLS), eq(APPOP_NONUI_UID), - matches(APPOP_NONUI_PKG))).thenReturn(AppOpsManager.MODE_ALLOWED); - - // Now bind; we should bind to the system dialer and app op non ui app. - mInCallController.bindToServices(mMockCall); - - // Bind InCallServices - ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mMockContext, times(2)).bindServiceAsUser( - bindIntentCaptor.capture(), - any(ServiceConnection.class), - eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE - | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS), - eq(UserHandle.CURRENT)); - - // Verify bind - assertEquals(2, bindIntentCaptor.getAllValues().size()); - - // Should have first bound to the system dialer. - verifyBinding(bindIntentCaptor, 0, SYS_PKG, SYS_CLASS); - - // Should have next bound to the third party app op non ui app. - verifyBinding(bindIntentCaptor, 1, APPOP_NONUI_PKG, APPOP_NONUI_CLASS); + final MockitoSession mockitoSession = ExtendedMockito.mockitoSession() + .strictness(Strictness.WARN) + .spyStatic(PermissionChecker.class) + .startMocking(); + try { + setupMocks(false /* isExternalCall */); + setupMockPackageManager(false /* default */, false /* nonui */, true /* appop_nonui */, + true /* system */, false /* external calls */, false /* self mgd in default */, + false /* self mgd in car*/); + + // Enable Third Party Companion App + ExtendedMockito.doReturn(PermissionChecker.PERMISSION_GRANTED).when(() -> + PermissionChecker.checkPermissionForDataDeliveryFromDataSource( + any(Context.class), eq(Manifest.permission.MANAGE_ONGOING_CALLS), + anyInt(), any(AttributionSource.class), nullable(String.class))); + + // Now bind; we should bind to the system dialer and app op non ui app. + mInCallController.bindToServices(mMockCall); + + // Bind InCallServices + ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mMockContext, times(2)).bindServiceAsUser( + bindIntentCaptor.capture(), + any(ServiceConnection.class), + eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE + | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS), + eq(UserHandle.CURRENT)); + + // Verify bind + assertEquals(2, bindIntentCaptor.getAllValues().size()); + + // Should have first bound to the system dialer. + verifyBinding(bindIntentCaptor, 0, SYS_PKG, SYS_CLASS); + + // Should have next bound to the third party app op non ui app. + verifyBinding(bindIntentCaptor, 1, APPOP_NONUI_PKG, APPOP_NONUI_CLASS); + } finally { + mockitoSession.finishMocking(); + } } /** @@ -951,6 +1030,31 @@ public class InCallControllerTests extends TelecomTestCase { */ @MediumTest @Test + public void testRandomAppRemovalWhenNotInCarMode() throws Exception { + setupMocks(true /* isExternalCall */); + setupMockPackageManager(true /* default */, true /* system */, true /* external calls */); + // Bind to default dialer. + mInCallController.bindToServices(mMockCall); + + // Uninstall an unrelated app. + mSystemStateListener.onPackageUninstalled("com.joe.stuff"); + + // Bind InCallServices, just once; we should not re-bind to the same app. + ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mMockContext).bindServiceAsUser( + bindIntentCaptor.capture(), + any(ServiceConnection.class), + eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE + | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS), + eq(UserHandle.CURRENT)); + } + + /** + * Ensures that the {@link InCallController} will bind to a higher priority car mode service + * when one becomes available. + */ + @MediumTest + @Test public void testCarmodeRebindHigherPriority() throws Exception { setupMocks(true /* isExternalCall */); setupMockPackageManager(true /* default */, true /* system */, true /* external calls */); @@ -958,7 +1062,7 @@ public class InCallControllerTests extends TelecomTestCase { mInCallController.bindToServices(mMockCall); // Enable car mode and enter car mode at default priority. - when(mMockSystemStateHelper.isCarMode()).thenReturn(true); + when(mMockSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(true); mInCallController.handleCarModeChange(UiModeManager.DEFAULT_PRIORITY, CAR_PKG, true); // And change to the second car mode app. @@ -1105,7 +1209,7 @@ public class InCallControllerTests extends TelecomTestCase { // Now switch to car mode. // Enable car mode and enter car mode at default priority. - when(mMockSystemStateHelper.isCarMode()).thenReturn(true); + when(mMockSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(true); mInCallController.handleCarModeChange(UiModeManager.DEFAULT_PRIORITY, CAR_PKG, true); ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class); @@ -1145,7 +1249,7 @@ public class InCallControllerTests extends TelecomTestCase { // Now switch to car mode. // Enable car mode and enter car mode at default priority. - when(mMockSystemStateHelper.isCarMode()).thenReturn(true); + when(mMockSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(true); mInCallController.handleCarModeChange(UiModeManager.DEFAULT_PRIORITY, CAR_PKG, true); // We currently will bind to the car-mode InCallService even if there are no calls available @@ -1379,6 +1483,28 @@ public class InCallControllerTests extends TelecomTestCase { } }).when(mMockPackageManager).queryIntentServicesAsUser( any(Intent.class), anyInt(), eq(CURRENT_USER_ID)); + + if (useDefaultDialer) { + when(mMockPackageManager + .getComponentEnabledSetting(new ComponentName(DEF_PKG, DEF_CLASS))) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + } + + when(mMockPackageManager + .getComponentEnabledSetting(new ComponentName(SYS_PKG, SYS_CLASS))) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + + when(mMockPackageManager + .getComponentEnabledSetting(new ComponentName(CAR_PKG, CAR_CLASS))) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + + when(mMockPackageManager + .getComponentEnabledSetting(new ComponentName(COMPANION_PKG, COMPANION_CLASS))) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + + when(mMockPackageManager + .getComponentEnabledSetting(new ComponentName(CAR2_PKG, CAR2_CLASS))) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED); } private void setupMockPackageManagerLocationPermission(final String pkg, @@ -1388,4 +1514,24 @@ public class InCallControllerTests extends TelecomTestCase { ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); } + + private static AttributionSourceState matchesAttributionSourcePackage( + @Nullable String packageName) { + return argThat(new PackageNameArgumentMatcher(packageName)); + } + + private static class PackageNameArgumentMatcher implements + ArgumentMatcher<AttributionSourceState> { + @Nullable + private final String mPackgeName; + + PackageNameArgumentMatcher(@Nullable String packageName) { + mPackgeName = packageName; + } + + @Override + public boolean matches(@NonNull AttributionSourceState attributionSource) { + return Objects.equals(mPackgeName, attributionSource.packageName); + } + } } diff --git a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java index db44dcd4b..2b05430c5 100644 --- a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java +++ b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java @@ -29,6 +29,8 @@ import android.content.Context; import android.content.IContentProvider; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; @@ -65,11 +67,13 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; @@ -79,6 +83,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -132,6 +137,7 @@ public class MissedCallNotifierImplTest extends TelecomTestCase { } } + private static final long TIMEOUT_DELAY = 5000; private static final Uri TEL_CALL_HANDLE = Uri.parse("tel:+11915552620"); private static final Uri SIP_CALL_HANDLE = Uri.parse("sip:testaddress@testdomain.com"); private static final String CALLER_NAME = "Fake Name"; @@ -139,6 +145,7 @@ public class MissedCallNotifierImplTest extends TelecomTestCase { private static final String MISSED_CALLS_TITLE = "Missed Calls"; private static final String MISSED_CALLS_MSG = "%s missed calls"; private static final String USER_CALL_ACTIVITY_LABEL = "Phone"; + private static final String DEFAULT_DIALER_PACKAGE = "com.android.server.telecom.test"; private static final int REQUEST_ID = 0; private static final long CALL_TIMESTAMP; @@ -213,6 +220,49 @@ public class MissedCallNotifierImplTest extends TelecomTestCase { cancelNotificationTestInternal(SECONARY_USER); } + @SmallTest + @Test + public void testDefaultDialerClear() { + MissedCallNotifier missedCallNotifier = setupMissedCallNotificationThroughDefaultDialer(); + missedCallNotifier.clearMissedCalls(PRIMARY_USER); + + ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).sendBroadcastAsUser(intentArgumentCaptor.capture(), any(), + anyString(), any()); + Intent sentIntent = intentArgumentCaptor.getValue(); + assertEquals(0, sentIntent.getIntExtra(TelecomManager.EXTRA_NOTIFICATION_COUNT, -1)); + } + + @SmallTest + @Test + public void testDefaultDialerIncrement() { + MissedCallNotifier missedCallNotifier = setupMissedCallNotificationThroughDefaultDialer(); + PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY); + MissedCallNotifier.CallInfo fakeCall = makeFakeCallInfo(TEL_CALL_HANDLE, CALLER_NAME, + CALL_TIMESTAMP, phoneAccount.getAccountHandle()); + + missedCallNotifier.showMissedCallNotification(fakeCall); + ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).sendBroadcastAsUser(intentArgumentCaptor.capture(), any(), + anyString(), any()); + + Intent sentIntent = intentArgumentCaptor.getValue(); + assertEquals(1, sentIntent.getIntExtra(TelecomManager.EXTRA_NOTIFICATION_COUNT, -1)); + } + + private MissedCallNotifier setupMissedCallNotificationThroughDefaultDialer() { + mComponentContextFixture.addIntentReceiver( + TelecomManager.ACTION_SHOW_MISSED_CALLS_NOTIFICATION, COMPONENT_NAME); + when(mDefaultDialerCache.getDefaultDialerApplication(anyInt())).thenReturn( + DEFAULT_DIALER_PACKAGE); + + Notification.Builder builder1 = makeNotificationBuilder("builder1"); + Notification.Builder builder2 = makeNotificationBuilder("builder2"); + MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory = + makeNotificationBuilderFactory(builder1, builder1, builder2, builder2); + return makeMissedCallNotifier(fakeBuilderFactory, PRIMARY_USER); + } + private void cancelNotificationTestInternal(UserHandle userHandle) { Notification.Builder builder1 = makeNotificationBuilder("builder1"); Notification.Builder builder2 = makeNotificationBuilder("builder2"); @@ -458,7 +508,7 @@ public class MissedCallNotifierImplTest extends TelecomTestCase { CallLog.Calls.PRESENTATION_ALLOWED, CALL_TIMESTAMP) .build(); - when(cp.query(anyString(), nullable(String.class), eq(queryUri), nullable(String[].class), + when(cp.query(any(), eq(queryUri), nullable(String[].class), nullable(Bundle.class), nullable(ICancellationSignal.class))) .thenReturn(mockMissedCallsCursor); @@ -528,7 +578,7 @@ public class MissedCallNotifierImplTest extends TelecomTestCase { PRIMARY_USER.getIdentifier()); IContentProvider cp = getContentProviderForUser(PRIMARY_USER.getIdentifier()); - when(cp.query(anyString(), nullable(String.class), eq(queryUri), nullable(String[].class), + when(cp.query(any(), eq(queryUri), nullable(String[].class), nullable(Bundle.class), nullable(ICancellationSignal.class))) .thenReturn(mockMissedCallsCursor); @@ -611,7 +661,7 @@ public class MissedCallNotifierImplTest extends TelecomTestCase { assertNotNull("Not expecting null options bundle", bundleCaptor.getValue()); BroadcastOptions options = new BroadcastOptions(bundleCaptor.getValue()); assertTrue("App must have a temporary exemption set.", - options.getTemporaryAppWhitelistDuration() > 0); + options.getTemporaryAppAllowlistDuration() > 0); // A notification should never be posted by Telecom verify(mNotificationManager, never()).notifyAsUser(nullable(String.class), anyInt(), diff --git a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java index a5b78b7c3..e6c6bacf4 100644 --- a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java +++ b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java @@ -110,7 +110,7 @@ public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase { when(mPhoneAccountRegistrar.getPhoneAccountUnchecked( any(PhoneAccountHandle.class))).thenReturn(mPhoneAccount); when(mPhoneAccount.isSelfManaged()).thenReturn(true); - when(mSystemStateHelper.isCarMode()).thenReturn(false); + when(mSystemStateHelper.isCarModeOrProjectionActive()).thenReturn(false); } @Override diff --git a/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java index 6c941fe4d..a50328386 100644 --- a/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java +++ b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java @@ -1,13 +1,12 @@ package com.android.server.telecom.tests; -import static com.android.server.telecom.TelecomSystem.*; +import static com.android.server.telecom.TelecomSystem.SyncRoot; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import android.content.ComponentName; @@ -17,6 +16,7 @@ import android.os.SystemClock; import android.telecom.Connection; import android.telecom.ParcelableCall; import android.telecom.PhoneAccountHandle; +import android.telephony.ims.ImsCallProfile; import android.test.suitebuilder.annotation.SmallTest; import com.android.server.telecom.Call; @@ -26,7 +26,6 @@ import com.android.server.telecom.ClockProxy; import com.android.server.telecom.ParcelableCallUtils; import com.android.server.telecom.PhoneAccountRegistrar; import com.android.server.telecom.PhoneNumberUtilsAdapter; -import com.android.server.telecom.TelecomSystem; import com.android.server.telecom.ui.ToastFactory; import org.junit.After; @@ -98,6 +97,7 @@ public class ParcelableCallUtilsTest extends TelecomTestCase { Bundle parceledExtras = call.getExtras(); assertFalse(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE)); + assertTrue(parceledExtras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL)); assertFalse(parceledExtras.containsKey("SomeExtra")); assertTrue(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)); } @@ -115,6 +115,7 @@ public class ParcelableCallUtilsTest extends TelecomTestCase { Bundle parceledExtras = call.getExtras(); assertTrue(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE)); + assertTrue(parceledExtras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL)); assertTrue(parceledExtras.containsKey("SomeExtra")); assertTrue(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)); } @@ -128,6 +129,7 @@ public class ParcelableCallUtilsTest extends TelecomTestCase { Bundle parceledExtras = call.getExtras(); assertTrue(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE)); + assertTrue(parceledExtras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL)); assertFalse(parceledExtras.containsKey("SomeExtra")); assertFalse(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)); } @@ -141,6 +143,7 @@ public class ParcelableCallUtilsTest extends TelecomTestCase { Bundle parceledExtras = call.getExtras(); assertFalse(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE)); + assertFalse(parceledExtras.containsKey(ImsCallProfile.EXTRA_IS_BUSINESS_CALL)); assertFalse(parceledExtras.containsKey("SomeExtra")); assertFalse(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)); } @@ -192,6 +195,7 @@ public class ParcelableCallUtilsTest extends TelecomTestCase { extras.putString(Connection.EXTRA_SIP_INVITE, "scary data"); extras.putString("SomeExtra", "Extra Extra"); extras.putString(Connection.EXTRA_CALL_SUBJECT, "Blah"); + extras.putBoolean(ImsCallProfile.EXTRA_IS_BUSINESS_CALL, true); return extras; } } diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java index 38f63d2d2..0e93481cb 100644 --- a/tests/src/com/android/server/telecom/tests/RingerTest.java +++ b/tests/src/com/android/server/telecom/tests/RingerTest.java @@ -79,6 +79,16 @@ public class RingerTest extends TelecomTestCase { } @Override + public VibrationEffect resolve(int defaultAmplitude) { + return this; + } + + @Override + public VibrationEffect scale(float scaleFactor) { + return this; + } + + @Override public void validate() { // not needed } diff --git a/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java b/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java index 7113148da..dc7d1fd8a 100644 --- a/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java +++ b/tests/src/com/android/server/telecom/tests/SystemStateHelperTest.java @@ -61,6 +61,7 @@ import org.mockito.MockitoAnnotations; import org.mockito.internal.util.reflection.FieldSetter; import java.util.List; +import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -93,6 +94,8 @@ public class SystemStateHelperTest extends TelecomTestCase { when(mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)).thenReturn(mGravitySensor); when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(mProxSensor); + doReturn(mUiModeManager).when(mContext).getSystemService(UiModeManager.class); + mComponentContextFixture.putFloatResource( R.dimen.device_on_ear_xy_gravity_threshold, 5.5f); mComponentContextFixture.putFloatResource( @@ -119,17 +122,53 @@ public class SystemStateHelperTest extends TelecomTestCase { @SmallTest @Test public void testQuerySystemForCarMode_True() { - when(mContext.getSystemService(Context.UI_MODE_SERVICE)).thenReturn(mUiModeManager); when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR); - assertTrue(new SystemStateHelper(mContext, mLock).isCarMode()); + assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive()); } @SmallTest @Test public void testQuerySystemForCarMode_False() { - when(mContext.getSystemService(Context.UI_MODE_SERVICE)).thenReturn(mUiModeManager); when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_NORMAL); - assertFalse(new SystemStateHelper(mContext, mLock).isCarMode()); + assertFalse(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive()); + } + + @SmallTest + @Test + public void testQuerySystemForAutomotiveProjection_True() { + when(mUiModeManager.getActiveProjectionTypes()) + .thenReturn(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE); + assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive()); + + when(mUiModeManager.getActiveProjectionTypes()) + .thenReturn(UiModeManager.PROJECTION_TYPE_ALL); + assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive()); + } + + @SmallTest + @Test + public void testQuerySystemForAutomotiveProjection_False() { + when(mUiModeManager.getActiveProjectionTypes()) + .thenReturn(UiModeManager.PROJECTION_TYPE_NONE); + assertFalse(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive()); + } + + @SmallTest + @Test + public void testQuerySystemForAutomotiveProjectionAndCarMode_True() { + when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR); + when(mUiModeManager.getActiveProjectionTypes()) + .thenReturn(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE); + assertTrue(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive()); + } + + @SmallTest + @Test + public void testQuerySystemForAutomotiveProjectionOrCarMode_nullService() { + when(mContext.getSystemService(UiModeManager.class)) + .thenReturn(mUiModeManager) // Without this, class construction will throw NPE. + .thenReturn(null); + assertFalse(new SystemStateHelper(mContext, mLock).isCarModeOrProjectionActive()); } @SmallTest @@ -206,6 +245,40 @@ public class SystemStateHelperTest extends TelecomTestCase { @SmallTest @Test + public void testOnSetReleaseAutomotiveProjection() { + SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock); + // We don't care what listener is registered, that's an implementation detail, but we need + // to call methods on whatever it is. + ArgumentCaptor<UiModeManager.OnProjectionStateChangedListener> listenerCaptor = + ArgumentCaptor.forClass(UiModeManager.OnProjectionStateChangedListener.class); + verify(mUiModeManager).addOnProjectionStateChangedListener( + eq(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE), any(), listenerCaptor.capture()); + systemStateHelper.addListener(mSystemStateListener); + + String packageName1 = "Sufjan Stevens"; + String packageName2 = "The Ascension"; + + // Should pay attention to automotive projection, though. + listenerCaptor.getValue().onProjectionStateChanged( + UiModeManager.PROJECTION_TYPE_AUTOMOTIVE, Set.of(packageName2)); + verify(mSystemStateListener).onAutomotiveProjectionStateSet(packageName2); + + // Without any automotive projection, it should see it as released. + listenerCaptor.getValue().onProjectionStateChanged( + UiModeManager.PROJECTION_TYPE_NONE, Set.of()); + verify(mSystemStateListener).onAutomotiveProjectionStateReleased(); + + // Try the whole thing again, with different values. + listenerCaptor.getValue().onProjectionStateChanged( + UiModeManager.PROJECTION_TYPE_AUTOMOTIVE, Set.of(packageName1)); + verify(mSystemStateListener).onAutomotiveProjectionStateSet(packageName1); + listenerCaptor.getValue().onProjectionStateChanged( + UiModeManager.PROJECTION_TYPE_AUTOMOTIVE, Set.of()); + verify(mSystemStateListener, times(2)).onAutomotiveProjectionStateReleased(); + } + + @SmallTest + @Test public void testDeviceOnEarCorrectlyDetected() { doAnswer(invocation -> { SensorEventListener listener = invocation.getArgument(0); diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java index 0dfe29a08..3cec50b78 100644 --- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java @@ -460,10 +460,10 @@ public class TelecomServiceImplTest extends TelecomTestCase { @Test public void testGetPhoneAccount() throws RemoteException { makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17); - assertEquals(TEL_PA_HANDLE_16, mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_16) - .getAccountHandle()); - assertEquals(SIP_PA_HANDLE_17, mTSIBinder.getPhoneAccount(SIP_PA_HANDLE_17) - .getAccountHandle()); + assertEquals(TEL_PA_HANDLE_16, mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_16, + mContext.getPackageName()).getAccountHandle()); + assertEquals(SIP_PA_HANDLE_17, mTSIBinder.getPhoneAccount(SIP_PA_HANDLE_17, + mContext.getPackageName()).getAccountHandle()); } @SmallTest @@ -1202,6 +1202,39 @@ public class TelecomServiceImplTest extends TelecomTestCase { } /** + * Ensure self-managed calls cannot be ended using {@link TelecomManager#endCall()}. + * @throws Exception + */ + @SmallTest + @Test + public void testCannotEndSelfManagedCall() throws Exception { + Call call = mock(Call.class); + when(call.isSelfManaged()).thenReturn(true); + when(call.getState()).thenReturn(CallState.ACTIVE); + when(mFakeCallsManager.getFirstCallWithState(any())) + .thenReturn(call); + assertFalse(mTSIBinder.endCall(TEST_PACKAGE)); + verify(mFakeCallsManager, never()).disconnectCall(eq(call)); + } + + /** + * Ensure self-managed calls cannot be answered using {@link TelecomManager#acceptRingingCall()} + * or {@link TelecomManager#acceptRingingCall(int)}. + * @throws Exception + */ + @SmallTest + @Test + public void testCannotAnswerSelfManagedCall() throws Exception { + Call call = mock(Call.class); + when(call.isSelfManaged()).thenReturn(true); + when(call.getState()).thenReturn(CallState.ACTIVE); + when(mFakeCallsManager.getFirstCallWithState(any())) + .thenReturn(call); + mTSIBinder.acceptRingingCall(TEST_PACKAGE); + verify(mFakeCallsManager, never()).answerCall(eq(call), anyInt()); + } + + /** * Register phone accounts for the supplied PhoneAccountHandles to make them * visible to all users (via the isVisibleToCaller method in TelecomServiceImpl. * @param handles the handles for which phone accounts should be created for. |
