1 /* 2 * Copyright (C) 2022 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 17 package com.android.carrierdefaultapp; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 import static org.mockito.Mockito.any; 25 import static org.mockito.Mockito.anyInt; 26 import static org.mockito.Mockito.anyString; 27 import static org.mockito.Mockito.clearInvocations; 28 import static org.mockito.Mockito.doCallRealMethod; 29 import static org.mockito.Mockito.doReturn; 30 import static org.mockito.Mockito.eq; 31 import static org.mockito.Mockito.never; 32 import static org.mockito.Mockito.spy; 33 import static org.mockito.Mockito.verify; 34 35 import android.annotation.NonNull; 36 import android.app.Notification; 37 import android.app.NotificationManager; 38 import android.app.PendingIntent; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.pm.ApplicationInfo; 42 import android.content.pm.PackageManager; 43 import android.content.res.Configuration; 44 import android.content.res.Resources; 45 import android.os.UserHandle; 46 import android.telephony.SubscriptionManager; 47 import android.telephony.TelephonyManager; 48 49 import androidx.test.runner.AndroidJUnit4; 50 51 import com.android.phone.slice.SlicePurchaseController; 52 53 import org.junit.Before; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.mockito.ArgumentCaptor; 57 import org.mockito.Mock; 58 import org.mockito.MockitoAnnotations; 59 60 import java.net.URL; 61 import java.util.Locale; 62 63 @RunWith(AndroidJUnit4.class) 64 public class SlicePurchaseBroadcastReceiverTest { 65 private static final int PHONE_ID = 0; 66 private static final String CARRIER = "Some Carrier"; 67 private static final String EXTRA = "EXTRA"; 68 69 @Mock Intent mIntent; 70 @Mock Intent mDataIntent; 71 @Mock PendingIntent mPendingIntent; 72 @Mock PendingIntent mCanceledIntent; 73 @Mock PendingIntent mContentIntent1; 74 @Mock PendingIntent mContentIntent2; 75 @Mock PendingIntent mNotificationShownIntent; 76 @Mock PendingIntent mNotificationsDisabledIntent; 77 @Mock Context mContext; 78 @Mock Resources mResources; 79 @Mock Configuration mConfiguration; 80 @Mock NotificationManager mNotificationManager; 81 @Mock ApplicationInfo mApplicationInfo; 82 @Mock PackageManager mPackageManager; 83 84 private SlicePurchaseBroadcastReceiver mSlicePurchaseBroadcastReceiver; 85 private Resources mSpiedResources; 86 87 @Before setUp()88 public void setUp() throws Exception { 89 MockitoAnnotations.initMocks(this); 90 mSpiedResources = spy(Resources.getSystem()); 91 92 doReturn("").when(mResources).getString(anyInt()); 93 doReturn(mNotificationManager).when(mContext) 94 .getSystemService(eq(NotificationManager.class)); 95 doReturn(true).when(mNotificationManager).areNotificationsEnabled(); 96 doReturn(mApplicationInfo).when(mContext).getApplicationInfo(); 97 doReturn(mPackageManager).when(mContext).getPackageManager(); 98 doReturn(mSpiedResources).when(mContext).getResources(); 99 100 mSlicePurchaseBroadcastReceiver = spy(new SlicePurchaseBroadcastReceiver()); 101 } 102 103 @Test testSendSlicePurchaseAppResponse()104 public void testSendSlicePurchaseAppResponse() throws Exception { 105 SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(mIntent, EXTRA); 106 verify(mPendingIntent, never()).send(); 107 108 doReturn(mPendingIntent).when(mIntent).getParcelableExtra( 109 eq(EXTRA), eq(PendingIntent.class)); 110 SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(mIntent, EXTRA); 111 verify(mPendingIntent).send(); 112 } 113 114 @Test testSendSlicePurchaseAppResponseWithData()115 public void testSendSlicePurchaseAppResponseWithData() throws Exception { 116 SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData( 117 mContext, mIntent, EXTRA, mDataIntent); 118 verify(mPendingIntent, never()).send(eq(mContext), eq(0), any(Intent.class)); 119 120 doReturn(mPendingIntent).when(mIntent).getParcelableExtra( 121 eq(EXTRA), eq(PendingIntent.class)); 122 SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData( 123 mContext, mIntent, EXTRA, mDataIntent); 124 ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); 125 verify(mPendingIntent).send(eq(mContext), eq(0), captor.capture()); 126 assertEquals(mDataIntent, captor.getValue()); 127 } 128 129 @Test testIsIntentValid()130 public void testIsIntentValid() { 131 assertFalse(SlicePurchaseBroadcastReceiver.isIntentValid(mIntent)); 132 133 // set up intent 134 doReturn(PHONE_ID).when(mIntent).getIntExtra( 135 eq(SlicePurchaseController.EXTRA_PHONE_ID), anyInt()); 136 doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mIntent).getIntExtra( 137 eq(SlicePurchaseController.EXTRA_SUB_ID), anyInt()); 138 doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra( 139 eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt()); 140 doReturn(SlicePurchaseController.SLICE_PURCHASE_TEST_FILE).when(mIntent).getStringExtra( 141 eq(SlicePurchaseController.EXTRA_PURCHASE_URL)); 142 doReturn(CARRIER).when(mIntent).getStringExtra(eq(SlicePurchaseController.EXTRA_CARRIER)); 143 assertFalse(SlicePurchaseBroadcastReceiver.isIntentValid(mIntent)); 144 145 // set up pending intent 146 doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mPendingIntent).getCreatorPackage(); 147 doReturn(true).when(mPendingIntent).isBroadcast(); 148 doReturn(mPendingIntent).when(mIntent).getParcelableExtra( 149 anyString(), eq(PendingIntent.class)); 150 assertTrue(SlicePurchaseBroadcastReceiver.isIntentValid(mIntent)); 151 } 152 153 @Test testGetPurchaseUrl()154 public void testGetPurchaseUrl() { 155 String[] invalidUrls = new String[] { 156 null, 157 "", 158 "www.google.com", 159 "htt://www.google.com", 160 "http//www.google.com", 161 "http:/www.google.com", 162 "file:///android_asset/", 163 "file:///android_asset/slice_store_test.html" 164 }; 165 166 // test invalid URLs 167 for (String url : invalidUrls) { 168 URL purchaseUrl = SlicePurchaseBroadcastReceiver.getPurchaseUrl(url, null, false); 169 assertNull(purchaseUrl); 170 } 171 172 // test asset URL 173 assertEquals(SlicePurchaseController.SLICE_PURCHASE_TEST_FILE, 174 SlicePurchaseBroadcastReceiver.getPurchaseUrl( 175 SlicePurchaseController.SLICE_PURCHASE_TEST_FILE, null, false).toString()); 176 177 // test normal URL 178 String validUrl = "http://www.google.com"; 179 assertEquals(validUrl, 180 SlicePurchaseBroadcastReceiver.getPurchaseUrl(validUrl, null, false).toString()); 181 182 // test normal URL with user data but no append 183 String userData = "encryptedUserData=data"; 184 assertEquals(validUrl, 185 SlicePurchaseBroadcastReceiver.getPurchaseUrl(validUrl, userData, false) 186 .toString()); 187 188 // test normal URL with user data and append 189 assertEquals(validUrl + "?" + userData, 190 SlicePurchaseBroadcastReceiver.getPurchaseUrl(validUrl, userData, true).toString()); 191 192 // test normal URL without user data and append 193 assertEquals(validUrl, 194 SlicePurchaseBroadcastReceiver.getPurchaseUrl(validUrl, null, true).toString()); 195 } 196 197 @Test testDisplayPerformanceBoostNotification()198 public void testDisplayPerformanceBoostNotification() throws Exception { 199 displayPerformanceBoostNotification(); 200 201 // verify performance boost notification was shown 202 ArgumentCaptor<Notification> captor = ArgumentCaptor.forClass(Notification.class); 203 verify(mNotificationManager).notifyAsUser( 204 eq(SlicePurchaseBroadcastReceiver.PERFORMANCE_BOOST_NOTIFICATION_TAG), 205 eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), 206 captor.capture(), 207 eq(UserHandle.ALL)); 208 209 // verify notification fields 210 Notification notification = captor.getValue(); 211 assertEquals(mContentIntent1, notification.contentIntent); 212 assertEquals(mPendingIntent, notification.deleteIntent); 213 assertEquals(2, notification.actions.length); 214 assertEquals(mCanceledIntent, notification.actions[0].actionIntent); 215 assertEquals(mContentIntent2, notification.actions[1].actionIntent); 216 217 // verify SlicePurchaseController was notified 218 verify(mNotificationShownIntent).send(); 219 } 220 displayPerformanceBoostNotification()221 private void displayPerformanceBoostNotification() { 222 // set up pending intents 223 doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mPendingIntent).getCreatorPackage(); 224 doReturn(true).when(mPendingIntent).isBroadcast(); 225 doReturn(mPendingIntent).when(mIntent).getParcelableExtra( 226 anyString(), eq(PendingIntent.class)); 227 createValidPendingIntent(mNotificationShownIntent, 228 SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN); 229 createValidPendingIntent(mNotificationsDisabledIntent, 230 SlicePurchaseController.EXTRA_INTENT_NOTIFICATIONS_DISABLED); 231 232 // spy notification intents to prevent PendingIntent issues 233 doReturn(mContentIntent1).when(mSlicePurchaseBroadcastReceiver).createContentIntent( 234 eq(mContext), eq(mIntent), eq(1)); 235 doReturn(mContentIntent2).when(mSlicePurchaseBroadcastReceiver).createContentIntent( 236 eq(mContext), eq(mIntent), eq(2)); 237 doReturn(mCanceledIntent).when(mSlicePurchaseBroadcastReceiver).createCanceledIntent( 238 eq(mContext), eq(mIntent)); 239 240 // spy resources to prevent resource not found issues 241 doReturn(mResources).when(mSlicePurchaseBroadcastReceiver).getResources(eq(mContext)); 242 243 // send ACTION_START_SLICE_PURCHASE_APP 244 doReturn(SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP).when(mIntent).getAction(); 245 doReturn(PHONE_ID).when(mIntent).getIntExtra( 246 eq(SlicePurchaseController.EXTRA_PHONE_ID), anyInt()); 247 doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mIntent).getIntExtra( 248 eq(SlicePurchaseController.EXTRA_SUB_ID), anyInt()); 249 doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra( 250 eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt()); 251 doReturn(SlicePurchaseController.SLICE_PURCHASE_TEST_FILE).when(mIntent).getStringExtra( 252 eq(SlicePurchaseController.EXTRA_PURCHASE_URL)); 253 doReturn(CARRIER).when(mIntent).getStringExtra(eq(SlicePurchaseController.EXTRA_CARRIER)); 254 mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent); 255 } 256 createValidPendingIntent(@onNull PendingIntent intent, @NonNull String extra)257 private void createValidPendingIntent(@NonNull PendingIntent intent, @NonNull String extra) { 258 doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(intent).getCreatorPackage(); 259 doReturn(true).when(intent).isBroadcast(); 260 doReturn(intent).when(mIntent).getParcelableExtra(eq(extra), eq(PendingIntent.class)); 261 } 262 263 @Test testNotificationCanceled()264 public void testNotificationCanceled() { 265 // send ACTION_NOTIFICATION_CANCELED 266 doReturn("com.android.phone.slice.action.NOTIFICATION_CANCELED").when(mIntent).getAction(); 267 doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra( 268 eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt()); 269 mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent); 270 271 // verify notification was canceled 272 verify(mNotificationManager).cancelAsUser( 273 eq(SlicePurchaseBroadcastReceiver.PERFORMANCE_BOOST_NOTIFICATION_TAG), 274 eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), 275 eq(UserHandle.ALL)); 276 } 277 278 @Test testNotificationTimeout()279 public void testNotificationTimeout() throws Exception { 280 displayPerformanceBoostNotification(); 281 282 // send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT 283 doReturn(SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT).when(mIntent) 284 .getAction(); 285 doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra( 286 eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt()); 287 mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent); 288 289 // verify notification was canceled 290 verify(mNotificationManager).cancelAsUser( 291 eq(SlicePurchaseBroadcastReceiver.PERFORMANCE_BOOST_NOTIFICATION_TAG), 292 eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), 293 eq(UserHandle.ALL)); 294 } 295 296 @Test testLocaleChanged()297 public void testLocaleChanged() throws Exception { 298 // get previous locale 299 doReturn(mConfiguration).when(mSpiedResources).getConfiguration(); 300 Locale before = getLocale(); 301 302 // display notification 303 displayPerformanceBoostNotification(); 304 clearInvocations(mNotificationManager); 305 clearInvocations(mNotificationShownIntent); 306 307 // change current locale from previous value 308 Locale newLocale = Locale.forLanguageTag("en-US"); 309 if (before.equals(newLocale)) { 310 newLocale = Locale.forLanguageTag("ko-KR"); 311 } 312 doReturn(newLocale).when(mSlicePurchaseBroadcastReceiver).getCurrentLocale(); 313 314 // send ACTION_LOCALE_CHANGED 315 doReturn(Intent.ACTION_LOCALE_CHANGED).when(mIntent).getAction(); 316 mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent); 317 318 // verify notification was updated and SlicePurchaseController was not notified 319 verify(mNotificationManager).cancelAsUser( 320 eq(SlicePurchaseBroadcastReceiver.PERFORMANCE_BOOST_NOTIFICATION_TAG), 321 eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), 322 eq(UserHandle.ALL)); 323 verify(mNotificationManager).notifyAsUser( 324 eq(SlicePurchaseBroadcastReceiver.PERFORMANCE_BOOST_NOTIFICATION_TAG), 325 eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), 326 any(Notification.class), 327 eq(UserHandle.ALL)); 328 verify(mNotificationShownIntent, never()).send(); 329 330 // verify locale was changed successfully 331 doCallRealMethod().when(mSlicePurchaseBroadcastReceiver).getResources(eq(mContext)); 332 assertEquals(newLocale, getLocale()); 333 } 334 getLocale()335 private Locale getLocale() { 336 try { 337 mSlicePurchaseBroadcastReceiver.getResources(mContext); 338 fail("getLocale should not have completed successfully."); 339 } catch (NullPointerException expected) { } 340 ArgumentCaptor<Locale> captor = ArgumentCaptor.forClass(Locale.class); 341 verify(mConfiguration).setLocale(captor.capture()); 342 clearInvocations(mConfiguration); 343 return captor.getValue(); 344 } 345 346 @Test testNotificationsDisabled()347 public void testNotificationsDisabled() throws Exception { 348 doReturn(false).when(mNotificationManager).areNotificationsEnabled(); 349 350 displayPerformanceBoostNotification(); 351 352 // verify notification was not shown 353 verify(mNotificationManager, never()).notifyAsUser( 354 eq(SlicePurchaseBroadcastReceiver.PERFORMANCE_BOOST_NOTIFICATION_TAG), 355 eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY), 356 any(), 357 eq(UserHandle.ALL)); 358 verify(mNotificationShownIntent, never()).send(); 359 360 // verify SlicePurchaseController was notified that notifications are disabled 361 verify(mNotificationsDisabledIntent).send(); 362 } 363 } 364