Loading Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,8 @@ android_test { "androidx.legacy_legacy-support-core-utils", "androidx.core_core", "androidx.fragment_fragment", "androidx.test.ext.junit" "androidx.test.ext.junit", "platform-compat-test-rules", ], srcs: [ "tests/src/**/*.java", Loading src/com/android/server/telecom/InCallController.java +19 −1 Original line number Diff line number Diff line Loading @@ -22,8 +22,11 @@ import static android.os.Process.myUid; import android.Manifest; import android.annotation.NonNull; import android.app.AppOpsManager; import android.app.compat.CompatChanges; import android.app.Notification; import android.app.NotificationManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ComponentName; Loading @@ -37,6 +40,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.hardware.SensorPrivacyManager; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; Loading Loading @@ -87,6 +91,16 @@ public class InCallController extends CallsManagerListenerBase implements public static final String NOTIFICATION_TAG = InCallController.class.getSimpleName(); public static final int IN_CALL_SERVICE_NOTIFICATION_ID = 3; /** * Enable a crash notification if the default dialer app does not implement the * {@link InCallService} and the system Dialer takes over. * * @hide */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH = 218903401L; // bug id public class InCallServiceConnection { /** * Indicates that a call to {@link #connect(Call)} has succeeded and resulted in a Loading Loading @@ -1620,10 +1634,14 @@ public class InCallController extends CallsManagerListenerBase implements true /* ignoreDisabled */) : getInCallServiceComponent(packageName, IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI, true /* ignoreDisabled */); if (packageName != null && defaultDialerComponent == null) { if (packageName != null && defaultDialerComponent == null && CompatChanges.isChangeEnabled(ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH, Binder.getCallingUid())) { // The in call service of default phone app is disabled, send notification. sendCrashedInCallServiceNotification(packageName); } return defaultDialerComponent; } Loading tests/src/com/android/server/telecom/tests/InCallControllerTests.java +74 −1 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.compat.testing.PlatformCompatChangeRule; import android.os.Build; import android.os.Bundle; import android.os.Handler; Loading Loading @@ -102,7 +103,9 @@ import com.android.server.telecom.Timeouts; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; Loading @@ -120,6 +123,8 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; import libcore.junit.util.compat.CoreCompatChangeRule; @RunWith(JUnit4.class) public class InCallControllerTests extends TelecomTestCase { @Mock CallsManager mMockCallsManager; Loading @@ -139,6 +144,9 @@ public class InCallControllerTests extends TelecomTestCase { @Mock NotificationManager mNotificationManager; @Mock PermissionInfo mMockPermissionInfo; @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); private static final int CURRENT_USER_ID = 900973; private static final String DEF_PKG = "defpkg"; private static final String DEF_CLASS = "defcls"; Loading Loading @@ -909,10 +917,13 @@ public class InCallControllerTests extends TelecomTestCase { /** * Ensures that the {@link InCallController} will bind to an {@link InCallService} which * supports third party app * supports third party app. Also, we want to verify a notification is sent to apps targeting * Tiramisu and above when the InCallService of the default app is disabled. */ @MediumTest @Test @CoreCompatChangeRule.EnableCompatChanges({ InCallController.ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH}) public void testBindToService_ThirdPartyApp() throws Exception { final MockitoSession mockitoSession = ExtendedMockito.mockitoSession() .strictness(Strictness.WARN) Loading @@ -926,6 +937,7 @@ public class InCallControllerTests extends TelecomTestCase { ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU; // set up mock call for ICSC#sendCrashedInCallServiceNotification(String) when(mMockContext.getApplicationInfo()).thenReturn(applicationInfo); // Enable Third Party Companion App Loading Loading @@ -1005,6 +1017,67 @@ public class InCallControllerTests extends TelecomTestCase { eq(InCallController.IN_CALL_SERVICE_NOTIFICATION_ID), any()); } /** * Ensures that the {@link InCallController} will bind to an {@link InCallService} which * supports third party app. Also, we want to verify a notification is NOT sent to apps * targeting below Tiramisu when the InCallService of the default app is disabled. */ @MediumTest @Test @CoreCompatChangeRule.DisableCompatChanges({ InCallController.ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH}) public void testBindToService_ThirdPartyAppBelowTiramisu() throws Exception { 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*/); ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.targetSdkVersion = Build.VERSION_CODES.S_V2; // set up mock call for ICSC#sendCrashedInCallServiceNotification(String) when(mMockContext.getApplicationInfo()).thenReturn(applicationInfo); // 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); // Verify notification is NOT sent by NotificationManager verify(mNotificationManager, times(0)).notify(eq(InCallController.NOTIFICATION_TAG), eq(InCallController.IN_CALL_SERVICE_NOTIFICATION_ID), any()); } finally { mockitoSession.finishMocking(); } } @MediumTest @Test public void testSanitizeContactName() throws Exception { Loading Loading
Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,8 @@ android_test { "androidx.legacy_legacy-support-core-utils", "androidx.core_core", "androidx.fragment_fragment", "androidx.test.ext.junit" "androidx.test.ext.junit", "platform-compat-test-rules", ], srcs: [ "tests/src/**/*.java", Loading
src/com/android/server/telecom/InCallController.java +19 −1 Original line number Diff line number Diff line Loading @@ -22,8 +22,11 @@ import static android.os.Process.myUid; import android.Manifest; import android.annotation.NonNull; import android.app.AppOpsManager; import android.app.compat.CompatChanges; import android.app.Notification; import android.app.NotificationManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ComponentName; Loading @@ -37,6 +40,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.hardware.SensorPrivacyManager; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; Loading Loading @@ -87,6 +91,16 @@ public class InCallController extends CallsManagerListenerBase implements public static final String NOTIFICATION_TAG = InCallController.class.getSimpleName(); public static final int IN_CALL_SERVICE_NOTIFICATION_ID = 3; /** * Enable a crash notification if the default dialer app does not implement the * {@link InCallService} and the system Dialer takes over. * * @hide */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH = 218903401L; // bug id public class InCallServiceConnection { /** * Indicates that a call to {@link #connect(Call)} has succeeded and resulted in a Loading Loading @@ -1620,10 +1634,14 @@ public class InCallController extends CallsManagerListenerBase implements true /* ignoreDisabled */) : getInCallServiceComponent(packageName, IN_CALL_SERVICE_TYPE_DEFAULT_DIALER_UI, true /* ignoreDisabled */); if (packageName != null && defaultDialerComponent == null) { if (packageName != null && defaultDialerComponent == null && CompatChanges.isChangeEnabled(ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH, Binder.getCallingUid())) { // The in call service of default phone app is disabled, send notification. sendCrashedInCallServiceNotification(packageName); } return defaultDialerComponent; } Loading
tests/src/com/android/server/telecom/tests/InCallControllerTests.java +74 −1 Original line number Diff line number Diff line Loading @@ -64,6 +64,7 @@ import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.compat.testing.PlatformCompatChangeRule; import android.os.Build; import android.os.Bundle; import android.os.Handler; Loading Loading @@ -102,7 +103,9 @@ import com.android.server.telecom.Timeouts; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; Loading @@ -120,6 +123,8 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; import libcore.junit.util.compat.CoreCompatChangeRule; @RunWith(JUnit4.class) public class InCallControllerTests extends TelecomTestCase { @Mock CallsManager mMockCallsManager; Loading @@ -139,6 +144,9 @@ public class InCallControllerTests extends TelecomTestCase { @Mock NotificationManager mNotificationManager; @Mock PermissionInfo mMockPermissionInfo; @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); private static final int CURRENT_USER_ID = 900973; private static final String DEF_PKG = "defpkg"; private static final String DEF_CLASS = "defcls"; Loading Loading @@ -909,10 +917,13 @@ public class InCallControllerTests extends TelecomTestCase { /** * Ensures that the {@link InCallController} will bind to an {@link InCallService} which * supports third party app * supports third party app. Also, we want to verify a notification is sent to apps targeting * Tiramisu and above when the InCallService of the default app is disabled. */ @MediumTest @Test @CoreCompatChangeRule.EnableCompatChanges({ InCallController.ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH}) public void testBindToService_ThirdPartyApp() throws Exception { final MockitoSession mockitoSession = ExtendedMockito.mockitoSession() .strictness(Strictness.WARN) Loading @@ -926,6 +937,7 @@ public class InCallControllerTests extends TelecomTestCase { ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU; // set up mock call for ICSC#sendCrashedInCallServiceNotification(String) when(mMockContext.getApplicationInfo()).thenReturn(applicationInfo); // Enable Third Party Companion App Loading Loading @@ -1005,6 +1017,67 @@ public class InCallControllerTests extends TelecomTestCase { eq(InCallController.IN_CALL_SERVICE_NOTIFICATION_ID), any()); } /** * Ensures that the {@link InCallController} will bind to an {@link InCallService} which * supports third party app. Also, we want to verify a notification is NOT sent to apps * targeting below Tiramisu when the InCallService of the default app is disabled. */ @MediumTest @Test @CoreCompatChangeRule.DisableCompatChanges({ InCallController.ENABLE_NOTIFICATION_FOR_DEFAULT_DIALER_CRASH}) public void testBindToService_ThirdPartyAppBelowTiramisu() throws Exception { 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*/); ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.targetSdkVersion = Build.VERSION_CODES.S_V2; // set up mock call for ICSC#sendCrashedInCallServiceNotification(String) when(mMockContext.getApplicationInfo()).thenReturn(applicationInfo); // 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); // Verify notification is NOT sent by NotificationManager verify(mNotificationManager, times(0)).notify(eq(InCallController.NOTIFICATION_TAG), eq(InCallController.IN_CALL_SERVICE_NOTIFICATION_ID), any()); } finally { mockitoSession.finishMocking(); } } @MediumTest @Test public void testSanitizeContactName() throws Exception { Loading