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 package com.android.server.notification; 17 18 import static android.Manifest.permission.RECEIVE_SENSITIVE_NOTIFICATIONS; 19 import static android.content.pm.PackageManager.MATCH_ANY_USER; 20 import static android.permission.PermissionManager.PERMISSION_GRANTED; 21 import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS; 22 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; 23 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; 24 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; 25 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT; 26 27 import static com.android.server.notification.NotificationManagerService.NotificationListeners.TAG_REQUESTED_LISTENERS; 28 29 import static com.google.common.truth.Truth.assertThat; 30 31 import static junit.framework.Assert.assertFalse; 32 import static junit.framework.Assert.assertTrue; 33 34 import static org.junit.Assert.fail; 35 import static org.mockito.ArgumentMatchers.any; 36 import static org.mockito.ArgumentMatchers.anyInt; 37 import static org.mockito.ArgumentMatchers.anyString; 38 import static org.mockito.ArgumentMatchers.eq; 39 import static org.mockito.ArgumentMatchers.intThat; 40 import static org.mockito.ArgumentMatchers.nullable; 41 import static org.mockito.Mockito.atLeast; 42 import static org.mockito.Mockito.doAnswer; 43 import static org.mockito.Mockito.doNothing; 44 import static org.mockito.Mockito.doReturn; 45 import static org.mockito.Mockito.mock; 46 import static org.mockito.Mockito.never; 47 import static org.mockito.Mockito.reset; 48 import static org.mockito.Mockito.spy; 49 import static org.mockito.Mockito.times; 50 import static org.mockito.Mockito.verify; 51 import static org.mockito.Mockito.when; 52 53 import android.annotation.SuppressLint; 54 import android.app.INotificationManager; 55 import android.app.Notification; 56 import android.app.NotificationChannel; 57 import android.app.NotificationChannelGroup; 58 import android.app.NotificationManager; 59 import android.companion.AssociationInfo; 60 import android.companion.ICompanionDeviceManager; 61 import android.content.ComponentName; 62 import android.content.pm.IPackageManager; 63 import android.content.pm.PackageManager; 64 import android.content.pm.ServiceInfo; 65 import android.content.pm.VersionedPackage; 66 import android.content.res.Resources; 67 import android.os.Bundle; 68 import android.os.Parcel; 69 import android.os.RemoteException; 70 import android.os.UserHandle; 71 import android.platform.test.flag.junit.SetFlagsRule; 72 import android.service.notification.INotificationListener; 73 import android.service.notification.IStatusBarNotificationHolder; 74 import android.service.notification.NotificationListenerFilter; 75 import android.service.notification.NotificationListenerService; 76 import android.service.notification.NotificationRankingUpdate; 77 import android.service.notification.NotificationStats; 78 import android.service.notification.StatusBarNotification; 79 import android.testing.TestableContext; 80 import android.util.ArraySet; 81 import android.util.Pair; 82 import android.util.Xml; 83 84 import com.android.modules.utils.TypedXmlPullParser; 85 import com.android.modules.utils.TypedXmlSerializer; 86 import com.android.server.UiServiceTestCase; 87 import com.android.server.pm.pkg.PackageStateInternal; 88 89 import com.google.common.collect.ImmutableList; 90 91 import org.junit.Before; 92 import org.junit.Rule; 93 import org.junit.Test; 94 import org.mockito.ArgumentCaptor; 95 import org.mockito.ArgumentMatcher; 96 import org.mockito.Mock; 97 import org.mockito.MockitoAnnotations; 98 import org.mockito.internal.util.reflection.FieldSetter; 99 100 import java.io.BufferedInputStream; 101 import java.io.BufferedOutputStream; 102 import java.io.ByteArrayInputStream; 103 import java.io.ByteArrayOutputStream; 104 import java.util.ArrayList; 105 import java.util.Arrays; 106 import java.util.List; 107 import java.util.concurrent.CountDownLatch; 108 109 @SuppressLint("GuardedBy") 110 public class NotificationListenersTest extends UiServiceTestCase { 111 112 @Rule 113 public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 114 115 @Mock 116 private PackageManager mPm; 117 @Mock 118 private IPackageManager miPm; 119 @Mock 120 private Resources mResources; 121 122 // mNm is going to be a spy, so it must use doReturn.when, not when.thenReturn, as 123 // when.thenReturn will result in the real method being called 124 NotificationManagerService mNm; 125 @Mock 126 private INotificationManager mINm; 127 private TestableContext mContext = spy(getContext()); 128 129 NotificationManagerService.NotificationListeners mListeners; 130 131 private int mUid1 = 98989; 132 private ComponentName mCn1 = new ComponentName("pkg", "pkg.cmp"); 133 private ComponentName mCn2 = new ComponentName("pkg2", "pkg2.cmp2"); 134 private ComponentName mUninstalledComponent = new ComponentName("pkg3", 135 "pkg3.NotificationListenerService"); 136 137 @Before setUp()138 public void setUp() throws Exception { 139 mNm = spy(new NotificationManagerService(mContext)); 140 MockitoAnnotations.initMocks(this); 141 getContext().setMockPackageManager(mPm); 142 doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); 143 144 doReturn(true).when(mNm).isInteractionVisibleToListener(any(), anyInt()); 145 146 mListeners = spy(mNm.new NotificationListeners( 147 mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm)); 148 when(mNm.getBinderService()).thenReturn(mINm); 149 mNm.mPackageManager = mock(IPackageManager.class); 150 PackageStateInternal psi = mock(PackageStateInternal.class); 151 mNm.mPackageManagerInternal = mPmi; 152 when(psi.getAppId()).thenReturn(mUid1); 153 when(mNm.mPackageManagerInternal.getPackageStateInternal(any())).thenReturn(psi); 154 mNm.mCompanionManager = mock(ICompanionDeviceManager.class); 155 when(mNm.mCompanionManager.getAllAssociationsForUser(anyInt())) 156 .thenReturn(new ArrayList<>()); 157 mNm.mHandler = mock(NotificationManagerService.WorkerHandler.class); 158 mNm.mAssistants = mock(NotificationManagerService.NotificationAssistants.class); 159 FieldSetter.setField(mNm, 160 NotificationManagerService.class.getDeclaredField("mListeners"), 161 mListeners); 162 doReturn(android.service.notification.NotificationListenerService.TRIM_FULL) 163 .when(mListeners).getOnNotificationPostedTrim(any()); 164 } 165 166 @Test testReadExtraTag()167 public void testReadExtraTag() throws Exception { 168 String xml = "<" + TAG_REQUESTED_LISTENERS + ">" 169 + "<listener component=\"" + mCn1.flattenToString() + "\" user=\"0\">" 170 + "<allowed types=\"7\" />" 171 + "</listener>" 172 + "<listener component=\"" + mCn2.flattenToString() + "\" user=\"10\">" 173 + "<allowed types=\"4\" />" 174 + "<disallowed pkg=\"pkg1\" uid=\"243\"/>" 175 + "</listener>" 176 + "</" + TAG_REQUESTED_LISTENERS + ">"; 177 178 TypedXmlPullParser parser = Xml.newFastPullParser(); 179 parser.setInput(new BufferedInputStream( 180 new ByteArrayInputStream(xml.getBytes())), null); 181 parser.nextTag(); 182 mListeners.readExtraTag(TAG_REQUESTED_LISTENERS, parser); 183 184 validateListenersFromXml(); 185 } 186 187 @Test loadDefaultsFromConfig_forHeadlessSystemUser_loadUninstalled()188 public void loadDefaultsFromConfig_forHeadlessSystemUser_loadUninstalled() throws Exception { 189 // setup with headless system user mode 190 mListeners = spy(mNm.new NotificationListeners( 191 mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm, 192 /* isHeadlessSystemUserMode= */ true)); 193 mockDefaultListenerConfigForUninstalledComponent(mUninstalledComponent); 194 195 mListeners.loadDefaultsFromConfig(); 196 197 assertThat(mListeners.getDefaultComponents()).contains(mUninstalledComponent); 198 } 199 200 @Test loadDefaultsFromConfig_forNonHeadlessSystemUser_ignoreUninstalled()201 public void loadDefaultsFromConfig_forNonHeadlessSystemUser_ignoreUninstalled() 202 throws Exception { 203 // setup without headless system user mode 204 mListeners = spy(mNm.new NotificationListeners( 205 mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm, 206 /* isHeadlessSystemUserMode= */ false)); 207 mockDefaultListenerConfigForUninstalledComponent(mUninstalledComponent); 208 209 mListeners.loadDefaultsFromConfig(); 210 211 assertThat(mListeners.getDefaultComponents()).doesNotContain(mUninstalledComponent); 212 } 213 mockDefaultListenerConfigForUninstalledComponent(ComponentName componentName)214 private void mockDefaultListenerConfigForUninstalledComponent(ComponentName componentName) { 215 ArraySet<ComponentName> components = new ArraySet<>(Arrays.asList(componentName)); 216 when(mResources 217 .getString( 218 com.android.internal.R.string.config_defaultListenerAccessPackages)) 219 .thenReturn(componentName.getPackageName()); 220 when(mContext.getResources()).thenReturn(mResources); 221 doReturn(components).when(mListeners).queryPackageForServices( 222 eq(componentName.getPackageName()), 223 intThat(hasIntBitFlag(MATCH_ANY_USER)), 224 anyInt()); 225 } 226 hasIntBitFlag(int flag)227 public static ArgumentMatcher<Integer> hasIntBitFlag(int flag) { 228 return arg -> arg != null && ((arg & flag) == flag); 229 } 230 231 @Test testWriteExtraTag()232 public void testWriteExtraTag() throws Exception { 233 NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>()); 234 VersionedPackage a1 = new VersionedPackage("pkg1", 243); 235 NotificationListenerFilter nlf2 = 236 new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[]{a1})); 237 mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf); 238 mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2); 239 240 TypedXmlSerializer serializer = Xml.newFastSerializer(); 241 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 242 serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); 243 serializer.startDocument(null, true); 244 mListeners.writeExtraXmlTags(serializer); 245 serializer.endDocument(); 246 serializer.flush(); 247 248 TypedXmlPullParser parser = Xml.newFastPullParser(); 249 parser.setInput(new BufferedInputStream( 250 new ByteArrayInputStream(baos.toByteArray())), null); 251 parser.nextTag(); 252 mListeners.readExtraTag("req_listeners", parser); 253 254 validateListenersFromXml(); 255 } 256 validateListenersFromXml()257 private void validateListenersFromXml() { 258 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0)).getTypes()) 259 .isEqualTo(7); 260 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0)) 261 .getDisallowedPackages()) 262 .isEmpty(); 263 264 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes()) 265 .isEqualTo(4); 266 VersionedPackage a1 = new VersionedPackage("pkg1", 243); 267 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)) 268 .getDisallowedPackages()) 269 .contains(a1); 270 } 271 272 @Test testOnUserRemoved()273 public void testOnUserRemoved() { 274 NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>()); 275 VersionedPackage a1 = new VersionedPackage("pkg1", 243); 276 NotificationListenerFilter nlf2 = 277 new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1})); 278 mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf); 279 mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2); 280 281 mListeners.onUserRemoved(0); 282 283 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))).isNull(); 284 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes()) 285 .isEqualTo(4); 286 } 287 288 @Test testEnsureFilters_newServiceNoMetadata()289 public void testEnsureFilters_newServiceNoMetadata() { 290 ServiceInfo si = new ServiceInfo(); 291 si.packageName = "new2"; 292 si.name = "comp2"; 293 294 mListeners.ensureFilters(si, 0); 295 296 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))).isNull(); 297 } 298 299 @Test testEnsureFilters_preExisting()300 public void testEnsureFilters_preExisting() { 301 // one exists already, say from xml 302 VersionedPackage a1 = new VersionedPackage("pkg1", 243); 303 NotificationListenerFilter nlf = 304 new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1})); 305 mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf); 306 ServiceInfo siOld = new ServiceInfo(); 307 siOld.packageName = mCn2.getPackageName(); 308 siOld.name = mCn2.getClassName(); 309 310 mListeners.ensureFilters(siOld, 0); 311 312 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))).isEqualTo(nlf); 313 } 314 315 @Test testEnsureFilters_newServiceWithMetadata()316 public void testEnsureFilters_newServiceWithMetadata() { 317 ServiceInfo si = new ServiceInfo(); 318 si.packageName = "new"; 319 si.name = "comp"; 320 si.metaData = new Bundle(); 321 si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1|2"); 322 323 mListeners.ensureFilters(si, 0); 324 325 assertThat(mListeners.getNotificationListenerFilter( 326 Pair.create(si.getComponentName(), 0)).getTypes()) 327 .isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING); 328 } 329 330 @Test testEnsureFilters_newServiceWithMetadata_namesNotNumbers()331 public void testEnsureFilters_newServiceWithMetadata_namesNotNumbers() { 332 ServiceInfo si = new ServiceInfo(); 333 si.packageName = "new"; 334 si.name = "comp"; 335 si.metaData = new Bundle(); 336 si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, 337 "conversations|ALERTING"); 338 339 mListeners.ensureFilters(si, 0); 340 341 assertThat(mListeners.getNotificationListenerFilter( 342 Pair.create(si.getComponentName(), 0)).getTypes()) 343 .isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING); 344 } 345 346 @Test testEnsureFilters_newServiceWithMetadata_onlyOneListed()347 public void testEnsureFilters_newServiceWithMetadata_onlyOneListed() { 348 ServiceInfo si = new ServiceInfo(); 349 si.packageName = "new"; 350 si.name = "comp"; 351 si.metaData = new Bundle(); 352 si.metaData.putInt(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, 2); 353 354 mListeners.ensureFilters(si, 0); 355 356 assertThat(mListeners.getNotificationListenerFilter( 357 Pair.create(si.getComponentName(), 0)).getTypes()) 358 .isEqualTo(FLAG_FILTER_TYPE_ALERTING); 359 } 360 361 @Test testEnsureFilters_newServiceWithMetadata_disabledTypes()362 public void testEnsureFilters_newServiceWithMetadata_disabledTypes() { 363 ServiceInfo si = new ServiceInfo(); 364 si.packageName = "new"; 365 si.name = "comp"; 366 si.metaData = new Bundle(); 367 si.metaData.putString(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, "1|2"); 368 369 mListeners.ensureFilters(si, 0); 370 371 assertThat(mListeners.getNotificationListenerFilter( 372 Pair.create(si.getComponentName(), 0)).getTypes()) 373 .isEqualTo(FLAG_FILTER_TYPE_SILENT | FLAG_FILTER_TYPE_ONGOING); 374 } 375 376 @Test testEnsureFilters_newServiceWithMetadata_disabledTypes_mixedText()377 public void testEnsureFilters_newServiceWithMetadata_disabledTypes_mixedText() { 378 ServiceInfo si = new ServiceInfo(); 379 si.packageName = "new"; 380 si.name = "comp"; 381 si.metaData = new Bundle(); 382 si.metaData.putString(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, 383 "1|alerting"); 384 385 mListeners.ensureFilters(si, 0); 386 387 assertThat(mListeners.getNotificationListenerFilter( 388 Pair.create(si.getComponentName(), 0)).getTypes()) 389 .isEqualTo(FLAG_FILTER_TYPE_SILENT | FLAG_FILTER_TYPE_ONGOING); 390 } 391 392 @Test testEnsureFilters_newServiceWithMetadata_metaDataDisagrees()393 public void testEnsureFilters_newServiceWithMetadata_metaDataDisagrees() { 394 ServiceInfo si = new ServiceInfo(); 395 si.packageName = "new"; 396 si.name = "comp"; 397 si.metaData = new Bundle(); 398 si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1|2"); 399 si.metaData.putInt(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, 1); 400 401 mListeners.ensureFilters(si, 0); 402 403 assertThat(mListeners.getNotificationListenerFilter( 404 Pair.create(si.getComponentName(), 0)).getTypes()) 405 .isEqualTo(FLAG_FILTER_TYPE_ALERTING); 406 } 407 408 @Test testEnsureFilters_newServiceWithEmptyMetadata()409 public void testEnsureFilters_newServiceWithEmptyMetadata() { 410 ServiceInfo si = new ServiceInfo(); 411 si.packageName = "new"; 412 si.name = "comp"; 413 si.metaData = new Bundle(); 414 si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, ""); 415 416 mListeners.ensureFilters(si, 0); 417 418 assertThat(mListeners.getNotificationListenerFilter( 419 Pair.create(si.getComponentName(), 0)).getTypes()) 420 .isEqualTo(0); 421 } 422 423 @Test testOnPackageChanged()424 public void testOnPackageChanged() { 425 NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>()); 426 VersionedPackage a1 = new VersionedPackage("pkg1", 243); 427 NotificationListenerFilter nlf2 = 428 new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1})); 429 mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf); 430 mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2); 431 432 String[] pkgs = new String[] {mCn1.getPackageName()}; 433 int[] uids = new int[] {1}; 434 mListeners.onPackagesChanged(false, pkgs, uids); 435 436 // not removing; no change 437 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0)).getTypes()) 438 .isEqualTo(7); 439 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes()) 440 .isEqualTo(4); 441 } 442 443 @Test testOnPackageChanged_removing()444 public void testOnPackageChanged_removing() { 445 NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>()); 446 VersionedPackage a1 = new VersionedPackage("pkg1", 243); 447 NotificationListenerFilter nlf2 = 448 new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1})); 449 mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf); 450 mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2); 451 452 String[] pkgs = new String[] {mCn1.getPackageName()}; 453 int[] uids = new int[] {1}; 454 mListeners.onPackagesChanged(true, pkgs, uids); 455 456 // only mCn1 removed 457 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))).isNull(); 458 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)).getTypes()) 459 .isEqualTo(4); 460 } 461 462 @Test testOnPackageChanged_removingPackage_removeFromDisallowed()463 public void testOnPackageChanged_removingPackage_removeFromDisallowed() { 464 NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>()); 465 VersionedPackage a1 = new VersionedPackage("pkg1", 243); 466 NotificationListenerFilter nlf2 = 467 new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1})); 468 mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf); 469 mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2); 470 471 String[] pkgs = new String[] {"pkg1"}; 472 int[] uids = new int[] {243}; 473 mListeners.onPackagesChanged(true, pkgs, uids); 474 475 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0)) 476 .getDisallowedPackages()).isEmpty(); 477 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)) 478 .getDisallowedPackages()).isEmpty(); 479 } 480 481 @Test testOnPackageChanged_notRemovingPackage_staysInDisallowed()482 public void testOnPackageChanged_notRemovingPackage_staysInDisallowed() { 483 NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>()); 484 VersionedPackage a1 = new VersionedPackage("pkg1", 243); 485 NotificationListenerFilter nlf2 = 486 new NotificationListenerFilter(4, new ArraySet<>(new VersionedPackage[] {a1})); 487 mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf); 488 mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2); 489 490 String[] pkgs = new String[] {"pkg1"}; 491 int[] uids = new int[] {243}; 492 mListeners.onPackagesChanged(false, pkgs, uids); 493 494 assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)) 495 .getDisallowedPackages()).contains(a1); 496 } 497 498 @Test testHasAllowedListener()499 public void testHasAllowedListener() { 500 final int uid1 = 1, uid2 = 2; 501 // enable mCn1 but not mCn2 for uid1 502 mListeners.addApprovedList(mCn1.flattenToString(), uid1, true); 503 504 // verify that: 505 // the package for mCn1 has an allowed listener for uid1 and not uid2 506 assertTrue(mListeners.hasAllowedListener(mCn1.getPackageName(), uid1)); 507 assertFalse(mListeners.hasAllowedListener(mCn1.getPackageName(), uid2)); 508 509 // and that mCn2 has no allowed listeners for either user id 510 assertFalse(mListeners.hasAllowedListener(mCn2.getPackageName(), uid1)); 511 assertFalse(mListeners.hasAllowedListener(mCn2.getPackageName(), uid2)); 512 } 513 514 @Test testBroadcastUsers()515 public void testBroadcastUsers() { 516 int userId = 0; 517 mListeners.setPackageOrComponentEnabled(mCn1.flattenToString(), userId, true, false, true); 518 519 verify(mContext).sendBroadcastAsUser( 520 any(), eq(UserHandle.of(userId)), nullable(String.class)); 521 } 522 523 @Test testNotifyPostedLockedInLockdownMode()524 public void testNotifyPostedLockedInLockdownMode() { 525 NotificationRecord r0 = mock(NotificationRecord.class); 526 NotificationRecord old0 = mock(NotificationRecord.class); 527 UserHandle uh0 = mock(UserHandle.class); 528 529 NotificationRecord r1 = mock(NotificationRecord.class); 530 NotificationRecord old1 = mock(NotificationRecord.class); 531 UserHandle uh1 = mock(UserHandle.class); 532 533 // Neither user0 and user1 is in the lockdown mode 534 when(r0.getUser()).thenReturn(uh0); 535 when(uh0.getIdentifier()).thenReturn(0); 536 doReturn(false).when(mNm).isInLockDownMode(0); 537 538 when(r1.getUser()).thenReturn(uh1); 539 when(uh1.getIdentifier()).thenReturn(1); 540 doReturn(false).when(mNm).isInLockDownMode(1); 541 542 mListeners.notifyPostedLocked(r0, old0, true); 543 mListeners.notifyPostedLocked(r0, old0, false); 544 verify(r0, atLeast(2)).getSbn(); 545 546 mListeners.notifyPostedLocked(r1, old1, true); 547 mListeners.notifyPostedLocked(r1, old1, false); 548 verify(r1, atLeast(2)).getSbn(); 549 550 // Reset 551 reset(r0); 552 reset(old0); 553 reset(r1); 554 reset(old1); 555 556 // Only user 0 is in the lockdown mode 557 when(r0.getUser()).thenReturn(uh0); 558 when(uh0.getIdentifier()).thenReturn(0); 559 when(mNm.isInLockDownMode(0)).thenReturn(true); 560 561 when(r1.getUser()).thenReturn(uh1); 562 when(uh1.getIdentifier()).thenReturn(1); 563 when(mNm.isInLockDownMode(1)).thenReturn(false); 564 565 mListeners.notifyPostedLocked(r0, old0, true); 566 mListeners.notifyPostedLocked(r0, old0, false); 567 verify(r0, never()).getSbn(); 568 569 mListeners.notifyPostedLocked(r1, old1, true); 570 mListeners.notifyPostedLocked(r1, old1, false); 571 verify(r1, atLeast(2)).getSbn(); 572 } 573 574 @Test testNotifyRemovedLockedInLockdownMode()575 public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException { 576 NotificationRecord r0 = mock(NotificationRecord.class); 577 NotificationStats rs0 = mock(NotificationStats.class); 578 UserHandle uh0 = mock(UserHandle.class); 579 580 NotificationRecord r1 = mock(NotificationRecord.class); 581 NotificationStats rs1 = mock(NotificationStats.class); 582 UserHandle uh1 = mock(UserHandle.class); 583 584 StatusBarNotification sbn = mock(StatusBarNotification.class); 585 FieldSetter.setField(mNm, 586 NotificationManagerService.class.getDeclaredField("mHandler"), 587 mock(NotificationManagerService.WorkerHandler.class)); 588 589 // Neither user0 and user1 is in the lockdown mode 590 when(r0.getUser()).thenReturn(uh0); 591 when(uh0.getIdentifier()).thenReturn(0); 592 doReturn(false).when(mNm).isInLockDownMode(0); 593 when(r0.getSbn()).thenReturn(sbn); 594 595 when(r1.getUser()).thenReturn(uh1); 596 when(uh1.getIdentifier()).thenReturn(1); 597 doReturn(false).when(mNm).isInLockDownMode(1); 598 when(r1.getSbn()).thenReturn(sbn); 599 600 mListeners.notifyRemovedLocked(r0, 0, rs0); 601 mListeners.notifyRemovedLocked(r0, 0, rs0); 602 verify(r0, atLeast(2)).getSbn(); 603 604 mListeners.notifyRemovedLocked(r1, 0, rs1); 605 mListeners.notifyRemovedLocked(r1, 0, rs1); 606 verify(r1, atLeast(2)).getSbn(); 607 608 // Reset 609 reset(r0); 610 reset(rs0); 611 reset(r1); 612 reset(rs1); 613 614 // Only user 0 is in the lockdown mode 615 when(r0.getUser()).thenReturn(uh0); 616 when(uh0.getIdentifier()).thenReturn(0); 617 when(mNm.isInLockDownMode(0)).thenReturn(true); 618 when(r0.getSbn()).thenReturn(sbn); 619 620 when(r1.getUser()).thenReturn(uh1); 621 when(uh1.getIdentifier()).thenReturn(1); 622 when(mNm.isInLockDownMode(1)).thenReturn(false); 623 when(r1.getSbn()).thenReturn(sbn); 624 625 mListeners.notifyRemovedLocked(r0, 0, rs0); 626 mListeners.notifyRemovedLocked(r0, 0, rs0); 627 verify(r0, never()).getSbn(); 628 629 mListeners.notifyRemovedLocked(r1, 0, rs1); 630 mListeners.notifyRemovedLocked(r1, 0, rs1); 631 verify(r1, atLeast(2)).getSbn(); 632 } 633 634 @Test testImplicitGrant()635 public void testImplicitGrant() { 636 String pkg = "pkg"; 637 int uid = 9; 638 NotificationChannel channel = new NotificationChannel("id", "name", 639 NotificationManager.IMPORTANCE_HIGH); 640 Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) 641 .setContentTitle("foo") 642 .setSmallIcon(android.R.drawable.sym_def_app_icon) 643 .setTimeoutAfter(1); 644 645 StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0, 646 nb.build(), UserHandle.getUserHandleForUid(uid), null, 0); 647 NotificationRecord r = new NotificationRecord(mContext, sbn, channel); 648 649 ManagedServices.ManagedServiceInfo info = mListeners.new ManagedServiceInfo( 650 null, new ComponentName("a", "a"), sbn.getUserId(), false, null, 33, 33); 651 List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(info); 652 when(mListeners.getServices()).thenReturn(services); 653 654 doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any()); 655 doReturn(mock(NotificationRankingUpdate.class)).when(mNm).makeRankingUpdateLocked(info); 656 doReturn(false).when(mNm).isInLockDownMode(anyInt()); 657 doNothing().when(mNm).updateUriPermissions(any(), any(), any(), anyInt()); 658 659 mListeners.notifyPostedLocked(r, null); 660 661 verify(mPmi).grantImplicitAccess(sbn.getUserId(), null, UserHandle.getAppId(33), 662 sbn.getUid(), false, false); 663 } 664 665 @Test testUpdateGroup_notifyTwoListeners()666 public void testUpdateGroup_notifyTwoListeners() throws Exception { 667 final NotificationChannelGroup updated = new NotificationChannelGroup("id", "name"); 668 updated.setChannels(ImmutableList.of( 669 new NotificationChannel("a", "a", 1), new NotificationChannel("b", "b", 2))); 670 updated.setBlocked(true); 671 672 ManagedServices.ManagedServiceInfo i1 = getParcelingListener(updated); 673 ManagedServices.ManagedServiceInfo i2= getParcelingListener(updated); 674 when(mListeners.getServices()).thenReturn(ImmutableList.of(i1, i2)); 675 NotificationChannelGroup existing = new NotificationChannelGroup("id", "name"); 676 677 mListeners.notifyNotificationChannelGroupChanged("pkg", UserHandle.of(0), updated, 0); 678 Thread.sleep(500); 679 680 verify(((INotificationListener) i1.getService()), times(1)) 681 .onNotificationChannelGroupModification(anyString(), any(), any(), anyInt()); 682 } 683 684 @Test testNotificationListenerFilter_threadSafety()685 public void testNotificationListenerFilter_threadSafety() throws Exception { 686 testThreadSafety(() -> { 687 mListeners.setNotificationListenerFilter( 688 new Pair<>(new ComponentName("pkg1", "cls1"), 0), 689 new NotificationListenerFilter()); 690 mListeners.setNotificationListenerFilter( 691 new Pair<>(new ComponentName("pkg2", "cls2"), 10), 692 new NotificationListenerFilter()); 693 mListeners.setNotificationListenerFilter( 694 new Pair<>(new ComponentName("pkg3", "cls3"), 11), 695 new NotificationListenerFilter()); 696 697 mListeners.onUserRemoved(10); 698 mListeners.onPackagesChanged(true, new String[]{"pkg1", "pkg2"}, new int[]{0, 0}); 699 }, 20, 50); 700 } 701 702 @Test testListenerTrusted_withPermission()703 public void testListenerTrusted_withPermission() throws RemoteException { 704 mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); 705 when(mNm.mPackageManager.checkUidPermission(RECEIVE_SENSITIVE_NOTIFICATIONS, mUid1)) 706 .thenReturn(PERMISSION_GRANTED); 707 ManagedServices.ManagedServiceInfo info = getMockServiceInfo(); 708 mListeners.onServiceAdded(info); 709 assertTrue(mListeners.isUidTrusted(mUid1)); 710 } 711 712 @Test testListenerTrusted_withSystemSignature()713 public void testListenerTrusted_withSystemSignature() { 714 mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); 715 when(mNm.mPackageManagerInternal.isPlatformSigned(mCn1.getPackageName())).thenReturn(true); 716 ManagedServices.ManagedServiceInfo info = getMockServiceInfo(); 717 mListeners.onServiceAdded(info); 718 assertTrue(mListeners.isUidTrusted(mUid1)); 719 } 720 721 @Test testListenerTrusted_withCdmAssociation()722 public void testListenerTrusted_withCdmAssociation() throws Exception { 723 mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); 724 mNm.mCompanionManager = mock(ICompanionDeviceManager.class); 725 AssociationInfo assocInfo = mock(AssociationInfo.class); 726 when(assocInfo.isRevoked()).thenReturn(false); 727 when(assocInfo.getPackageName()).thenReturn(mCn1.getPackageName()); 728 when(assocInfo.getUserId()).thenReturn(UserHandle.getUserId(mUid1)); 729 ArrayList<AssociationInfo> infos = new ArrayList<>(); 730 infos.add(assocInfo); 731 when(mNm.mCompanionManager.getAllAssociationsForUser(anyInt())).thenReturn(infos); 732 ManagedServices.ManagedServiceInfo info = getMockServiceInfo(); 733 mListeners.onServiceAdded(info); 734 assertTrue(mListeners.isUidTrusted(mUid1)); 735 } 736 737 @Test testListenerTrusted_ifFlagDisabled()738 public void testListenerTrusted_ifFlagDisabled() { 739 mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); 740 ManagedServices.ManagedServiceInfo info = getMockServiceInfo(); 741 mListeners.onServiceAdded(info); 742 assertTrue(mListeners.isUidTrusted(mUid1)); 743 } 744 745 @Test testRedaction_whenPosted()746 public void testRedaction_whenPosted() { 747 mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); 748 ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>(); 749 infos.add(getMockServiceInfo()); 750 doReturn(infos).when(mListeners).getServices(); 751 doReturn(mock(StatusBarNotification.class)) 752 .when(mListeners).redactStatusBarNotification(any()); 753 doReturn(false).when(mNm).isInLockDownMode(anyInt()); 754 doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any()); 755 NotificationRecord r = mock(NotificationRecord.class); 756 when(r.getUser()).thenReturn(UserHandle.of(0)); 757 StatusBarNotification sbn = getSbn(0); 758 NotificationRecord old = mock(NotificationRecord.class); 759 when(old.getUser()).thenReturn(UserHandle.of(0)); 760 StatusBarNotification oldSbn = getSbn(1); 761 when(r.getSbn()).thenReturn(sbn); 762 when(r.hasSensitiveContent()).thenReturn(true); 763 when(old.getSbn()).thenReturn(oldSbn); 764 when(old.hasSensitiveContent()).thenReturn(true); 765 766 mListeners.notifyPostedLocked(r, old); 767 verify(mListeners, atLeast(1)).redactStatusBarNotification(eq(sbn)); 768 verify(mListeners, never()).redactStatusBarNotification(eq(oldSbn)); 769 } 770 771 @Test testRedaction_whenPosted_oldRemoved()772 public void testRedaction_whenPosted_oldRemoved() { 773 mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); 774 ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>(); 775 infos.add(getMockServiceInfo()); 776 doReturn(infos).when(mListeners).getServices(); 777 doReturn(mock(StatusBarNotification.class)) 778 .when(mListeners).redactStatusBarNotification(any()); 779 doReturn(false).when(mNm).isInLockDownMode(anyInt()); 780 doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any()); 781 NotificationRecord r = mock(NotificationRecord.class); 782 when(r.getUser()).thenReturn(UserHandle.of(0)); 783 StatusBarNotification sbn = getSbn(0); 784 NotificationRecord old = mock(NotificationRecord.class); 785 when(old.getUser()).thenReturn(UserHandle.of(0)); 786 StatusBarNotification oldSbn = getSbn(1); 787 when(r.getSbn()).thenReturn(sbn); 788 when(r.hasSensitiveContent()).thenReturn(true); 789 when(old.getSbn()).thenReturn(oldSbn); 790 when(old.hasSensitiveContent()).thenReturn(true); 791 792 doReturn(true).when(mNm).isVisibleToListener(eq(oldSbn), anyInt(), any()); 793 doReturn(false).when(mNm).isVisibleToListener(eq(sbn), anyInt(), any()); 794 mListeners.notifyPostedLocked(r, old); 795 // When the old sbn is removed, the old should be redacted 796 verify(mListeners, atLeast(1)).redactStatusBarNotification(eq(oldSbn)); 797 } 798 799 @Test testRedaction_whenRemoved()800 public void testRedaction_whenRemoved() { 801 mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); 802 doReturn(mock(StatusBarNotification.class)) 803 .when(mListeners).redactStatusBarNotification(any()); 804 ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>(); 805 infos.add(getMockServiceInfo()); 806 doReturn(infos).when(mListeners).getServices(); 807 doReturn(false).when(mNm).isInLockDownMode(anyInt()); 808 doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any()); 809 NotificationRecord r = mock(NotificationRecord.class); 810 when(r.getUser()).thenReturn(UserHandle.of(0)); 811 StatusBarNotification sbn = getSbn(0); 812 when(r.getSbn()).thenReturn(sbn); 813 when(r.hasSensitiveContent()).thenReturn(true); 814 mNm.mAssistants = mock(NotificationManagerService.NotificationAssistants.class); 815 816 mListeners.notifyRemovedLocked(r, 0, mock(NotificationStats.class)); 817 verify(mListeners, atLeast(1)).redactStatusBarNotification(any()); 818 } 819 820 @Test testRedaction_noneIfFlagDisabled()821 public void testRedaction_noneIfFlagDisabled() { 822 mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); 823 ArrayList<ManagedServices.ManagedServiceInfo> infos = new ArrayList<>(); 824 infos.add(getMockServiceInfo()); 825 doReturn(infos).when(mListeners).getServices(); 826 doReturn(false).when(mNm).isInLockDownMode(anyInt()); 827 doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any()); 828 NotificationRecord r = mock(NotificationRecord.class); 829 when(r.getUser()).thenReturn(UserHandle.of(0)); 830 StatusBarNotification sbn = getSbn(0); 831 when(r.getSbn()).thenReturn(sbn); 832 when(r.hasSensitiveContent()).thenReturn(true); 833 mListeners.notifyRemovedLocked(r, 0, mock(NotificationStats.class)); 834 verify(mListeners, never()).redactStatusBarNotification(eq(sbn)); 835 } 836 837 @Test testListenerPost_UpdateLifetimeExtended()838 public void testListenerPost_UpdateLifetimeExtended() throws Exception { 839 mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR); 840 841 // Create original notification, with FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY. 842 String pkg = "pkg"; 843 int uid = 9; 844 UserHandle userHandle = UserHandle.getUserHandleForUid(uid); 845 NotificationChannel channel = new NotificationChannel("id", "name", 846 NotificationManager.IMPORTANCE_HIGH); 847 Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) 848 .setContentTitle("foo") 849 .setSmallIcon(android.R.drawable.sym_def_app_icon) 850 .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, true); 851 StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0, 852 nb.build(), userHandle, null, 0); 853 NotificationRecord old = new NotificationRecord(mContext, sbn, channel); 854 855 // Creates updated notification (without FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) 856 Notification.Builder nb2 = new Notification.Builder(mContext, channel.getId()) 857 .setContentTitle("new title") 858 .setSmallIcon(android.R.drawable.sym_def_app_icon) 859 .setFlag(Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY, false); 860 StatusBarNotification sbn2 = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0, 861 nb2.build(), userHandle, null, 0); 862 NotificationRecord toPost = new NotificationRecord(mContext, sbn2, channel); 863 864 // Create system ui-like service. 865 ManagedServices.ManagedServiceInfo info = mListeners.new ManagedServiceInfo( 866 null, new ComponentName("a", "a"), sbn2.getUserId(), false, null, 33, 33); 867 info.isSystemUi = true; 868 INotificationListener l1 = mock(INotificationListener.class); 869 info.service = l1; 870 List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(info); 871 when(mListeners.getServices()).thenReturn(services); 872 873 FieldSetter.setField(mNm, 874 NotificationManagerService.class.getDeclaredField("mHandler"), 875 mock(NotificationManagerService.WorkerHandler.class)); 876 doReturn(true).when(mNm).isVisibleToListener(any(), anyInt(), any()); 877 doReturn(mock(NotificationRankingUpdate.class)).when(mNm).makeRankingUpdateLocked(info); 878 doReturn(false).when(mNm).isInLockDownMode(anyInt()); 879 doNothing().when(mNm).updateUriPermissions(any(), any(), any(), anyInt()); 880 doReturn(sbn2).when(mListeners).redactStatusBarNotification(sbn2); 881 doReturn(sbn2).when(mListeners).redactStatusBarNotification(any()); 882 883 // The notification change is posted to the service listener. 884 mListeners.notifyPostedLocked(toPost, old); 885 886 // Verify that the post occcurs with the updated notification value. 887 ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); 888 verify(mNm.mHandler, times(1)).post(runnableCaptor.capture()); 889 runnableCaptor.getValue().run(); 890 ArgumentCaptor<IStatusBarNotificationHolder> sbnCaptor = 891 ArgumentCaptor.forClass(IStatusBarNotificationHolder.class); 892 verify(l1, times(1)).onNotificationPosted(sbnCaptor.capture(), any()); 893 StatusBarNotification sbnResult = sbnCaptor.getValue().get(); 894 assertThat(sbnResult.getNotification() 895 .extras.getCharSequence(Notification.EXTRA_TITLE).toString()) 896 .isEqualTo("new title"); 897 } 898 899 /** 900 * Helper method to test the thread safety of some operations. 901 * 902 * <p>Runs the supplied {@code operationToTest}, {@code nRunsPerThread} times, 903 * concurrently using {@code nThreads} threads, and waits for all of them to finish. 904 */ testThreadSafety(Runnable operationToTest, int nThreads, int nRunsPerThread)905 private static void testThreadSafety(Runnable operationToTest, int nThreads, 906 int nRunsPerThread) throws InterruptedException { 907 final CountDownLatch startLatch = new CountDownLatch(1); 908 final CountDownLatch doneLatch = new CountDownLatch(nThreads); 909 910 for (int i = 0; i < nThreads; i++) { 911 Runnable threadRunnable = () -> { 912 try { 913 startLatch.await(); 914 for (int j = 0; j < nRunsPerThread; j++) { 915 operationToTest.run(); 916 } 917 } catch (InterruptedException e) { 918 e.printStackTrace(); 919 } finally { 920 doneLatch.countDown(); 921 } 922 }; 923 new Thread(threadRunnable, "Test Thread #" + i).start(); 924 } 925 926 // Ready set go 927 startLatch.countDown(); 928 929 // Wait for all test threads to be done. 930 doneLatch.await(); 931 } 932 getParcelingListener( final NotificationChannelGroup toParcel)933 private ManagedServices.ManagedServiceInfo getParcelingListener( 934 final NotificationChannelGroup toParcel) 935 throws RemoteException { 936 ManagedServices.ManagedServiceInfo i1 = getMockServiceInfo(); 937 INotificationListener l1 = (INotificationListener) i1.getService(); 938 doAnswer(invocationOnMock -> { 939 try { 940 toParcel.writeToParcel(Parcel.obtain(), 0); 941 } catch (Exception e) { 942 fail("Failed to parcel group to listener"); 943 return e; 944 945 } 946 return null; 947 }).when(l1).onNotificationChannelGroupModification(anyString(), any(), any(), anyInt()); 948 return i1; 949 } 950 getMockServiceInfo()951 private ManagedServices.ManagedServiceInfo getMockServiceInfo() { 952 ManagedServices.ManagedServiceInfo i1 = mock(ManagedServices.ManagedServiceInfo.class); 953 when(i1.isSystem()).thenReturn(true); 954 INotificationListener l1 = mock(INotificationListener.class); 955 when(i1.enabledAndUserMatches(anyInt())).thenReturn(true); 956 when(i1.getService()).thenReturn(l1); 957 i1.service = l1; 958 i1.uid = mUid1; 959 i1.component = mCn1; 960 return i1; 961 } 962 getSbn(int id)963 private StatusBarNotification getSbn(int id) { 964 return new StatusBarNotification("pkg1", "pkg1", id, "", mUid1, 0, 965 mock(Notification.class), UserHandle.of(0), "", 0); 966 967 } 968 } 969