Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
markchien | 503be61 | 2020-04-12 21:41:29 +0800 | [diff] [blame] | 17 | package com.android.networkstack.tethering |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 18 | |
| 19 | import android.app.Notification |
| 20 | import android.app.NotificationManager |
| 21 | import android.content.Context |
| 22 | import android.content.res.Resources |
| 23 | import android.net.ConnectivityManager.TETHERING_BLUETOOTH |
| 24 | import android.net.ConnectivityManager.TETHERING_USB |
| 25 | import android.net.ConnectivityManager.TETHERING_WIFI |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 26 | import android.net.Network |
| 27 | import android.os.Handler |
| 28 | import android.os.HandlerThread |
| 29 | import android.os.Looper |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 30 | import android.os.UserHandle |
| 31 | import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 32 | import android.telephony.TelephonyManager |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 33 | import androidx.test.filters.SmallTest |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 34 | import androidx.test.platform.app.InstrumentationRegistry |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 35 | import androidx.test.runner.AndroidJUnit4 |
| 36 | import com.android.internal.util.test.BroadcastInterceptingContext |
markchien | 503be61 | 2020-04-12 21:41:29 +0800 | [diff] [blame] | 37 | import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 38 | import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NOTIFICATION_ID |
| 39 | import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM |
| 40 | import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID |
| 41 | import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID |
| 42 | import com.android.testutils.waitForIdle |
| 43 | import org.junit.After |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 44 | import org.junit.Assert.assertEquals |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 45 | import org.junit.Assert.fail |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 46 | import org.junit.Before |
| 47 | import org.junit.Test |
| 48 | import org.junit.runner.RunWith |
| 49 | import org.mockito.ArgumentCaptor |
| 50 | import org.mockito.ArgumentMatchers.any |
| 51 | import org.mockito.ArgumentMatchers.anyInt |
| 52 | import org.mockito.ArgumentMatchers.eq |
| 53 | import org.mockito.Mock |
| 54 | import org.mockito.Mockito.doReturn |
| 55 | import org.mockito.Mockito.never |
| 56 | import org.mockito.Mockito.reset |
| 57 | import org.mockito.Mockito.times |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 58 | import org.mockito.Mockito.verify |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 59 | import org.mockito.Mockito.verifyZeroInteractions |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 60 | import org.mockito.MockitoAnnotations |
| 61 | |
| 62 | const val TEST_SUBID = 1 |
| 63 | const val WIFI_ICON_ID = 1 |
| 64 | const val USB_ICON_ID = 2 |
| 65 | const val BT_ICON_ID = 3 |
| 66 | const val GENERAL_ICON_ID = 4 |
| 67 | const val WIFI_MASK = 1 shl TETHERING_WIFI |
| 68 | const val USB_MASK = 1 shl TETHERING_USB |
| 69 | const val BT_MASK = 1 shl TETHERING_BLUETOOTH |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 70 | const val TITLE = "Tethering active" |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 71 | const val MESSAGE = "Tap here to set up." |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 72 | const val TEST_TITLE = "Hotspot active" |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 73 | const val TEST_MESSAGE = "Tap to set up hotspot." |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 74 | const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access" |
| 75 | const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet." |
| 76 | const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot" |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 77 | |
| 78 | @RunWith(AndroidJUnit4::class) |
| 79 | @SmallTest |
| 80 | class TetheringNotificationUpdaterTest { |
| 81 | // lateinit used here for mocks as they need to be reinitialized between each test and the test |
| 82 | // should crash if they are used before being initialized. |
| 83 | @Mock private lateinit var mockContext: Context |
| 84 | @Mock private lateinit var notificationManager: NotificationManager |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 85 | @Mock private lateinit var telephonyManager: TelephonyManager |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 86 | @Mock private lateinit var defaultResources: Resources |
| 87 | @Mock private lateinit var testResources: Resources |
| 88 | |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 89 | // lateinit for these classes under test, as they should be reset to a different instance for |
| 90 | // every test but should always be initialized before use (or the test should crash). |
| 91 | private lateinit var context: TestContext |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 92 | private lateinit var notificationUpdater: TetheringNotificationUpdater |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 93 | private lateinit var fakeTetheringThread: HandlerThread |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 94 | |
| 95 | private val ENABLE_ICON_CONFIGS = arrayOf( |
| 96 | "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth", |
| 97 | "WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general", |
| 98 | "USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general") |
| 99 | |
| 100 | private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) { |
| 101 | override fun createContextAsUser(user: UserHandle, flags: Int) = |
| 102 | if (user == UserHandle.ALL) mockContext else this |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 103 | override fun getSystemService(name: String) = |
| 104 | if (name == Context.TELEPHONY_SERVICE) telephonyManager |
| 105 | else super.getSystemService(name) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 106 | } |
| 107 | |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 108 | private inner class WrappedNotificationUpdater(c: Context, looper: Looper) |
| 109 | : TetheringNotificationUpdater(c, looper) { |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 110 | override fun getResourcesForSubId(context: Context, subId: Int) = |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 111 | when (subId) { |
| 112 | TEST_SUBID -> testResources |
| 113 | INVALID_SUBSCRIPTION_ID -> defaultResources |
| 114 | else -> super.getResourcesForSubId(context, subId) |
| 115 | } |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | private fun setupResources() { |
| 119 | doReturn(ENABLE_ICON_CONFIGS).`when`(defaultResources) |
| 120 | .getStringArray(R.array.tethering_notification_icons) |
| 121 | doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources) |
| 122 | .getStringArray(R.array.tethering_notification_icons) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 123 | doReturn(5).`when`(testResources) |
| 124 | .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) |
| 125 | doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 126 | doReturn(MESSAGE).`when`(defaultResources) |
| 127 | .getString(R.string.tethering_notification_message) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 128 | doReturn(TEST_TITLE).`when`(testResources).getString(R.string.tethering_notification_title) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 129 | doReturn(TEST_MESSAGE).`when`(testResources) |
| 130 | .getString(R.string.tethering_notification_message) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 131 | doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources) |
| 132 | .getString(R.string.no_upstream_notification_title) |
| 133 | doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources) |
| 134 | .getString(R.string.no_upstream_notification_message) |
| 135 | doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources) |
| 136 | .getString(R.string.no_upstream_notification_disable_button) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 137 | doReturn(USB_ICON_ID).`when`(defaultResources) |
| 138 | .getIdentifier(eq("android.test:drawable/usb"), any(), any()) |
| 139 | doReturn(BT_ICON_ID).`when`(defaultResources) |
| 140 | .getIdentifier(eq("android.test:drawable/bluetooth"), any(), any()) |
| 141 | doReturn(GENERAL_ICON_ID).`when`(defaultResources) |
| 142 | .getIdentifier(eq("android.test:drawable/general"), any(), any()) |
| 143 | doReturn(WIFI_ICON_ID).`when`(testResources) |
| 144 | .getIdentifier(eq("android.test:drawable/wifi"), any(), any()) |
| 145 | } |
| 146 | |
| 147 | @Before |
| 148 | fun setUp() { |
| 149 | MockitoAnnotations.initMocks(this) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 150 | context = TestContext(InstrumentationRegistry.getInstrumentation().context) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 151 | doReturn(notificationManager).`when`(mockContext) |
| 152 | .getSystemService(Context.NOTIFICATION_SERVICE) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 153 | fakeTetheringThread = HandlerThread(this::class.simpleName) |
| 154 | fakeTetheringThread.start() |
| 155 | notificationUpdater = WrappedNotificationUpdater(context, fakeTetheringThread.looper) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 156 | setupResources() |
| 157 | } |
| 158 | |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 159 | @After |
| 160 | fun tearDown() { |
| 161 | fakeTetheringThread.quitSafely() |
| 162 | } |
| 163 | |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 164 | private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE) |
| 165 | private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT) |
| 166 | |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 167 | private fun verifyNotification(iconId: Int, title: String, text: String, id: Int) { |
| 168 | verify(notificationManager, never()).cancel(any(), eq(id)) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 169 | |
| 170 | val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) |
Paul Hu | f950dc9 | 2020-03-25 12:39:13 +0000 | [diff] [blame] | 171 | verify(notificationManager, times(1)) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 172 | .notify(any(), eq(id), notificationCaptor.capture()) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 173 | |
| 174 | val notification = notificationCaptor.getValue() |
| 175 | assertEquals(iconId, notification.smallIcon.resId) |
| 176 | assertEquals(title, notification.title()) |
| 177 | assertEquals(text, notification.text()) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 178 | } |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 179 | |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 180 | private fun verifyNotificationCancelled(id: Int) = |
| 181 | verify(notificationManager, times(1)).cancel(any(), eq(id)) |
| 182 | |
| 183 | private val tetheringActiveNotifications = |
| 184 | listOf(NO_UPSTREAM_NOTIFICATION_ID, ENABLE_NOTIFICATION_ID) |
| 185 | |
| 186 | private fun verifyCancelAllTetheringActiveNotifications() { |
| 187 | tetheringActiveNotifications.forEach { |
| 188 | verifyNotificationCancelled(it) |
| 189 | } |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 190 | reset(notificationManager) |
| 191 | } |
| 192 | |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 193 | private fun verifyOnlyTetheringActiveNotification( |
| 194 | notifyId: Int, |
| 195 | iconId: Int, |
| 196 | title: String, |
| 197 | text: String |
| 198 | ) { |
| 199 | tetheringActiveNotifications.forEach { |
| 200 | when (it) { |
| 201 | notifyId -> verifyNotification(iconId, title, text, notifyId) |
| 202 | else -> verifyNotificationCancelled(it) |
| 203 | } |
| 204 | } |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 205 | reset(notificationManager) |
| 206 | } |
| 207 | |
| 208 | @Test |
| 209 | fun testNotificationWithDownstreamChanged() { |
| 210 | // Wifi downstream. No notification. |
| 211 | notificationUpdater.onDownstreamChanged(WIFI_MASK) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 212 | verifyCancelAllTetheringActiveNotifications() |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 213 | |
| 214 | // Same downstream changed. Nothing happened. |
| 215 | notificationUpdater.onDownstreamChanged(WIFI_MASK) |
| 216 | verifyZeroInteractions(notificationManager) |
| 217 | |
| 218 | // Wifi and usb downstreams. Show enable notification |
| 219 | notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 220 | verifyOnlyTetheringActiveNotification( |
| 221 | ENABLE_NOTIFICATION_ID, GENERAL_ICON_ID, TITLE, MESSAGE) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 222 | |
| 223 | // Usb downstream. Still show enable notification. |
| 224 | notificationUpdater.onDownstreamChanged(USB_MASK) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 225 | verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 226 | |
| 227 | // No downstream. No notification. |
| 228 | notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 229 | verifyCancelAllTetheringActiveNotifications() |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | @Test |
| 233 | fun testNotificationWithActiveDataSubscriptionIdChanged() { |
| 234 | // Usb downstream. Showed enable notification with default resource. |
| 235 | notificationUpdater.onDownstreamChanged(USB_MASK) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 236 | verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 237 | |
| 238 | // Same subId changed. Nothing happened. |
| 239 | notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) |
| 240 | verifyZeroInteractions(notificationManager) |
| 241 | |
| 242 | // Set test sub id. Clear notification with test resource. |
| 243 | notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 244 | verifyCancelAllTetheringActiveNotifications() |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 245 | |
| 246 | // Wifi downstream. Show enable notification with test resource. |
| 247 | notificationUpdater.onDownstreamChanged(WIFI_MASK) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 248 | verifyOnlyTetheringActiveNotification( |
| 249 | ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 250 | |
| 251 | // No downstream. No notification. |
| 252 | notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 253 | verifyCancelAllTetheringActiveNotifications() |
Paul Hu | 8d69280 | 2020-03-25 07:53:03 +0000 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | private fun assertIconNumbers(number: Int, configs: Array<String?>) { |
| 257 | doReturn(configs).`when`(defaultResources) |
| 258 | .getStringArray(R.array.tethering_notification_icons) |
| 259 | assertEquals(number, notificationUpdater.getIcons( |
| 260 | R.array.tethering_notification_icons, defaultResources).size()) |
| 261 | } |
| 262 | |
| 263 | @Test |
| 264 | fun testGetIcons() { |
| 265 | assertIconNumbers(0, arrayOfNulls<String>(0)) |
| 266 | assertIconNumbers(0, arrayOf(null, "")) |
| 267 | assertIconNumbers(3, arrayOf( |
| 268 | // These configurations are invalid with wrong strings or symbols. |
| 269 | ";", ",", "|", "|,;", "WIFI", "1;2", " U SB; ", "bt;", "WIFI;USB;BT", "WIFI|USB|BT", |
| 270 | "WIFI,BT,USB", " WIFI| | | USB, test:drawable/test", |
| 271 | // This configuration is valid with two downstream types (USB, BT). |
| 272 | "USB|,,,,,|BT;drawable/test ", |
| 273 | // This configuration is valid with one downstream types (WIFI). |
| 274 | " WIFI ; android.test:drawable/xxx ")) |
| 275 | } |
| 276 | |
| 277 | @Test |
| 278 | fun testGetDownstreamTypesMask() { |
| 279 | assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("")) |
| 280 | assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("1")) |
| 281 | assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("WIFI_P2P")) |
| 282 | assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("usb")) |
| 283 | assertEquals(WIFI_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI ")) |
| 284 | assertEquals(USB_MASK, notificationUpdater.getDownstreamTypesMask("USB | B T")) |
| 285 | assertEquals(BT_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI: | BT")) |
| 286 | assertEquals(WIFI_MASK or USB_MASK, |
| 287 | notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) |
| 288 | } |
Paul Hu | f950dc9 | 2020-03-25 12:39:13 +0000 | [diff] [blame] | 289 | |
| 290 | @Test |
| 291 | fun testSetupRestrictedNotification() { |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 292 | val title = context.resources.getString(R.string.disable_tether_notification_title) |
| 293 | val message = context.resources.getString(R.string.disable_tether_notification_message) |
Paul Hu | f950dc9 | 2020-03-25 12:39:13 +0000 | [diff] [blame] | 294 | val disallowTitle = "Tether function is disallowed" |
| 295 | val disallowMessage = "Please contact your admin" |
| 296 | doReturn(title).`when`(defaultResources) |
| 297 | .getString(R.string.disable_tether_notification_title) |
| 298 | doReturn(message).`when`(defaultResources) |
| 299 | .getString(R.string.disable_tether_notification_message) |
| 300 | doReturn(disallowTitle).`when`(testResources) |
| 301 | .getString(R.string.disable_tether_notification_title) |
| 302 | doReturn(disallowMessage).`when`(testResources) |
| 303 | .getString(R.string.disable_tether_notification_message) |
| 304 | |
| 305 | // User restrictions on. Show restricted notification. |
| 306 | notificationUpdater.notifyTetheringDisabledByRestriction() |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 307 | verifyNotification(R.drawable.stat_sys_tether_general, title, message, |
| 308 | RESTRICTED_NOTIFICATION_ID) |
| 309 | reset(notificationManager) |
Paul Hu | f950dc9 | 2020-03-25 12:39:13 +0000 | [diff] [blame] | 310 | |
| 311 | // User restrictions off. Clear notification. |
| 312 | notificationUpdater.tetheringRestrictionLifted() |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 313 | verifyNotificationCancelled(RESTRICTED_NOTIFICATION_ID) |
| 314 | reset(notificationManager) |
Paul Hu | f950dc9 | 2020-03-25 12:39:13 +0000 | [diff] [blame] | 315 | |
| 316 | // Set test sub id. No notification. |
| 317 | notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 318 | verifyCancelAllTetheringActiveNotifications() |
Paul Hu | f950dc9 | 2020-03-25 12:39:13 +0000 | [diff] [blame] | 319 | |
| 320 | // User restrictions on again. Show restricted notification with test resource. |
| 321 | notificationUpdater.notifyTetheringDisabledByRestriction() |
Paul Hu | 77fa8d6 | 2020-04-16 02:54:37 +0000 | [diff] [blame] | 322 | verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage, |
| 323 | RESTRICTED_NOTIFICATION_ID) |
| 324 | reset(notificationManager) |
| 325 | } |
| 326 | |
| 327 | val MAX_BACKOFF_MS = 200L |
| 328 | /** |
| 329 | * Waits for all messages, including delayed ones, to be processed. |
| 330 | * |
| 331 | * This will wait until the handler has no more messages to be processed including |
| 332 | * delayed ones, or the timeout has expired. It uses an exponential backoff strategy |
| 333 | * to wait longer and longer to consume less CPU, with the max granularity being |
| 334 | * MAX_BACKOFF_MS. |
| 335 | * |
| 336 | * @return true if all messages have been processed including delayed ones, false if timeout |
| 337 | * |
| 338 | * TODO: Move this method to com.android.testutils.HandlerUtils.kt. |
| 339 | */ |
| 340 | private fun Handler.waitForDelayedMessage(what: Int?, timeoutMs: Long) { |
| 341 | fun hasMatchingMessages() = |
| 342 | if (what == null) hasMessagesOrCallbacks() else hasMessages(what) |
| 343 | val expiry = System.currentTimeMillis() + timeoutMs |
| 344 | var delay = 5L |
| 345 | while (System.currentTimeMillis() < expiry && hasMatchingMessages()) { |
| 346 | // None of Handler, Looper, Message and MessageQueue expose any way to retrieve |
| 347 | // the time when the next (let alone the last) message will be processed, so |
| 348 | // short of examining the internals with reflection sleep() is the only solution. |
| 349 | Thread.sleep(delay) |
| 350 | delay = (delay * 2) |
| 351 | .coerceAtMost(expiry - System.currentTimeMillis()) |
| 352 | .coerceAtMost(MAX_BACKOFF_MS) |
| 353 | } |
| 354 | |
| 355 | val timeout = expiry - System.currentTimeMillis() |
| 356 | if (timeout <= 0) fail("Delayed message did not process yet after ${timeoutMs}ms") |
| 357 | waitForIdle(timeout) |
| 358 | } |
| 359 | |
| 360 | @Test |
| 361 | fun testNotificationWithUpstreamNetworkChanged() { |
| 362 | // Set test sub id. No notification. |
| 363 | notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) |
| 364 | verifyCancelAllTetheringActiveNotifications() |
| 365 | |
| 366 | // Wifi downstream. Show enable notification with test resource. |
| 367 | notificationUpdater.onDownstreamChanged(WIFI_MASK) |
| 368 | verifyOnlyTetheringActiveNotification( |
| 369 | ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) |
| 370 | |
| 371 | // There is no upstream. Show no upstream notification. |
| 372 | notificationUpdater.onUpstreamNetworkChanged(null) |
| 373 | notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) |
| 374 | verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE, |
| 375 | TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) |
| 376 | reset(notificationManager) |
| 377 | |
| 378 | // Same upstream network changed. Nothing happened. |
| 379 | notificationUpdater.onUpstreamNetworkChanged(null) |
| 380 | verifyZeroInteractions(notificationManager) |
| 381 | |
| 382 | // Upstream come back. Clear no upstream notification. |
| 383 | notificationUpdater.onUpstreamNetworkChanged(Network(1000)) |
| 384 | verifyNotificationCancelled(NO_UPSTREAM_NOTIFICATION_ID) |
| 385 | reset(notificationManager) |
| 386 | |
| 387 | // No upstream again. Show no upstream notification. |
| 388 | notificationUpdater.onUpstreamNetworkChanged(null) |
| 389 | notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) |
| 390 | verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE, |
| 391 | TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) |
| 392 | reset(notificationManager) |
| 393 | |
| 394 | // No downstream. No notification. |
| 395 | notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) |
| 396 | verifyCancelAllTetheringActiveNotifications() |
| 397 | |
| 398 | // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to 0 and have wifi downstream |
| 399 | // again. Show enable notification only. |
| 400 | doReturn(-1).`when`(testResources) |
| 401 | .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) |
| 402 | notificationUpdater.onDownstreamChanged(WIFI_MASK) |
| 403 | notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L) |
| 404 | verifyOnlyTetheringActiveNotification( |
| 405 | ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE) |
| 406 | } |
| 407 | |
| 408 | @Test |
| 409 | fun testGetResourcesForSubId() { |
| 410 | doReturn(telephonyManager).`when`(telephonyManager).createForSubscriptionId(anyInt()) |
| 411 | doReturn(1234).`when`(telephonyManager).getSimCarrierId() |
| 412 | doReturn("000000").`when`(telephonyManager).getSimOperator() |
| 413 | |
| 414 | val subId = -2 // Use invalid subId to avoid getting resource from cache or real subId. |
| 415 | val config = context.resources.configuration |
| 416 | var res = notificationUpdater.getResourcesForSubId(context, subId) |
| 417 | assertEquals(config.mcc, res.configuration.mcc) |
| 418 | assertEquals(config.mnc, res.configuration.mnc) |
| 419 | |
| 420 | doReturn(1839).`when`(telephonyManager).getSimCarrierId() |
| 421 | res = notificationUpdater.getResourcesForSubId(context, subId) |
| 422 | assertEquals(config.mcc, res.configuration.mcc) |
| 423 | assertEquals(config.mnc, res.configuration.mnc) |
| 424 | |
| 425 | doReturn("20404").`when`(telephonyManager).getSimOperator() |
| 426 | res = notificationUpdater.getResourcesForSubId(context, subId) |
| 427 | assertEquals(311, res.configuration.mcc) |
| 428 | assertEquals(480, res.configuration.mnc) |
Paul Hu | f950dc9 | 2020-03-25 12:39:13 +0000 | [diff] [blame] | 429 | } |
markchien | 503be61 | 2020-04-12 21:41:29 +0800 | [diff] [blame] | 430 | } |