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

Commit b1ff20e4 authored by Sarah Chin's avatar Sarah Chin
Browse files

Slice upsell fail if notifications are disabled

If notifications are disabled for the app or channel,
fail the purchase request and notify SlicePurchaseController.
Create a new PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED result for
this scenario.

Test: atest CarrierDefaultAppTest
Test: manual verify failure when notifications are disabled
Test: manual verify no regressions of other behavior
Bug: 258083232
Change-Id: I4a3d2b09320edfa232cf87e6235371dc0d84c224
parent bbbb0233
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -46015,6 +46015,7 @@ package android.telephony {
    field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED = 2; // 0x2
    field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT = 9; // 0x9
    field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED = 6; // 0x6
    field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 16; // 0x10
    field public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; // 0x2
    field public static final int SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE = 3; // 0x3
    field public static final int SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION = 4; // 0x4
+38 −14
Original line number Diff line number Diff line
@@ -191,6 +191,8 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
                && isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED)
                && isPendingIntentValid(intent,
                        SlicePurchaseController.EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION)
                && isPendingIntentValid(intent,
                        SlicePurchaseController.EXTRA_INTENT_NOTIFICATIONS_DISABLED)
                && isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_SUCCESS)
                && isPendingIntentValid(intent,
                        SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN);
@@ -276,6 +278,8 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
            case SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED: return "request failed";
            case SlicePurchaseController.EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION:
                return "not default data subscription";
            case SlicePurchaseController.EXTRA_INTENT_NOTIFICATIONS_DISABLED:
                return "notifications disabled";
            case SlicePurchaseController.EXTRA_INTENT_SUCCESS: return "success";
            case SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN:
                return "notification shown";
