Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3018d1dc authored by Thomas Stuart's avatar Thomas Stuart
Browse files

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
parent 3af1646b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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",
+19 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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
@@ -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;
    }

+74 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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";
@@ -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)
@@ -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
@@ -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 {