Binds to 3rd-party InCallService with MANAGE_ONGOING_CALL permission
Test: atest InCallControllerTests; atest ThirdPartyInCallServiceAppOpsPermissionTest; Bug: 169595473 Change-Id: I28ada2b72d3955dfd500d34317b24dd7385fbc2b
This commit is contained in:
parent
8a66b49bd6
commit
4cc5a541d2
|
@ -28,6 +28,7 @@ import android.content.ComponentName;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.PermissionChecker;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
|
@ -1524,7 +1525,7 @@ public class InCallController extends CallsManagerListenerBase {
|
|||
if (requestedType == IN_CALL_SERVICE_TYPE_NON_UI) {
|
||||
mKnownNonUiInCallServices.add(foundComponentName);
|
||||
}
|
||||
|
||||
|
||||
boolean isEnabled = isServiceEnabled(foundComponentName,
|
||||
serviceInfo, packageManager);
|
||||
if (isEnabled && (requestedType == 0 || requestedType == currentType)) {
|
||||
|
@ -1594,9 +1595,17 @@ public class InCallController extends CallsManagerListenerBase {
|
|||
p -> packageManager.checkPermission(
|
||||
Manifest.permission.CONTROL_INCALL_EXPERIENCE,
|
||||
p) == PackageManager.PERMISSION_GRANTED);
|
||||
|
||||
boolean hasAppOpsPermittedManageOngoingCalls = false;
|
||||
if (isAppOpsPermittedManageOngoingCalls(serviceInfo.applicationInfo.uid,
|
||||
serviceInfo.packageName)) {
|
||||
hasAppOpsPermittedManageOngoingCalls = true;
|
||||
}
|
||||
|
||||
boolean isCarModeUIService = serviceInfo.metaData != null &&
|
||||
serviceInfo.metaData.getBoolean(
|
||||
TelecomManager.METADATA_IN_CALL_SERVICE_CAR_MODE_UI, false);
|
||||
|
||||
if (isCarModeUIService && hasControlInCallPermission) {
|
||||
return IN_CALL_SERVICE_TYPE_CAR_MODE_UI;
|
||||
}
|
||||
|
@ -1611,7 +1620,8 @@ public class InCallController extends CallsManagerListenerBase {
|
|||
|
||||
// Also allow any in-call service that has the control-experience permission (to ensure
|
||||
// that it is a system app) and doesn't claim to show any UI.
|
||||
if (!isUIService && !isCarModeUIService && hasControlInCallPermission) {
|
||||
if (!isUIService && !isCarModeUIService && (hasControlInCallPermission ||
|
||||
hasAppOpsPermittedManageOngoingCalls)) {
|
||||
return IN_CALL_SERVICE_TYPE_NON_UI;
|
||||
}
|
||||
|
||||
|
@ -2028,6 +2038,12 @@ public class InCallController extends CallsManagerListenerBase {
|
|||
return mCallsManager.getAudioState().isMuted();
|
||||
}
|
||||
|
||||
private boolean isAppOpsPermittedManageOngoingCalls(int uid, String callingPackage) {
|
||||
return PermissionChecker.checkPermissionForPreflight(mContext,
|
||||
Manifest.permission.MANAGE_ONGOING_CALLS, PermissionChecker.PID_UNKNOWN, uid,
|
||||
callingPackage) == PermissionChecker.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
private void sendCrashedInCallServiceNotification(String packageName) {
|
||||
PackageManager packageManager = mContext.getPackageManager();
|
||||
CharSequence appName;
|
||||
|
|
|
@ -42,6 +42,7 @@ import android.content.ServiceConnection;
|
|||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Configuration;
|
||||
|
@ -485,6 +486,7 @@ public class ComponentContextFixture implements TestFixture<Context> {
|
|||
private final RoleManager mRoleManager = mock(RoleManager.class);
|
||||
private final TelephonyRegistryManager mTelephonyRegistryManager =
|
||||
mock(TelephonyRegistryManager.class);
|
||||
private final PermissionInfo mPermissionInfo = mock(PermissionInfo.class);
|
||||
|
||||
private TelecomManager mTelecomManager = mock(TelecomManager.class);
|
||||
|
||||
|
@ -539,6 +541,14 @@ public class ComponentContextFixture implements TestFixture<Context> {
|
|||
matches(Manifest.permission.CALL_COMPANION_APP), anyString()))
|
||||
.thenReturn(PackageManager.PERMISSION_DENIED);
|
||||
|
||||
try {
|
||||
when(mPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn(
|
||||
mPermissionInfo);
|
||||
} catch (PackageManager.NameNotFoundException ex) {
|
||||
}
|
||||
|
||||
when(mPermissionInfo.isAppOp()).thenReturn(true);
|
||||
|
||||
// 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
|
||||
// chosen.
|
||||
|
|
|
@ -52,6 +52,7 @@ import android.content.Intent;
|
|||
import android.content.ServiceConnection;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Resources;
|
||||
|
@ -122,6 +123,7 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
@Mock ClockProxy mClockProxy;
|
||||
@Mock Analytics.CallInfoImpl mCallInfo;
|
||||
@Mock NotificationManager mNotificationManager;
|
||||
@Mock PermissionInfo mMockPermissionInfo;
|
||||
|
||||
private static final int CURRENT_USER_ID = 900973;
|
||||
private static final String DEF_PKG = "defpkg";
|
||||
|
@ -142,6 +144,9 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
private static final String NONUI_PKG = "nonui_pkg";
|
||||
private static final String NONUI_CLASS = "nonui_cls";
|
||||
private static final int NONUI_UID = 6;
|
||||
private static final String APPOP_NONUI_PKG = "appop_nonui_pkg";
|
||||
private static final String APPOP_NONUI_CLASS = "appop_nonui_cls";
|
||||
private static final int APPOP_NONUI_UID = 7;
|
||||
|
||||
private static final PhoneAccountHandle PA_HANDLE =
|
||||
new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"), "pa_id");
|
||||
|
@ -173,6 +178,8 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter);
|
||||
when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
|
||||
.thenReturn(mNotificationManager);
|
||||
when(mMockPackageManager.getPermissionInfo(anyString(), anyInt())).thenReturn(
|
||||
mMockPermissionInfo);
|
||||
mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
|
||||
mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter,
|
||||
mEmergencyCallHelper, mCarModeTracker, mClockProxy);
|
||||
|
@ -198,6 +205,8 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
return new String[] { CAR2_PKG };
|
||||
case NONUI_UID:
|
||||
return new String[] { NONUI_PKG };
|
||||
case APPOP_NONUI_UID:
|
||||
return new String[] { APPOP_NONUI_PKG };
|
||||
}
|
||||
return null;
|
||||
}).when(mMockPackageManager).getPackagesForUid(anyInt());
|
||||
|
@ -213,6 +222,9 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
when(mMockPackageManager.checkPermission(
|
||||
matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
|
||||
matches(NONUI_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED);
|
||||
when(mMockPackageManager.checkPermission(
|
||||
matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
|
||||
matches(APPOP_NONUI_PKG))).thenReturn(PackageManager.PERMISSION_DENIED);
|
||||
when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0));
|
||||
}
|
||||
|
||||
|
@ -822,6 +834,49 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
verifyBinding(bindIntentCaptor, 0, DEF_PKG, DEF_CLASS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the {@link InCallController} will bind to an {@link InCallService} which
|
||||
* supports third party app
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testSanitizeContactName() throws Exception {
|
||||
|
@ -934,8 +989,8 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
nullable(ContentResolver.class))).thenReturn(500L);
|
||||
|
||||
when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall));
|
||||
setupMockPackageManager(true /* default */, true /* nonui */, true /* system */,
|
||||
false /* external calls */,
|
||||
setupMockPackageManager(true /* default */, true /* nonui */, false /* appop_nonui */ ,
|
||||
true /* system */, false /* external calls */,
|
||||
false /* self mgd in default*/, false /* self mgd in car*/);
|
||||
mInCallController.bindToServices(mMockCall);
|
||||
|
||||
|
@ -1195,9 +1250,21 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
}};
|
||||
}
|
||||
|
||||
private ResolveInfo getAppOpNonUiResolveinfo() {
|
||||
return new ResolveInfo() {{
|
||||
serviceInfo = new ServiceInfo();
|
||||
serviceInfo.packageName = APPOP_NONUI_PKG;
|
||||
serviceInfo.name = APPOP_NONUI_CLASS;
|
||||
serviceInfo.applicationInfo = new ApplicationInfo();
|
||||
serviceInfo.applicationInfo.uid = APPOP_NONUI_UID;
|
||||
serviceInfo.enabled = true;
|
||||
serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
|
||||
}};
|
||||
}
|
||||
|
||||
private void setupMockPackageManager(final boolean useDefaultDialer,
|
||||
final boolean useSystemDialer, final boolean includeExternalCalls) {
|
||||
setupMockPackageManager(useDefaultDialer, false, useSystemDialer, includeExternalCalls,
|
||||
setupMockPackageManager(useDefaultDialer, false, false, useSystemDialer, includeExternalCalls,
|
||||
false /* self mgd */, false /* self mgd */);
|
||||
}
|
||||
|
||||
|
@ -1205,13 +1272,13 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
final boolean useSystemDialer, final boolean includeExternalCalls,
|
||||
final boolean includeSelfManagedCallsInDefaultDialer,
|
||||
final boolean includeSelfManagedCallsInCarModeDialer) {
|
||||
setupMockPackageManager(useDefaultDialer, false /* nonui */, useSystemDialer,
|
||||
includeExternalCalls, includeSelfManagedCallsInDefaultDialer,
|
||||
setupMockPackageManager(useDefaultDialer, false /* nonui */, false /* appop_nonui */,
|
||||
useSystemDialer, includeExternalCalls, includeSelfManagedCallsInDefaultDialer,
|
||||
includeSelfManagedCallsInCarModeDialer);
|
||||
}
|
||||
|
||||
private void setupMockPackageManager(final boolean useDefaultDialer,
|
||||
final boolean useNonUiInCalls,
|
||||
final boolean useNonUiInCalls, final boolean useAppOpNonUiInCalls,
|
||||
final boolean useSystemDialer, final boolean includeExternalCalls,
|
||||
final boolean includeSelfManagedCallsInDefaultDialer,
|
||||
final boolean includeSelfManagedCallsInCarModeDialer) {
|
||||
|
@ -1254,6 +1321,10 @@ public class InCallControllerTests extends TelecomTestCase {
|
|||
if (useNonUiInCalls) {
|
||||
resolveInfo.add(getNonUiResolveinfo());
|
||||
}
|
||||
// InCallController uses a blank package name when querying for App Op non-ui incalls
|
||||
if (useAppOpNonUiInCalls) {
|
||||
resolveInfo.add(getAppOpNonUiResolveinfo());
|
||||
}
|
||||
}
|
||||
|
||||
return resolveInfo;
|
||||
|
|
Loading…
Reference in New Issue