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

Commit f924bced authored by paulhu's avatar paulhu
Browse files

Verify pending intent flags

All PendingIntent included activity and broadcast should set
FLAG_IMMUTABLE flag.

Bug: 156353008
Test: atest TetheringTests
Change-Id: I4592e914b67ecb8865d7de47797423f27e9ff840
parent a5a04bbb
Loading
Loading
Loading
Loading
+78 −15
Original line number Diff line number Diff line
@@ -18,33 +18,40 @@ package com.android.networkstack.tethering

import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.res.Resources
import android.net.ConnectivityManager.TETHERING_WIFI
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
import android.os.UserHandle
import android.provider.Settings
import android.telephony.TelephonyManager
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import com.android.internal.util.test.BroadcastInterceptingContext
import com.android.networkstack.tethering.TetheringNotificationUpdater.ACTION_DISABLE_TETHERING
import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.getSettingsPackageName
import com.android.testutils.waitForIdle
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Test
@@ -87,12 +94,17 @@ class TetheringNotificationUpdaterTest {
    // every test but should always be initialized before use (or the test should crash).
    private lateinit var context: TestContext
    private lateinit var notificationUpdater: TetheringNotificationUpdater

    // Initializing the following members depends on initializing some of the mocks and
    // is more logically done in setup().
    private lateinit var fakeTetheringThread: HandlerThread

    private val ROAMING_CAPABILITIES = NetworkCapabilities()
    private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING)
    private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general
    private val TIMEOUT_MS = 500L
    private val ACTIVITY_PENDING_INTENT = 0
    private val BROADCAST_PENDING_INTENT = 1

    private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
        override fun createContextAsUser(user: UserHandle, flags: Int) =
@@ -146,10 +158,43 @@ class TetheringNotificationUpdaterTest {
        fakeTetheringThread.quitSafely()
    }

    private fun verifyActivityPendingIntent(intent: Intent, flags: Int) {
        // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add
        // the flag in creating arguments). If the described PendingIntent does not already exist,
        // getActivity() will return null instead of PendingIntent object.
        val pi = PendingIntent.getActivity(
                context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
                0 /* requestCode */,
                intent,
                flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE,
                null /* options */)
        assertNotNull("Activity PendingIntent with FLAG_IMMUTABLE does not exist.", pi)
    }

    private fun verifyBroadcastPendingIntent(intent: Intent, flags: Int) {
        // Use FLAG_NO_CREATE to verify whether PendingIntent has FLAG_IMMUTABLE flag(forcefully add
        // the flag in creating arguments). If the described PendingIntent does not already exist,
        // getBroadcast() will return null instead of PendingIntent object.
        val pi = PendingIntent.getBroadcast(
                context.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
                0 /* requestCode */,
                intent,
                flags or FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE)
        assertNotNull("Broadcast PendingIntent with FLAG_IMMUTABLE does not exist.", pi)
    }

    private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
    private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)

    private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) {
    private fun verifyNotification(
        iconId: Int,
        title: String,
        text: String,
        id: Int,
        intentSenderType: Int,
        intent: Intent,
        flags: Int
    ) {
        verify(notificationManager, never()).cancel(any(), eq(id))

        val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
@@ -161,6 +206,11 @@ class TetheringNotificationUpdaterTest {
        assertEquals(title, notification.title())
        assertEquals(text, notification.text())

        when (intentSenderType) {
            ACTIVITY_PENDING_INTENT -> verifyActivityPendingIntent(intent, flags)
            BROADCAST_PENDING_INTENT -> verifyBroadcastPendingIntent(intent, flags)
        }

        reset(notificationManager)
    }

@@ -176,6 +226,10 @@ class TetheringNotificationUpdaterTest {

    @Test
    fun testRestrictedNotification() {
        val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS)
                .setPackage(getSettingsPackageName(context.packageManager))
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

        // Set test sub id.
        notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
@@ -183,7 +237,7 @@ class TetheringNotificationUpdaterTest {
        // User restrictions on. Show restricted notification.
        notificationUpdater.notifyTetheringDisabledByRestriction()
        verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
                RESTRICTED_NOTIFICATION_ID)
                RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)

        // User restrictions off. Clear notification.
        notificationUpdater.tetheringRestrictionLifted()
@@ -196,7 +250,7 @@ class TetheringNotificationUpdaterTest {
        // User restrictions on again. Show restricted notification.
        notificationUpdater.notifyTetheringDisabledByRestriction()
        verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE,
                RESTRICTED_NOTIFICATION_ID)
                RESTRICTED_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)
    }

    val MAX_BACKOFF_MS = 200L