@@ -321,26 +325,45 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
    }

    private void onDisplayPerformanceBoostNotification(@NonNull Context context,
            @NonNull Intent intent, boolean repeat) {
        if (!repeat && !isIntentValid(intent)) {
            @NonNull Intent intent, boolean localeChanged) {
        if (!localeChanged && !isIntentValid(intent)) {
            sendSlicePurchaseAppResponse(intent,
                    SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED);
            return;
        }

        Resources res = getResources(context);
        NotificationChannel channel = new NotificationChannel(
        NotificationManager notificationManager =
                context.getSystemService(NotificationManager.class);
        NotificationChannel channel = notificationManager.getNotificationChannel(
                PERFORMANCE_BOOST_NOTIFICATION_CHANNEL_ID);
        if (channel == null) {
            channel = new NotificationChannel(
                    PERFORMANCE_BOOST_NOTIFICATION_CHANNEL_ID,
                    res.getString(R.string.performance_boost_notification_channel),
                    NotificationManager.IMPORTANCE_DEFAULT);
        // CarrierDefaultApp notifications are unblockable by default. Make this channel blockable
        //  to allow users to disable notifications posted to this channel without affecting other
        //  notifications in this application.
            // CarrierDefaultApp notifications are unblockable by default.
            // Make this channel blockable to allow users to disable notifications posted to this
            // channel without affecting other notifications in this application.
            channel.setBlockable(true);
            context.getSystemService(NotificationManager.class).createNotificationChannel(channel);
        } else if (localeChanged) {
            // If the channel already exists but the locale has changed, update the channel name.
            channel.setName(res.getString(R.string.performance_boost_notification_channel));
        }

        String carrier = intent.getStringExtra(SlicePurchaseController.EXTRA_CARRIER);
        boolean channelNotificationsDisabled =
                channel.getImportance() == NotificationManager.IMPORTANCE_NONE;
        if (channelNotificationsDisabled || !notificationManager.areNotificationsEnabled()) {
            // If notifications are disabled for the app or channel, fail the purchase request.
            logd("Purchase request failed because notifications are disabled for the "
                    + (channelNotificationsDisabled ? "channel." : "application."));
            sendSlicePurchaseAppResponse(intent,
                    SlicePurchaseController.EXTRA_INTENT_NOTIFICATIONS_DISABLED);
            return;
        }

        String carrier = intent.getStringExtra(SlicePurchaseController.EXTRA_CARRIER);
        Notification notification =
                new Notification.Builder(context, PERFORMANCE_BOOST_NOTIFICATION_CHANNEL_ID)
                        .setContentTitle(res.getString(
@@ -369,11 +392,12 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{

        int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
                SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
        logd((repeat ? "Update" : "Display") + " the performance boost notification for capability "
        logd((localeChanged ? "Update" : "Display")
                + " the performance boost notification for capability "
                + TelephonyManager.convertPremiumCapabilityToString(capability));
        context.getSystemService(NotificationManager.class).notifyAsUser(
                PERFORMANCE_BOOST_NOTIFICATION_TAG, capability, notification, UserHandle.ALL);
        if (!repeat) {
        if (!localeChanged) {
            sIntents.put(capability, intent);
            sendSlicePurchaseAppResponse(intent,
                    SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN);
+31 −6
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -72,6 +73,7 @@ public class SlicePurchaseBroadcastReceiverTest {
    @Mock PendingIntent mContentIntent1;
    @Mock PendingIntent mContentIntent2;
    @Mock PendingIntent mNotificationShownIntent;
    @Mock PendingIntent mNotificationsDisabledIntent;
    @Mock Context mContext;
    @Mock Resources mResources;
    @Mock Configuration mConfiguration;
@@ -90,6 +92,7 @@ public class SlicePurchaseBroadcastReceiverTest {
        doReturn("").when(mResources).getString(anyInt());
        doReturn(mNotificationManager).when(mContext)
                .getSystemService(eq(NotificationManager.class));
        doReturn(true).when(mNotificationManager).areNotificationsEnabled();
        doReturn(mApplicationInfo).when(mContext).getApplicationInfo();
        doReturn(mPackageManager).when(mContext).getPackageManager();
        doReturn(mSpiedResources).when(mContext).getResources();
@@ -221,12 +224,10 @@ public class SlicePurchaseBroadcastReceiverTest {
        doReturn(true).when(mPendingIntent).isBroadcast();
        doReturn(mPendingIntent).when(mIntent).getParcelableExtra(
                anyString(), eq(PendingIntent.class));
        doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mNotificationShownIntent)
                .getCreatorPackage();
        doReturn(true).when(mNotificationShownIntent).isBroadcast();
        doReturn(mNotificationShownIntent).when(mIntent).getParcelableExtra(
                eq(SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN),
                eq(PendingIntent.class));
        createValidPendingIntent(mNotificationShownIntent,
                SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN);
        createValidPendingIntent(mNotificationsDisabledIntent,
                SlicePurchaseController.EXTRA_INTENT_NOTIFICATIONS_DISABLED);

        // spy notification intents to prevent PendingIntent issues
        doReturn(mContentIntent1).when(mSlicePurchaseBroadcastReceiver).createContentIntent(
@@ -253,6 +254,12 @@ public class SlicePurchaseBroadcastReceiverTest {
        mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
    }

    private void createValidPendingIntent(@NonNull PendingIntent intent, @NonNull String extra) {
        doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(intent).getCreatorPackage();
        doReturn(true).when(intent).isBroadcast();
        doReturn(intent).when(mIntent).getParcelableExtra(eq(extra), eq(PendingIntent.class));
    }

    @Test
    public void testNotificationCanceled() {
        // send ACTION_NOTIFICATION_CANCELED
@@ -335,4 +342,22 @@ public class SlicePurchaseBroadcastReceiverTest {
        clearInvocations(mConfiguration);
        return captor.getValue();
    }

    @Test
    public void testNotificationsDisabled() throws Exception {
        doReturn(false).when(mNotificationManager).areNotificationsEnabled();

        displayPerformanceBoostNotification();

        // verify notification was not shown
        verify(mNotificationManager, never()).notifyAsUser(
                eq(SlicePurchaseBroadcastReceiver.PERFORMANCE_BOOST_NOTIFICATION_TAG),
                eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
                any(),
                eq(UserHandle.ALL));
        verify(mNotificationShownIntent, never()).send();

        // verify SlicePurchaseController was notified that notifications are disabled
        verify(mNotificationsDisabledIntent).send();
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -9628,6 +9628,7 @@ public class CarrierConfigManager {
     *
     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
     * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED
     */
    public static final String
            KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG =
+11 −1
Original line number Diff line number Diff line
@@ -17601,6 +17601,15 @@ public class TelephonyManager {
     */
    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP = 15;
    /**
     * Purchase premium capability failed because the user disabled the feature.
     * Subsequent attempts will be throttled for the amount of time specified by
     * {@link CarrierConfigManager
     * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
     * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
     */
    public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 16;
    /**
     * Results of the purchase premium capability request.
     * @hide
@@ -17620,7 +17629,8 @@ public class TelephonyManager {
            PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
            PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED,
            PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
            PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP})
            PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP,
            PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED})
    public @interface PurchasePremiumCapabilityResult {}
    /**