summaryrefslogtreecommitdiff
path: root/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java')
-rw-r--r--tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java985
1 files changed, 926 insertions, 59 deletions
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index f6f2ae283..8bc1f2a4d 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -18,11 +18,13 @@ package com.android.server.telecom.tests;
import static android.Manifest.permission.CALL_PHONE;
import static android.Manifest.permission.CALL_PRIVILEGED;
+import static android.Manifest.permission.MANAGE_OWN_CALLS;
import static android.Manifest.permission.MODIFY_PHONE_STATE;
import static android.Manifest.permission.READ_PHONE_NUMBERS;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.ComponentName;
@@ -31,13 +33,16 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.telecom.CallAttributes;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -45,7 +50,9 @@ import android.telecom.VideoProfile;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.telecom.ICallEventCallback;
import com.android.internal.telecom.ITelecomService;
+import com.android.server.telecom.AnomalyReporterAdapter;
import com.android.server.telecom.Call;
import com.android.server.telecom.CallIntentProcessor;
import com.android.server.telecom.CallState;
@@ -56,6 +63,9 @@ import com.android.server.telecom.TelecomServiceImpl;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.components.UserCallIntentProcessor;
import com.android.server.telecom.components.UserCallIntentProcessorFactory;
+import com.android.server.telecom.voip.IncomingCallTransaction;
+import com.android.server.telecom.voip.OutgoingCallTransaction;
+import com.android.server.telecom.voip.TransactionManager;
import org.junit.After;
import org.junit.Before;
@@ -66,7 +76,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
@@ -78,6 +87,7 @@ import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.nullable;
@@ -94,13 +104,18 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.isA;
import static org.mockito.Mockito.when;
@RunWith(JUnit4.class)
public class TelecomServiceImplTest extends TelecomTestCase {
+ private static final String CALLING_PACKAGE = TelecomServiceImplTest.class.getPackageName();
+ private static final String TEST_NAME = "Alan Turing";
+ private static final Uri TEST_URI = Uri.fromParts("tel", "abc", "123");
public static final String TEST_PACKAGE = "com.test";
public static final String PACKAGE_NAME = "test";
@@ -174,6 +189,9 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@Mock private UserCallIntentProcessor mUserCallIntentProcessor;
private PackageManager mPackageManager;
@Mock private ApplicationInfo mApplicationInfo;
+ @Mock private ICallEventCallback mICallEventCallback;
+ @Mock private TransactionManager mTransactionManager;
+ @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
@@ -203,6 +221,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
doReturn(mContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class),
anyString());
+ when(mContext.checkCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
doAnswer(invocation -> {
mDefaultDialerObserver = invocation.getArgument(1);
return null;
@@ -223,6 +243,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
mSubscriptionManagerAdapter,
mSettingsSecureAdapter,
mLock);
+ telecomServiceImpl.setTransactionManager(mTransactionManager);
+ telecomServiceImpl.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
mTSIBinder = telecomServiceImpl.getBinder();
mComponentContextFixture.setTelecomManager(mTelecomManager);
when(mTelecomManager.getDefaultDialerPackage()).thenReturn(DEFAULT_DIALER_PACKAGE);
@@ -271,6 +293,51 @@ public class TelecomServiceImplTest extends TelecomTestCase {
assertEquals(SIP_PA_HANDLE_17, returnedHandleSip);
}
+ /**
+ * Clear the groupId from the PhoneAccount if a package does NOT have MODIFY_PHONE_STATE
+ */
+ @SmallTest
+ @Test
+ public void testGroupIdIsClearedWhenPermissionIsMissing() throws RemoteException {
+ // GIVEN
+ PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_CURRENT)
+ .setGroupId("testId")
+ .build();
+ // WHEN
+ doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+ eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class), anyBoolean());
+ doNothing().when(mAppOpsManager).checkPackage(anyInt(), anyString());
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingPermission(MODIFY_PHONE_STATE);
+ // THEN
+ PhoneAccount account =
+ mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_CURRENT, PACKAGE_NAME);
+ assertEquals("***", account.getGroupId());
+ }
+
+ /**
+ * Ensure groupId is not cleared if a package has MODIFY_PHONE_STATE
+ */
+ @SmallTest
+ @Test
+ public void testGroupIdIsNotCleared() throws RemoteException {
+ // GIVEN
+ final String groupId = "testId";
+ PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_CURRENT)
+ .setGroupId(groupId)
+ .build();
+ // WHEN
+ doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+ eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class), anyBoolean());
+ doNothing().when(mAppOpsManager).checkPackage(anyInt(), anyString());
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .when(mContext).checkCallingPermission(MODIFY_PHONE_STATE);
+ // THEN
+ PhoneAccount account =
+ mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_CURRENT, DEFAULT_DIALER_PACKAGE);
+ assertEquals(groupId, account.getGroupId());
+ }
+
@SmallTest
@Test
public void testGetDefaultOutgoingPhoneAccountSucceedsIfCallerIsSimCallManager()
@@ -354,19 +421,90 @@ public class TelecomServiceImplTest extends TelecomTestCase {
.setUserSelectedOutgoingPhoneAccount(eq(TEL_PA_HANDLE_16), any(UserHandle.class));
}
+ @Test
+ public void testAddCallWithOutgoingCall() throws RemoteException {
+ // GIVEN
+ CallAttributes mOutgoingCallAttributes = new CallAttributes.Builder(TEL_PA_HANDLE_CURRENT,
+ CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI)
+ .setCallType(CallAttributes.AUDIO_CALL)
+ .setCallCapabilities(CallAttributes.SUPPORTS_SET_INACTIVE)
+ .build();
+ PhoneAccount phoneAccount = makeMultiUserPhoneAccount(TEL_PA_HANDLE_CURRENT).build();
+ phoneAccount.setIsEnabled(true);
+
+ // WHEN
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ phoneAccount);
+
+ doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+ eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class));
+
+ mTSIBinder.addCall(mOutgoingCallAttributes, mICallEventCallback, "1", CALLING_PACKAGE);
+
+ // THEN
+ verify(mTransactionManager, times(1))
+ .addTransaction(isA(OutgoingCallTransaction.class), isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testAddCallWithIncomingCall() throws RemoteException {
+ // GIVEN
+ CallAttributes mIncomingCallAttributes = new CallAttributes.Builder(TEL_PA_HANDLE_CURRENT,
+ CallAttributes.DIRECTION_INCOMING, TEST_NAME, TEST_URI)
+ .setCallType(CallAttributes.AUDIO_CALL)
+ .setCallCapabilities(CallAttributes.SUPPORTS_SET_INACTIVE)
+ .build();
+ PhoneAccount phoneAccount = makeMultiUserPhoneAccount(TEL_PA_HANDLE_CURRENT).build();
+ phoneAccount.setIsEnabled(true);
+
+ // WHEN
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ phoneAccount);
+
+ doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+ eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class));
+
+ mTSIBinder.addCall(mIncomingCallAttributes, mICallEventCallback, "1", CALLING_PACKAGE);
+
+ // THEN
+ verify(mTransactionManager, times(1))
+ .addTransaction(isA(IncomingCallTransaction.class), isA(OutcomeReceiver.class));
+ }
+
+ @Test
+ public void testAddCallWithManagedPhoneAccount() throws RemoteException {
+ // GIVEN
+ CallAttributes attributes = new CallAttributes.Builder(TEL_PA_HANDLE_CURRENT,
+ CallAttributes.DIRECTION_OUTGOING, TEST_NAME, TEST_URI).build();
+ PhoneAccount phoneAccount = makeMultiUserPhoneAccount(TEL_PA_HANDLE_CURRENT)
+ .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
+ .build();
+ phoneAccount.setIsEnabled(true);
+
+ // WHEN
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ phoneAccount);
+
+ doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+ eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class));
+
+ // THEN
+ try {
+ mTSIBinder.addCall(attributes, mICallEventCallback, "1", CALLING_PACKAGE);
+ fail("should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // pass
+ }
+ }
+
@SmallTest
@Test
- public void testSetUserSelectedOutgoingPhoneAccountFailure() throws RemoteException {
+ public void testSetUserSelectedOutgoingPhoneAccountWithoutPermission() throws RemoteException {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
anyString(), nullable(String.class));
- try {
- mTSIBinder.setUserSelectedOutgoingPhoneAccount(TEL_PA_HANDLE_16);
- } catch (SecurityException e) {
- // desired result
- }
- verify(mFakePhoneAccountRegistrar, never())
- .setUserSelectedOutgoingPhoneAccount(
- any(PhoneAccountHandle.class), any(UserHandle.class));
+
+ assertThrows(SecurityException.class,
+ () -> mTSIBinder.setUserSelectedOutgoingPhoneAccount(TEL_PA_HANDLE_16));
}
@SmallTest
@@ -378,11 +516,11 @@ public class TelecomServiceImplTest extends TelecomTestCase {
// Returns all phone accounts when getCallCapablePhoneAccounts is called.
when(mFakePhoneAccountRegistrar
.getCallCapablePhoneAccounts(nullable(String.class), eq(true),
- nullable(UserHandle.class))).thenReturn(fullPHList);
+ nullable(UserHandle.class), eq(true))).thenReturn(fullPHList);
// Returns only enabled phone accounts when getCallCapablePhoneAccounts is called.
when(mFakePhoneAccountRegistrar
.getCallCapablePhoneAccounts(nullable(String.class), eq(false),
- nullable(UserHandle.class))).thenReturn(smallPHList);
+ nullable(UserHandle.class), eq(true))).thenReturn(smallPHList);
makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
assertEquals(fullPHList,
@@ -395,21 +533,63 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@SmallTest
@Test
- public void testGetCallCapablePhoneAccountsFailure() throws RemoteException {
+ public void testGetCallCapablePhoneAccountsWithoutPermission() throws RemoteException {
List<String> enforcedPermissions = List.of(READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE);
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
argThat(new AnyStringIn(enforcedPermissions)), anyString());
- List<PhoneAccountHandle> result = null;
- try {
- result = mTSIBinder.getCallCapablePhoneAccounts(true, "", null).getList();
- } catch (SecurityException e) {
- // intended behavior
- }
- assertNull(result);
- verify(mFakePhoneAccountRegistrar, never())
- .getCallCapablePhoneAccounts(anyString(), anyBoolean(), any(UserHandle.class));
+ assertThrows(SecurityException.class,
+ () -> mTSIBinder.getCallCapablePhoneAccounts(true, "", null));
+ }
+
+ @SmallTest
+ @Test
+ public void testGetSelfManagedPhoneAccounts() throws RemoteException {
+ List<PhoneAccountHandle> accounts = List.of(TEL_PA_HANDLE_16);
+
+ when(mFakePhoneAccountRegistrar.getSelfManagedPhoneAccounts(nullable(UserHandle.class)))
+ .thenReturn(accounts);
+ makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16);
+
+ assertEquals(accounts,
+ mTSIBinder.getSelfManagedPhoneAccounts(DEFAULT_DIALER_PACKAGE, null).getList());
+ }
+
+ @SmallTest
+ @Test
+ public void testGetSelfManagedPhoneAccountsWithoutPermission() throws RemoteException {
+ List<String> enforcedPermissions = List.of(READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ argThat(new AnyStringIn(enforcedPermissions)), anyString());
+
+ assertThrows(SecurityException.class,
+ () -> mTSIBinder.getSelfManagedPhoneAccounts("", null));
+ }
+
+ @SmallTest
+ @Test
+ public void testGetOwnSelfManagedPhoneAccounts() throws RemoteException {
+ List<PhoneAccountHandle> accounts = List.of(TEL_PA_HANDLE_16);
+
+ when(mFakePhoneAccountRegistrar.getSelfManagedPhoneAccountsForPackage(
+ eq(DEFAULT_DIALER_PACKAGE), nullable(UserHandle.class)))
+ .thenReturn(accounts);
+ makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16);
+
+ assertEquals(accounts,
+ mTSIBinder.getOwnSelfManagedPhoneAccounts(DEFAULT_DIALER_PACKAGE, null).getList());
+ }
+
+ @SmallTest
+ @Test
+ public void testGetOwnSelfManagedPhoneAccountsWithoutPermission() throws RemoteException {
+ List<String> enforcedPermissions = List.of(MANAGE_OWN_CALLS);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ argThat(new AnyStringIn(enforcedPermissions)), anyString());
+
+ assertThrows(SecurityException.class,
+ () -> mTSIBinder.getOwnSelfManagedPhoneAccounts("", null));
}
@SmallTest
@@ -419,10 +599,12 @@ public class TelecomServiceImplTest extends TelecomTestCase {
List<PhoneAccountHandle> telPHList = List.of(TEL_PA_HANDLE_16);
when(mFakePhoneAccountRegistrar
- .getCallCapablePhoneAccounts(eq("tel"), anyBoolean(), any(UserHandle.class)))
+ .getCallCapablePhoneAccounts(eq("tel"), anyBoolean(),
+ any(UserHandle.class), anyBoolean()))
.thenReturn(telPHList);
when(mFakePhoneAccountRegistrar
- .getCallCapablePhoneAccounts(eq("sip"), anyBoolean(), any(UserHandle.class)))
+ .getCallCapablePhoneAccounts(eq("sip"), anyBoolean(),
+ any(UserHandle.class), anyBoolean()))
.thenReturn(sipPHList);
makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
@@ -436,11 +618,21 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@SmallTest
@Test
+ public void testGetPhoneAccountsSupportingSchemeWithoutPermission() throws RemoteException {
+ List<String> enforcedPermissions = List.of(MODIFY_PHONE_STATE);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ argThat(new AnyStringIn(enforcedPermissions)), anyString());
+
+ assertTrue(mTSIBinder.getPhoneAccountsSupportingScheme("any", "").getList().isEmpty());
+ }
+
+ @SmallTest
+ @Test
public void testGetPhoneAccountsForPackage() throws RemoteException {
List<PhoneAccountHandle> phoneAccountHandleList = List.of(
TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
when(mFakePhoneAccountRegistrar
- .getPhoneAccountsForPackage(anyString(), any(UserHandle.class)))
+ .getAllPhoneAccountHandlesForPackage(any(UserHandle.class), anyString()))
.thenReturn(phoneAccountHandleList);
makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
assertEquals(phoneAccountHandleList,
@@ -450,7 +642,20 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@SmallTest
@Test
+ public void testGetPhoneAccountsForPackageWithoutPermission() throws RemoteException {
+ List<String> enforcedPermissions = List.of(READ_PRIVILEGED_PHONE_STATE);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ argThat(new AnyStringIn(enforcedPermissions)), any());
+
+ assertThrows(SecurityException.class,
+ () -> mTSIBinder.getPhoneAccountsForPackage(""));
+ }
+
+ @SmallTest
+ @Test
public void testGetPhoneAccount() throws Exception {
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .when(mContext).checkCallingPermission(MODIFY_PHONE_STATE);
makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
assertEquals(TEL_PA_HANDLE_16, mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_16,
mContext.getPackageName()).getAccountHandle());
@@ -466,15 +671,96 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@SmallTest
@Test
+ public void testGetAllPhoneAccountsCount() throws RemoteException {
+ List<PhoneAccount> phoneAccountList = List.of(
+ makePhoneAccount(TEL_PA_HANDLE_16).build(),
+ makePhoneAccount(SIP_PA_HANDLE_17).build());
+
+ when(mFakePhoneAccountRegistrar.getAllPhoneAccounts(any(UserHandle.class), anyBoolean()))
+ .thenReturn(phoneAccountList);
+
+ assertEquals(phoneAccountList.size(), mTSIBinder.getAllPhoneAccountsCount());
+ }
+
+ @SmallTest
+ @Test
+ public void testGetAllPhoneAccountsCountWithoutPermission() throws RemoteException {
+ List<String> enforcedPermissions = List.of(MODIFY_PHONE_STATE);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ argThat(new AnyStringIn(enforcedPermissions)), any());
+
+ assertThrows(SecurityException.class,
+ () -> mTSIBinder.getAllPhoneAccountsCount());
+ }
+
+ @SmallTest
+ @Test
public void testGetAllPhoneAccounts() throws RemoteException {
List<PhoneAccount> phoneAccountList = List.of(
makePhoneAccount(TEL_PA_HANDLE_16).build(),
makePhoneAccount(SIP_PA_HANDLE_17).build());
- when(mFakePhoneAccountRegistrar.getAllPhoneAccounts(any(UserHandle.class)))
+ when(mFakePhoneAccountRegistrar.getAllPhoneAccounts(any(UserHandle.class), anyBoolean()))
.thenReturn(phoneAccountList);
- assertEquals(2, mTSIBinder.getAllPhoneAccounts().getList().size());
+ assertEquals(phoneAccountList.size(), mTSIBinder.getAllPhoneAccounts().getList().size());
+ }
+
+ @SmallTest
+ @Test
+ public void testGetAllPhoneAccountsWithoutPermission() throws RemoteException {
+ List<String> enforcedPermissions = List.of(MODIFY_PHONE_STATE);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ argThat(new AnyStringIn(enforcedPermissions)), any());
+
+ assertThrows(SecurityException.class,
+ () -> mTSIBinder.getAllPhoneAccounts());
+ }
+
+ @SmallTest
+ @Test
+ public void testGetAllPhoneAccountHandles() throws RemoteException {
+ List<PhoneAccountHandle> handles = List.of(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
+ when(mFakePhoneAccountRegistrar.getAllPhoneAccountHandles(
+ any(UserHandle.class), anyBoolean())).thenReturn(handles);
+
+ assertEquals(handles, mTSIBinder.getAllPhoneAccountHandles().getList());
+ }
+
+ @SmallTest
+ @Test
+ public void testGetAllPhoneAccountHandlesWithoutPermission() throws RemoteException {
+ List<String> enforcedPermissions = List.of(MODIFY_PHONE_STATE);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ argThat(new AnyStringIn(enforcedPermissions)), any());
+
+ assertThrows(SecurityException.class,
+ () -> mTSIBinder.getAllPhoneAccountHandles());
+ }
+
+ @SmallTest
+ @Test
+ public void testGetSimCallManager() throws RemoteException {
+ final PhoneAccountHandle handle = TEL_PA_HANDLE_16;
+ final int subId = 1;
+ when(mFakePhoneAccountRegistrar.getSimCallManager(eq(subId), any(UserHandle.class)))
+ .thenReturn(handle);
+
+ assertEquals(handle, mTSIBinder.getSimCallManager(subId, "any"));
+ }
+
+ @SmallTest
+ @Test
+ public void testGetSimCallManagerForUser() throws RemoteException {
+ final PhoneAccountHandle handle = TEL_PA_HANDLE_16;
+ final int user = 1;
+ when(mFakePhoneAccountRegistrar.getSimCallManager(
+ argThat(userHandle -> {
+ return userHandle.getIdentifier() == user;
+ })))
+ .thenReturn(handle);
+
+ assertEquals(handle, mTSIBinder.getSimCallManagerForUser(user, "any"));
}
@SmallTest
@@ -492,6 +778,65 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@SmallTest
@Test
+ public void testRegisterPhoneAccountWithoutPermissionAnomalyReported() throws RemoteException {
+ PhoneAccountHandle handle = new PhoneAccountHandle(
+ new ComponentName("package", "cs"), "test", Binder.getCallingUserHandle());
+ PhoneAccount account = makeSelfManagedPhoneAccount(handle).build();
+
+ List<String> enforcedPermissions = List.of(MANAGE_OWN_CALLS);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ argThat(new AnyStringIn(enforcedPermissions)), any());
+
+ registerPhoneAccountTestHelper(account, false);
+ verify(mAnomalyReporterAdapter).reportAnomaly(
+ TelecomServiceImpl.REGISTER_PHONE_ACCOUNT_ERROR_UUID,
+ TelecomServiceImpl.REGISTER_PHONE_ACCOUNT_ERROR_MSG);
+ }
+
+ @SmallTest
+ @Test
+ public void testRegisterPhoneAccountSelfManagedWithoutPermission() throws RemoteException {
+ PhoneAccountHandle handle = new PhoneAccountHandle(
+ new ComponentName("package", "cs"), "test", Binder.getCallingUserHandle());
+ PhoneAccount account = makeSelfManagedPhoneAccount(handle).build();
+
+ List<String> enforcedPermissions = List.of(MANAGE_OWN_CALLS);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ argThat(new AnyStringIn(enforcedPermissions)), any());
+
+ registerPhoneAccountTestHelper(account, false);
+ }
+
+ @SmallTest
+ @Test
+ public void testRegisterPhoneAccountSelfManagedInvalidCapabilities() throws RemoteException {
+ PhoneAccountHandle handle = new PhoneAccountHandle(
+ new ComponentName("package", "cs"), "test", Binder.getCallingUserHandle());
+
+ PhoneAccount selfManagedCallProviderAccount = makePhoneAccount(handle)
+ .setCapabilities(
+ PhoneAccount.CAPABILITY_SELF_MANAGED |
+ PhoneAccount.CAPABILITY_CALL_PROVIDER)
+ .build();
+ registerPhoneAccountTestHelper(selfManagedCallProviderAccount, false);
+
+ PhoneAccount selfManagedConnectionManagerAccount = makePhoneAccount(handle)
+ .setCapabilities(
+ PhoneAccount.CAPABILITY_SELF_MANAGED |
+ PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
+ .build();
+ registerPhoneAccountTestHelper(selfManagedConnectionManagerAccount, false);
+
+ PhoneAccount selfManagedSimSubscriptionAccount = makePhoneAccount(handle)
+ .setCapabilities(
+ PhoneAccount.CAPABILITY_SELF_MANAGED |
+ PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
+ .build();
+ registerPhoneAccountTestHelper(selfManagedSimSubscriptionAccount, false);
+ }
+
+ @SmallTest
+ @Test
public void testRegisterPhoneAccountWithoutModifyPermission() throws RemoteException {
// tests the case where the package does not have MODIFY_PHONE_STATE but is
// registering its own phone account as a third-party connection service
@@ -589,7 +934,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
boolean didExceptionOccur = false;
try {
- mTSIBinder.registerPhoneAccount(testPhoneAccount);
+ mTSIBinder.registerPhoneAccount(testPhoneAccount, CALLING_PACKAGE);
} catch (Exception e) {
didExceptionOccur = true;
}
@@ -606,6 +951,26 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@SmallTest
@Test
+ public void testRegisterPhoneAccountImageIconCrossUser() throws RemoteException {
+ String packageNameToUse = "com.android.officialpackage";
+ PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
+ packageNameToUse, "cs"), "test", Binder.getCallingUserHandle());
+ Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+ PhoneAccount phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .when(mContext).checkCallingOrSelfPermission(MODIFY_PHONE_STATE);
+
+ // This should fail; security exception will be thrown.
+ registerPhoneAccountTestHelper(phoneAccount, false);
+
+ icon = Icon.createWithContentUri("content://0@media/external/images/media/");
+ phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
+ // This should succeed.
+ registerPhoneAccountTestHelper(phoneAccount, true);
+ }
+
+ @SmallTest
+ @Test
public void testUnregisterPhoneAccount() throws RemoteException {
String packageNameToUse = "com.android.officialpackage";
PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
@@ -615,7 +980,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
doReturn(PackageManager.PERMISSION_GRANTED)
.when(mContext).checkCallingOrSelfPermission(MODIFY_PHONE_STATE);
- mTSIBinder.unregisterPhoneAccount(phHandle);
+ mTSIBinder.unregisterPhoneAccount(phHandle, CALLING_PACKAGE);
verify(mFakePhoneAccountRegistrar).unregisterPhoneAccount(phHandle);
}
@@ -632,7 +997,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
when(pm.hasSystemFeature(PackageManager.FEATURE_TELECOM)).thenReturn(false);
try {
- mTSIBinder.unregisterPhoneAccount(phHandle);
+ mTSIBinder.unregisterPhoneAccount(phHandle, CALLING_PACKAGE);
} catch (UnsupportedOperationException e) {
// expected behavior
}
@@ -644,25 +1009,47 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@SmallTest
@Test
+ public void testClearAccounts() throws RemoteException {
+ mTSIBinder.clearAccounts(CALLING_PACKAGE);
+
+ verify(mFakePhoneAccountRegistrar)
+ .clearAccounts(CALLING_PACKAGE, mTSIBinder.getCallingUserHandle());
+ }
+
+ @SmallTest
+ @Test
+ public void testClearAccountsWithoutPermission() throws RemoteException {
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(MODIFY_PHONE_STATE);
+
+ assertThrows(UnsupportedOperationException.class,
+ () -> mTSIBinder.clearAccounts(CALLING_PACKAGE));
+ }
+
+ @SmallTest
+ @Test
public void testAddNewIncomingCall() throws Exception {
- PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_CURRENT).build();
+ PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_16).build();
phoneAccount.setIsEnabled(true);
doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
- eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class));
+ eq(TEL_PA_HANDLE_16), any(UserHandle.class));
doNothing().when(mAppOpsManager).checkPackage(anyInt(), anyString());
Bundle extras = createSampleExtras();
- mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_CURRENT, extras);
+ mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_16, extras, CALLING_PACKAGE);
+ verify(mFakePhoneAccountRegistrar).getPhoneAccount(
+ TEL_PA_HANDLE_16, TEL_PA_HANDLE_16.getUserHandle());
addCallTestHelper(TelecomManager.ACTION_INCOMING_CALL,
- CallIntentProcessor.KEY_IS_INCOMING_CALL, extras, false);
+ CallIntentProcessor.KEY_IS_INCOMING_CALL, extras,
+ TEL_PA_HANDLE_16, false);
}
@SmallTest
@Test
public void testAddNewIncomingCallFailure() throws Exception {
try {
- mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_16, null);
+ mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_16, null, CALLING_PACKAGE);
} catch (SecurityException e) {
// expected
}
@@ -670,7 +1057,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString());
try {
- mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_CURRENT, null);
+ mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_CURRENT, null, CALLING_PACKAGE);
} catch (SecurityException e) {
// expected
}
@@ -693,7 +1080,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
mTSIBinder.addNewUnknownCall(TEL_PA_HANDLE_CURRENT, extras);
addCallTestHelper(TelecomManager.ACTION_NEW_UNKNOWN_CALL,
- CallIntentProcessor.KEY_IS_UNKNOWN_CALL, extras, true);
+ CallIntentProcessor.KEY_IS_UNKNOWN_CALL, extras, TEL_PA_HANDLE_CURRENT, true);
}
@SmallTest
@@ -719,7 +1106,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
}
private void addCallTestHelper(String expectedAction, String extraCallKey,
- Bundle expectedExtras, boolean isUnknown) {
+ Bundle expectedExtras, PhoneAccountHandle expectedPhoneAccountHandle,
+ boolean isUnknown) {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
if (isUnknown) {
verify(mCallIntentProcessorAdapter).processUnknownCallIntent(any(CallsManager.class),
@@ -731,7 +1119,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
Intent capturedIntent = intentCaptor.getValue();
assertEquals(expectedAction, capturedIntent.getAction());
Bundle intentExtras = capturedIntent.getExtras();
- assertEquals(TEL_PA_HANDLE_CURRENT,
+ assertEquals(expectedPhoneAccountHandle,
intentExtras.get(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE));
assertTrue(intentExtras.getBoolean(extraCallKey));
@@ -747,88 +1135,517 @@ public class TelecomServiceImplTest extends TelecomTestCase {
}
}
+ /**
+ * Place a managed call with no PhoneAccount specified and ensure no security exception is
+ * thrown.
+ */
@SmallTest
@Test
public void testPlaceCallWithNonEmergencyPermission() throws Exception {
Uri handle = Uri.parse("tel:6505551234");
Bundle extras = createSampleExtras();
+ // We have passed in the DEFAULT_DIALER_PACKAGE for this test, so canCallPhone is always
+ // true.
when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
nullable(String.class), nullable(String.class)))
.thenReturn(AppOpsManager.MODE_ALLOWED);
doReturn(PackageManager.PERMISSION_GRANTED)
- .when(mContext).checkCallingPermission(CALL_PHONE);
+ .when(mContext).checkCallingOrSelfPermission(CALL_PHONE);
doReturn(PackageManager.PERMISSION_DENIED)
- .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
- placeCallTestHelper(handle, extras, true);
+ placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+ /*shouldNonEmergencyBeAllowed*/ true);
}
+ /**
+ * Ensure that we get a SecurityException if the UID of the caller doesn't match the UID of the
+ * UID of the package name passed in.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_enforceCallingPackageFailure() throws Exception {
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed
+ // ConnectionService.
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ // Return a non-matching UID for testing purposes.
+ when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(-1);
+ try {
+ mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
+ fail("Expected SecurityException because calling package doesn't match");
+ } catch(SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * In the case that there is a self-managed call request and MANAGE_OWN_CALLS is granted, ensure
+ * that placeCall does not generate a SecurityException.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_selfManaged_permissionGranted() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed
+ // ConnectionService.
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ // pass MANAGE_OWN_CALLS check, but do not have CALL_PHONE
+ doNothing().when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PHONE);
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+
+ try {
+ mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
+ placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
+ /*shouldNonEmergencyBeAllowed*/ false);
+ } catch(SecurityException e) {
+ fail("Unexpected SecurityException - MANAGE_OWN_CALLS is set");
+ }
+ }
+
+ /**
+ * In the case that the placeCall API is being used place a self-managed call
+ * (phone account is marked self-managed and the calling application owns that PhoneAccount),
+ * ensure that the call gets placed as not self-managed as to not disclose PA info.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_selfManaged_noPermission() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed
+ // ConnectionService.
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+
+ try {
+ mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
+ fail("Expected SecurityException because MANAGE_OWN_CALLS is not set");
+ } catch(SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * In the case that there is a self-managed call request and the app doesn't own that
+ * PhoneAccount, we will need to check CALL_PHONE. If they do not have CALL_PHONE permission,
+ * we need to throw a security exception.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_selfManaged_permissionFail() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage doesn't match the PhoneAccountHandle package
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ // pass MANAGE_OWN_CALLS check, but do not have CALL PHONE
+ doNothing().when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+ doThrow(new SecurityException())
+ .when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+
+ try {
+ // Calling package is received and is not the same as PACKAGE_NAME
+ mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
+ fail("Expected a SecurityException - CALL_PHONE was not granted");
+ } catch(SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * In the case that there is a self-managed call request and the app doesn't own that
+ * PhoneAccount, we will need to check CALL_PHONE. If they have the CALL_PHONE permission, but
+ * the app op has been denied, this should throw a security exception.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_selfManaged_appOpPermissionFail() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage doesn't match the PhoneAccountHandle package.
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ // pass MANAGE_OWN_CALLS check, but do not have CALL PHONE
+ doNothing().when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+ doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+ nullable(String.class), nullable(String.class)))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+ try {
+ mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
+ fail("Expected a SecurityException - CALL_PHONE app op is denied");
+ } catch(SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * In the case that there is a self-managed call request and the app doesn't own that
+ * PhoneAccount, we will need to check CALL_PHONE. If they have the correct permissions, the
+ * call will go through, however we will have removed the self-managed PhoneAccountHandle. The
+ * call will go through as a normal managed call request with no PhoneAccountHandle.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_selfManaged_differentCallingPackage() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage doesn't match the PhoneAccountHandle package
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ // simulate default dialer so CALL_PHONE is granted.
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+ nullable(String.class), nullable(String.class)))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PHONE);
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+
+ // We expect the call to go through with no PhoneAccount specified, since the request
+ // contained a self-managed PhoneAccountHandle that didn't belong to this app.
+ Bundle expectedExtras = extras.deepCopy();
+ expectedExtras.remove(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+ try {
+ mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
+ } catch (SecurityException e) {
+ fail("Unexpected SecurityException - CTS is default dialer and MANAGE_OWN_CALLS is not"
+ + " required. Exception: " + e);
+ }
+ placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+ /*shouldNonEmergencyBeAllowed*/ true);
+ }
+
+ /**
+ * In the case that there is a managed call request and the app owns that
+ * PhoneAccount (but is not a self-managed), we will still need to check CALL_PHONE.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_samePackage_managedPhoneAccount_permissionFail() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ makePhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage doesn't match the PhoneAccountHandle package
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ // CALL_PHONE is not granted to the device.
+ doThrow(new SecurityException())
+ .when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+ doThrow(new SecurityException())
+ .when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+
+ try {
+ mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
+ fail("Expected a SecurityException - CALL_PHONE is not granted");
+ } catch(SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * In the case that there is a managed call request and the app owns that
+ * PhoneAccount (but is not a self-managed), we will still need to check CALL_PHONE.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_samePackage_managedPhoneAccount_AppOpFail() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ makePhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage matches the PhoneAccountHandle, but this is not a self managed phone
+ // account.
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ // CALL_PHONE is granted, but the app op is not
+ doThrow(new SecurityException())
+ .when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+ doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+ nullable(String.class), nullable(String.class)))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+
+ try {
+ mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
+ fail("Expected a SecurityException - CALL_PHONE app op is denied");
+ } catch(SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Since this is a self-managed call being requested, so ensure we report the call as
+ * self-managed and without non-emergency permissions.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_selfManaged_nonEmergencyPermission() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+ makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed
+ // ConnectionService.
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ // enforceCallingOrSelfPermission is implicitly granted for MANAGE_OWN_CALLS here and
+ // CALL_PHONE is not required.
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+ nullable(String.class), nullable(String.class)))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PHONE);
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+
+ mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
+ placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
+ /*shouldNonEmergencyBeAllowed*/ false);
+ }
+
+ /**
+ * Default dialer is calling placeCall and has CALL_PHONE granted, so non-emergency calls
+ * are allowed.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCall_managed_nonEmergencyGranted() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+ // callingPackage doesn't match the PhoneAccountHandle, so this app does not have a
+ // self-managed ConnectionService
+ extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+ // CALL_PHONE granted
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+ nullable(String.class), nullable(String.class)))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PHONE);
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+
+ mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
+ placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+ /*shouldNonEmergencyBeAllowed*/ true);
+ }
+
+ /**
+ * In the case that there is a managed normal call request and the app has CALL_PRIVILEGED
+ * permission, place call should complete successfully.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCallPrivileged() throws Exception {
+ doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+ eq(DEFAULT_DIALER_PACKAGE), anyInt());
+ Uri handle = Uri.parse("tel:6505551234");
+
+ // CALL_PHONE is not granted, but CALL_PRIVILEGED is
+ doThrow(new SecurityException())
+ .when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+ doReturn(PackageManager.PERMISSION_GRANTED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+ doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+ eq(CALL_PHONE), anyString());
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+ nullable(String.class), nullable(String.class)))
+ .thenReturn(AppOpsManager.MODE_ERRORED);
+
+ try {
+ mTSIBinder.placeCall(handle, null, PACKAGE_NAME + "2", null);
+ } catch(SecurityException e) {
+ fail("Expected no SecurityException - CALL_PRIVILEGED is granted");
+ }
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mUserCallIntentProcessor).processIntent(intentCaptor.capture(), anyString(),
+ eq(false), eq(true), eq(true));
+ Intent capturedIntent = intentCaptor.getValue();
+ assertEquals(Intent.ACTION_CALL_PRIVILEGED, capturedIntent.getAction());
+ assertEquals(handle, capturedIntent.getData());
+ }
+
+ /**
+ * The default dialer is requesting to place a call and CALL_PHONE is granted, however
+ * OP_CALL_PHONE app op is denied to that app, so non-emergency calls will be denied.
+ */
@SmallTest
@Test
public void testPlaceCallWithAppOpsOff() throws Exception {
Uri handle = Uri.parse("tel:6505551234");
Bundle extras = createSampleExtras();
+ // We have passed in the DEFAULT_DIALER_PACKAGE for this test, so canCallPhone is always
+ // true.
when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
nullable(String.class), nullable(String.class)))
.thenReturn(AppOpsManager.MODE_IGNORED);
doReturn(PackageManager.PERMISSION_GRANTED)
- .when(mContext).checkCallingPermission(CALL_PHONE);
+ .when(mContext).checkCallingOrSelfPermission(CALL_PHONE);
doReturn(PackageManager.PERMISSION_DENIED)
- .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
- placeCallTestHelper(handle, extras, false);
+ placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+ /*shouldNonEmergencyBeAllowed*/ false);
}
+ /**
+ * The default dialer is requesting to place a call, however CALL_PHONE is denied to that app,
+ * so non-emergency calls will be denied.
+ */
@SmallTest
@Test
public void testPlaceCallWithNoCallingPermission() throws Exception {
Uri handle = Uri.parse("tel:6505551234");
Bundle extras = createSampleExtras();
+ // We are assumed to be default dialer in this test, so canCallPhone is always true.
when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
nullable(String.class), nullable(String.class)))
.thenReturn(AppOpsManager.MODE_ALLOWED);
doReturn(PackageManager.PERMISSION_DENIED)
- .when(mContext).checkCallingPermission(CALL_PHONE);
+ .when(mContext).checkCallingOrSelfPermission(CALL_PHONE);
doReturn(PackageManager.PERMISSION_DENIED)
- .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
- placeCallTestHelper(handle, extras, false);
+ placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+ /*shouldNonEmergencyBeAllowed*/ false);
}
+ /**
+ * Ensure the expected handle, extras, and non-emergency call permission checks have been
+ * correctly included in the ACTION_CALL intent as part of the
+ * {@link UserCallIntentProcessor#processIntent} method called during the placeCall procedure.
+ * @param expectedHandle Expected outgoing number handle
+ * @param expectedExtras Expected extras in the ACTION_CALL intent.
+ * @param shouldNonEmergencyBeAllowed true if non-emergency calls should be allowed, false if
+ * permission checks failed for non-emergency.
+ */
private void placeCallTestHelper(Uri expectedHandle, Bundle expectedExtras,
- boolean shouldNonEmergencyBeAllowed) {
+ boolean isSelfManagedExpected, boolean shouldNonEmergencyBeAllowed) {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mUserCallIntentProcessor).processIntent(intentCaptor.capture(), anyString(),
- eq(shouldNonEmergencyBeAllowed), eq(true));
+ eq(isSelfManagedExpected), eq(shouldNonEmergencyBeAllowed), eq(true));
Intent capturedIntent = intentCaptor.getValue();
assertEquals(Intent.ACTION_CALL, capturedIntent.getAction());
assertEquals(expectedHandle, capturedIntent.getData());
assertTrue(areBundlesEqual(expectedExtras, capturedIntent.getExtras()));
}
+ /**
+ * Ensure that if the caller was never granted CALL_PHONE (and is not the default dialer), a
+ * SecurityException is thrown.
+ */
@SmallTest
@Test
public void testPlaceCallFailure() throws Exception {
Uri handle = Uri.parse("tel:6505551234");
Bundle extras = createSampleExtras();
+ // The app is not considered a privileged dialer and does not have the CALL_PHONE
+ // permission.
doThrow(new SecurityException())
.when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+
+ try {
+ mTSIBinder.placeCall(handle, extras, "arbitrary_package_name", null);
+ fail("Expected SecurityException because CALL_PHONE was not granted to caller");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ verify(mUserCallIntentProcessor, never())
+ .processIntent(any(Intent.class), anyString(), eq(false), anyBoolean(), eq(true));
+ }
+
+ /**
+ * Ensure that if the caller was granted CALL_PHONE, but did not get the OP_CALL_PHONE app op
+ * (and is not the default dialer), a SecurityException is thrown.
+ */
+ @SmallTest
+ @Test
+ public void testPlaceCallAppOpFailure() throws Exception {
+ Uri handle = Uri.parse("tel:6505551234");
+ Bundle extras = createSampleExtras();
+
+ // The app is not considered a privileged dialer and does not have the OP_CALL_PHONE
+ // app op.
+ doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+ doReturn(PackageManager.PERMISSION_DENIED)
+ .when(mContext).checkCallingOrSelfPermission(CALL_PRIVILEGED);
+ when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+ nullable(String.class), nullable(String.class)))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
try {
mTSIBinder.placeCall(handle, extras, "arbitrary_package_name", null);
+ fail("Expected SecurityException because CALL_PHONE was not granted to caller");
} catch (SecurityException e) {
// expected
}
verify(mUserCallIntentProcessor, never())
- .processIntent(any(Intent.class), anyString(), anyBoolean(), eq(true));
+ .processIntent(any(Intent.class), anyString(), eq(false), anyBoolean(), eq(true));
}
@SmallTest
@@ -1090,6 +1907,29 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@SmallTest
@Test
+ public void testGetDefaultDialerPackageForUser() throws Exception {
+ final int userId = 1;
+ final String packageName = "some.package";
+
+ when(mDefaultDialerCache.getDefaultDialerApplication(userId))
+ .thenReturn(packageName);
+
+ assertEquals(packageName, mTSIBinder.getDefaultDialerPackageForUser(userId));
+ }
+
+ @SmallTest
+ @Test
+ public void testGetSystemDialerPackage() throws Exception {
+ final String packageName = "some.package";
+
+ when(mDefaultDialerCache.getSystemDialerApplication())
+ .thenReturn(packageName);
+
+ assertEquals(packageName, mTSIBinder.getSystemDialerPackage(CALLING_PACKAGE));
+ }
+
+ @SmallTest
+ @Test
public void testEndCallWithRingingForegroundCall() throws Exception {
Call call = mock(Call.class);
when(call.getState()).thenReturn(CallState.RINGING);
@@ -1123,8 +1963,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
public void testEndCallWithNoForegroundCall() throws Exception {
Call call = mock(Call.class);
when(call.getState()).thenReturn(CallState.ACTIVE);
- when(mFakeCallsManager.getFirstCallWithState(any()))
- .thenReturn(call);
+ when(mFakeCallsManager.getFirstCallWithState(any())).thenReturn(call);
assertTrue(mTSIBinder.endCall(TEST_PACKAGE));
verify(mFakeCallsManager).disconnectCall(eq(call));
}
@@ -1165,14 +2004,16 @@ public class TelecomServiceImplTest extends TelecomTestCase {
@SmallTest
@Test
public void testIsInCall() throws Exception {
- when(mFakeCallsManager.hasOngoingCalls()).thenReturn(true);
+ when(mFakeCallsManager.hasOngoingCalls(any(UserHandle.class), anyBoolean()))
+ .thenReturn(true);
assertTrue(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE, null));
}
@SmallTest
@Test
public void testNotIsInCall() throws Exception {
- when(mFakeCallsManager.hasOngoingCalls()).thenReturn(false);
+ when(mFakeCallsManager.hasOngoingCalls(any(UserHandle.class), anyBoolean()))
+ .thenReturn(false);
assertFalse(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE, null));
}
@@ -1187,20 +2028,22 @@ public class TelecomServiceImplTest extends TelecomTestCase {
} catch (SecurityException e) {
// desired result
}
- verify(mFakeCallsManager, never()).hasOngoingCalls();
+ verify(mFakeCallsManager, never()).hasOngoingCalls(any(UserHandle.class), anyBoolean());
}
@SmallTest
@Test
public void testIsInManagedCall() throws Exception {
- when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(true);
+ when(mFakeCallsManager.hasOngoingManagedCalls(any(UserHandle.class), anyBoolean()))
+ .thenReturn(true);
assertTrue(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE, null));
}
@SmallTest
@Test
public void testNotIsInManagedCall() throws Exception {
- when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(false);
+ when(mFakeCallsManager.hasOngoingManagedCalls(any(UserHandle.class), anyBoolean()))
+ .thenReturn(false);
assertFalse(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE, null));
}
@@ -1215,7 +2058,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
} catch (SecurityException e) {
// desired result
}
- verify(mFakeCallsManager, never()).hasOngoingCalls();
+ verify(mFakeCallsManager, never()).hasOngoingCalls(any(UserHandle.class), anyBoolean());
}
/**
@@ -1251,6 +2094,22 @@ public class TelecomServiceImplTest extends TelecomTestCase {
verify(mFakeCallsManager, never()).answerCall(eq(call), anyInt());
}
+ @SmallTest
+ @Test
+ public void testGetAdnUriForPhoneAccount() throws Exception {
+ final int subId = 1;
+ final Uri adnUri = Uri.parse("content://icc/adn/subId/" + subId);
+ PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_CURRENT).build();
+ when(mFakePhoneAccountRegistrar.getPhoneAccount(
+ eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class)))
+ .thenReturn(phoneAccount);
+ when(mFakePhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(TEL_PA_HANDLE_CURRENT))
+ .thenReturn(subId);
+
+ assertEquals(adnUri,
+ mTSIBinder.getAdnUriForPhoneAccount(TEL_PA_HANDLE_CURRENT, DEFAULT_DIALER_PACKAGE));
+ }
+
/**
* Register phone accounts for the supplied PhoneAccountHandles to make them
* visible to all users (via the isVisibleToCaller method in TelecomServiceImpl.
@@ -1275,6 +2134,12 @@ public class TelecomServiceImplTest extends TelecomTestCase {
return paBuilder;
}
+ private PhoneAccount.Builder makeSelfManagedPhoneAccount(PhoneAccountHandle paHandle) {
+ PhoneAccount.Builder paBuilder = makePhoneAccount(paHandle);
+ paBuilder.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED);
+ return paBuilder;
+ }
+
private PhoneAccount.Builder makePhoneAccount(PhoneAccountHandle paHandle) {
return new PhoneAccount.Builder(paHandle, "testLabel");
}
@@ -1286,6 +2151,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
}
private static boolean areBundlesEqual(Bundle b1, Bundle b2) {
+ if (b1.keySet().size() != b2.keySet().size()) return false;
+
for (String key1 : b1.keySet()) {
if (!b1.get(key1).equals(b2.get(key1))) {
return false;