@@ -234,6 +288,8 @@ class TetheringNotificationUpdaterTest {

    @Test
    fun testNoUpstreamNotification() {
        val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName)

        // Set test sub id.
        notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
@@ -246,7 +302,8 @@ class TetheringNotificationUpdaterTest {
        notificationUpdater.onUpstreamCapabilitiesChanged(null)
        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
        verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
                NO_UPSTREAM_NOTIFICATION_ID)
                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
                FLAG_IMMUTABLE)

        // Same capabilities changed. Nothing happened.
        notificationUpdater.onUpstreamCapabilitiesChanged(null)
@@ -260,7 +317,8 @@ class TetheringNotificationUpdaterTest {
        notificationUpdater.onUpstreamCapabilitiesChanged(null)
        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
        verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
                NO_UPSTREAM_NOTIFICATION_ID)
                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
                FLAG_IMMUTABLE)

        // No downstream.
        notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
@@ -305,6 +363,11 @@ class TetheringNotificationUpdaterTest {

    @Test
    fun testRoamingNotification() {
        val disableIntent = Intent(ACTION_DISABLE_TETHERING).setPackage(context.packageName)
        val settingsIntent = Intent(Settings.ACTION_TETHER_SETTINGS)
                .setPackage(getSettingsPackageName(context.packageManager))
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

        // Set test sub id.
        notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
        verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
@@ -316,7 +379,7 @@ class TetheringNotificationUpdaterTest {
        // Upstream capabilities changed to roaming state. Show roaming notification.
        notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
        verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
                ROAMING_NOTIFICATION_ID)
                ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)

        // Same capabilities change. Nothing happened.
        notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
@@ -329,14 +392,15 @@ class TetheringNotificationUpdaterTest {
        // Upstream capabilities changed to roaming state again. Show roaming notification.
        notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
        verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
                ROAMING_NOTIFICATION_ID)
                ROAMING_NOTIFICATION_ID, ACTIVITY_PENDING_INTENT, settingsIntent, FLAG_IMMUTABLE)

        // No upstream. Clear roaming notification and show no upstream notification.
        notificationUpdater.onUpstreamCapabilitiesChanged(null)
        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
        verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
        verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
                NO_UPSTREAM_NOTIFICATION_ID)
                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
                FLAG_IMMUTABLE)

        // No downstream.
        notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
@@ -347,7 +411,8 @@ class TetheringNotificationUpdaterTest {
        notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
        verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
        verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
                NO_UPSTREAM_NOTIFICATION_ID)
                NO_UPSTREAM_NOTIFICATION_ID, BROADCAST_PENDING_INTENT, disableIntent,
                FLAG_IMMUTABLE)

        // Set R.bool.config_upstream_roaming_notification to false and change upstream
        // network to roaming state again. No roaming notification.
@@ -363,8 +428,7 @@ class TetheringNotificationUpdaterTest {
        val testSettingsPackageName = "com.android.test.settings"
        val pm = mock(PackageManager::class.java)
        doReturn(null).`when`(pm).resolveActivity(any(), anyInt())
        assertEquals(defaultSettingsPackageName,
                TetheringNotificationUpdater.getSettingsPackageName(pm))
        assertEquals(defaultSettingsPackageName, getSettingsPackageName(pm))

        val resolveInfo = ResolveInfo().apply {
            activityInfo = ActivityInfo().apply {
@@ -375,7 +439,6 @@ class TetheringNotificationUpdaterTest {
            }
        }
        doReturn(resolveInfo).`when`(pm).resolveActivity(any(), anyInt())
        assertEquals(testSettingsPackageName,
                TetheringNotificationUpdater.getSettingsPackageName(pm))
        assertEquals(testSettingsPackageName, getSettingsPackageName(pm))
    }
}