update when getDefaultDialerComponent sends a crash notif.
A recent change was made to InCallController#getDefaultDialerComponent
that sends a crash notification when the Default Dialer does not
implement an InCallService. However, role requirements are only
enforced for apps targeting T. Therefore, the change needs to be
stricter and an additional check has been added to ensure only
apps targeting T and above will receive the notification.
bug: 218903401
Test: 1 new unit test, 1 updated
Change-Id: I98df1e9a97b0184b924505ce3db46b8c689b96d2
diff --git a/Android.bp b/Android.bp
index 88cffb8..1b422aa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -54,7 +54,8 @@
"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",
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index e29d905..30c5ccb 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -22,8 +22,11 @@
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;
@@ -37,6 +40,7 @@
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;
@@ -87,6 +91,16 @@
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
@@ -1620,10 +1634,14 @@
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;
}
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 79032da..dfc41a2 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -64,6 +64,7 @@
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;
@@ -102,7 +103,9 @@
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;
@@ -120,6 +123,8 @@
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;
@@ -139,6 +144,9 @@
@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";
@@ -909,10 +917,13 @@
/**
* 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)
@@ -926,6 +937,7 @@
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
@@ -1005,6 +1017,67 @@
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 {