1 /* 2 * Copyright (C) 2021 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.server.statusbar; 18 19 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; 20 import static android.app.ActivityManager.PROCESS_STATE_TOP; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertNotEquals; 24 import static org.junit.Assert.assertThrows; 25 import static org.junit.Assert.fail; 26 import static org.mockito.ArgumentMatchers.anyBoolean; 27 import static org.mockito.ArgumentMatchers.nullable; 28 import static org.mockito.Mockito.any; 29 import static org.mockito.Mockito.anyInt; 30 import static org.mockito.Mockito.anyLong; 31 import static org.mockito.Mockito.anyString; 32 import static org.mockito.Mockito.argThat; 33 import static org.mockito.Mockito.eq; 34 import static org.mockito.Mockito.never; 35 import static org.mockito.Mockito.times; 36 import static org.mockito.Mockito.verify; 37 import static org.mockito.Mockito.when; 38 39 import android.Manifest; 40 import android.app.ActivityManagerInternal; 41 import android.app.StatusBarManager; 42 import android.compat.testing.PlatformCompatChangeRule; 43 import android.content.ComponentName; 44 import android.content.Intent; 45 import android.content.om.IOverlayManager; 46 import android.content.pm.ApplicationInfo; 47 import android.content.pm.PackageInfo; 48 import android.content.pm.PackageManager; 49 import android.content.pm.PackageManagerInternal; 50 import android.content.pm.ResolveInfo; 51 import android.content.pm.ServiceInfo; 52 import android.graphics.drawable.Icon; 53 import android.hardware.display.DisplayManager; 54 import android.os.Binder; 55 import android.os.Looper; 56 import android.os.RemoteException; 57 import android.os.UserHandle; 58 import android.service.quicksettings.TileService; 59 import android.testing.TestableContext; 60 61 import androidx.test.InstrumentationRegistry; 62 63 import com.android.internal.statusbar.IAddTileResultCallback; 64 import com.android.internal.statusbar.IStatusBar; 65 import com.android.server.LocalServices; 66 import com.android.server.policy.GlobalActionsProvider; 67 import com.android.server.wm.ActivityTaskManagerInternal; 68 69 import libcore.junit.util.compat.CoreCompatChangeRule; 70 71 import org.junit.Before; 72 import org.junit.BeforeClass; 73 import org.junit.Rule; 74 import org.junit.Test; 75 import org.junit.rules.TestRule; 76 import org.junit.runner.RunWith; 77 import org.junit.runners.JUnit4; 78 import org.mockito.ArgumentCaptor; 79 import org.mockito.ArgumentMatcher; 80 import org.mockito.Captor; 81 import org.mockito.Mock; 82 import org.mockito.MockitoAnnotations; 83 import org.mockito.stubbing.Answer; 84 85 @RunWith(JUnit4.class) 86 public class StatusBarManagerServiceTest { 87 88 private static final String TEST_PACKAGE = "test_pkg"; 89 private static final String TEST_SERVICE = "test_svc"; 90 private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PACKAGE, 91 TEST_SERVICE); 92 private static final CharSequence APP_NAME = "AppName"; 93 private static final CharSequence TILE_LABEL = "Tile label"; 94 95 @Rule 96 public final TestableContext mContext = 97 new NoBroadcastContextWrapper(InstrumentationRegistry.getContext()); 98 99 @Rule 100 public TestRule mCompatChangeRule = new PlatformCompatChangeRule(); 101 102 @Mock 103 private ActivityTaskManagerInternal mActivityTaskManagerInternal; 104 @Mock 105 private PackageManagerInternal mPackageManagerInternal; 106 @Mock 107 private ActivityManagerInternal mActivityManagerInternal; 108 @Mock 109 private ApplicationInfo mApplicationInfo; 110 @Mock 111 private IStatusBar.Stub mMockStatusBar; 112 @Mock 113 private IOverlayManager mOverlayManager; 114 @Mock 115 private PackageManager mPackageManager; 116 @Captor 117 private ArgumentCaptor<IAddTileResultCallback> mAddTileResultCallbackCaptor; 118 119 private Icon mIcon; 120 private StatusBarManagerService mStatusBarManagerService; 121 122 @BeforeClass oneTimeInitialization()123 public static void oneTimeInitialization() { 124 if (Looper.myLooper() == null) { 125 Looper.prepare(); 126 } 127 } 128 129 @Before setUp()130 public void setUp() { 131 MockitoAnnotations.initMocks(this); 132 LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); 133 LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal); 134 LocalServices.removeServiceForTest(ActivityManagerInternal.class); 135 LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInternal); 136 LocalServices.removeServiceForTest(PackageManagerInternal.class); 137 LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); 138 139 when(mMockStatusBar.asBinder()).thenReturn(mMockStatusBar); 140 when(mApplicationInfo.loadLabel(any())).thenReturn(APP_NAME); 141 mockHandleIncomingUser(); 142 143 mStatusBarManagerService = new StatusBarManagerService(mContext); 144 LocalServices.removeServiceForTest(StatusBarManagerInternal.class); 145 LocalServices.removeServiceForTest(GlobalActionsProvider.class); 146 147 mContext.getSystemService(DisplayManager.class).unregisterDisplayListener( 148 mStatusBarManagerService); 149 150 mStatusBarManagerService.registerStatusBar(mMockStatusBar); 151 mStatusBarManagerService.registerOverlayManager(mOverlayManager); 152 153 mIcon = Icon.createWithResource(mContext, android.R.drawable.btn_plus); 154 } 155 156 @Test 157 @CoreCompatChangeRule.EnableCompatChanges( 158 {StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE}) testRequestActive_changeEnabled_OKCall()159 public void testRequestActive_changeEnabled_OKCall() throws RemoteException { 160 int user = 0; 161 mockEverything(user); 162 mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, user); 163 164 verify(mMockStatusBar).requestTileServiceListeningState(TEST_COMPONENT); 165 } 166 167 @Test 168 @CoreCompatChangeRule.EnableCompatChanges( 169 {StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE}) testRequestActive_changeEnabled_differentPackage_fail()170 public void testRequestActive_changeEnabled_differentPackage_fail() throws RemoteException { 171 when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0L, mContext.getUserId())) 172 .thenReturn(Binder.getCallingUid() + 1); 173 try { 174 mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, 0); 175 fail("Should cause security exception"); 176 } catch (SecurityException e) { } 177 verify(mMockStatusBar, never()).requestTileServiceListeningState(TEST_COMPONENT); 178 } 179 180 @Test 181 @CoreCompatChangeRule.EnableCompatChanges( 182 {StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE}) testRequestActive_changeEnabled_notCurrentUser_fail()183 public void testRequestActive_changeEnabled_notCurrentUser_fail() throws RemoteException { 184 mockUidCheck(); 185 int user = 0; 186 mockCurrentUserCheck(user); 187 try { 188 mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, user + 1); 189 fail("Should cause illegal argument exception"); 190 } catch (IllegalArgumentException e) { } 191 192 // Do not call into SystemUI 193 verify(mMockStatusBar, never()).requestTileServiceListeningState(TEST_COMPONENT); 194 } 195 196 @Test 197 @CoreCompatChangeRule.DisableCompatChanges( 198 {StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE}) testRequestActive_changeDisabled_pass()199 public void testRequestActive_changeDisabled_pass() throws RemoteException { 200 int user = 0; 201 mockEverything(user); 202 mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, user); 203 204 verify(mMockStatusBar).requestTileServiceListeningState(TEST_COMPONENT); 205 } 206 207 @Test 208 @CoreCompatChangeRule.DisableCompatChanges( 209 {StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE}) testRequestActive_changeDisabled_differentPackage_pass()210 public void testRequestActive_changeDisabled_differentPackage_pass() throws RemoteException { 211 when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0L, mContext.getUserId())) 212 .thenReturn(Binder.getCallingUid() + 1); 213 mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, 0); 214 215 verify(mMockStatusBar).requestTileServiceListeningState(TEST_COMPONENT); 216 } 217 218 @Test 219 @CoreCompatChangeRule.DisableCompatChanges( 220 {StatusBarManagerService.REQUEST_LISTENING_MUST_MATCH_PACKAGE}) testRequestActive_changeDisabled_notCurrentUser_pass()221 public void testRequestActive_changeDisabled_notCurrentUser_pass() throws RemoteException { 222 mockUidCheck(); 223 int user = 0; 224 mockCurrentUserCheck(user); 225 mStatusBarManagerService.requestTileServiceListeningState(TEST_COMPONENT, user + 1); 226 227 verify(mMockStatusBar).requestTileServiceListeningState(TEST_COMPONENT); 228 } 229 230 @Test testHandleIncomingUserCalled()231 public void testHandleIncomingUserCalled() { 232 int fakeUser = 17; 233 try { 234 mStatusBarManagerService.requestAddTile( 235 TEST_COMPONENT, 236 TILE_LABEL, 237 mIcon, 238 fakeUser, 239 new Callback() 240 ); 241 fail("Should have SecurityException from uid check"); 242 } catch (SecurityException e) { 243 verify(mActivityManagerInternal).handleIncomingUser( 244 eq(Binder.getCallingPid()), 245 eq(Binder.getCallingUid()), 246 eq(fakeUser), 247 eq(false), 248 eq(ActivityManagerInternal.ALLOW_NON_FULL), 249 anyString(), 250 eq(TEST_PACKAGE) 251 ); 252 } 253 } 254 255 @Test testCheckUid_pass()256 public void testCheckUid_pass() { 257 when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0, mContext.getUserId())) 258 .thenReturn(Binder.getCallingUid()); 259 try { 260 mStatusBarManagerService.requestAddTile( 261 TEST_COMPONENT, 262 TILE_LABEL, 263 mIcon, 264 mContext.getUserId(), 265 new Callback() 266 ); 267 } catch (SecurityException e) { 268 fail("No SecurityException should be thrown"); 269 } 270 } 271 272 @Test testCheckUid_pass_differentUser()273 public void testCheckUid_pass_differentUser() { 274 int otherUserUid = UserHandle.getUid(17, UserHandle.getAppId(Binder.getCallingUid())); 275 when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0, mContext.getUserId())) 276 .thenReturn(otherUserUid); 277 try { 278 mStatusBarManagerService.requestAddTile( 279 TEST_COMPONENT, 280 TILE_LABEL, 281 mIcon, 282 mContext.getUserId(), 283 new Callback() 284 ); 285 } catch (SecurityException e) { 286 fail("No SecurityException should be thrown"); 287 } 288 } 289 290 @Test testCheckUid_fail()291 public void testCheckUid_fail() { 292 when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0, mContext.getUserId())) 293 .thenReturn(Binder.getCallingUid() + 1); 294 try { 295 mStatusBarManagerService.requestAddTile( 296 TEST_COMPONENT, 297 TILE_LABEL, 298 mIcon, 299 mContext.getUserId(), 300 new Callback() 301 ); 302 fail("Should throw SecurityException"); 303 } catch (SecurityException e) { 304 // pass 305 } 306 } 307 308 @Test testCurrentUser_fail()309 public void testCurrentUser_fail() { 310 mockUidCheck(); 311 int user = 0; 312 when(mActivityManagerInternal.getCurrentUserId()).thenReturn(user + 1); 313 314 Callback callback = new Callback(); 315 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 316 317 assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_NOT_CURRENT_USER, 318 callback.mUserResponse); 319 } 320 321 @Test testCurrentUser_pass()322 public void testCurrentUser_pass() { 323 mockUidCheck(); 324 int user = 0; 325 when(mActivityManagerInternal.getCurrentUserId()).thenReturn(user); 326 327 Callback callback = new Callback(); 328 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 329 330 assertNotEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_NOT_CURRENT_USER, 331 callback.mUserResponse); 332 } 333 334 @Test testValidComponent_fail_noComponentFound()335 public void testValidComponent_fail_noComponentFound() { 336 int user = 10; 337 mockUidCheck(); 338 mockCurrentUserCheck(user); 339 IntentMatcher im = new IntentMatcher( 340 new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT)); 341 when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L), 342 eq(user), anyInt())).thenReturn(null); 343 344 Callback callback = new Callback(); 345 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 346 347 assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT, callback.mUserResponse); 348 } 349 350 @Test testValidComponent_fail_notEnabled()351 public void testValidComponent_fail_notEnabled() { 352 int user = 10; 353 mockUidCheck(); 354 mockCurrentUserCheck(user); 355 356 ResolveInfo r = makeResolveInfo(); 357 r.serviceInfo.permission = Manifest.permission.BIND_QUICK_SETTINGS_TILE; 358 359 IntentMatcher im = new IntentMatcher( 360 new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT)); 361 when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L), 362 eq(user), anyInt())).thenReturn(r); 363 when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT, 364 Binder.getCallingUid(), user)).thenReturn( 365 PackageManager.COMPONENT_ENABLED_STATE_DISABLED); 366 367 Callback callback = new Callback(); 368 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 369 370 assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT, callback.mUserResponse); 371 } 372 373 @Test testValidComponent_fail_noPermission()374 public void testValidComponent_fail_noPermission() { 375 int user = 10; 376 mockUidCheck(); 377 mockCurrentUserCheck(user); 378 379 ResolveInfo r = makeResolveInfo(); 380 381 IntentMatcher im = new IntentMatcher( 382 new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT)); 383 when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L), 384 eq(user), anyInt())).thenReturn(r); 385 when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT, 386 Binder.getCallingUid(), user)).thenReturn( 387 PackageManager.COMPONENT_ENABLED_STATE_ENABLED); 388 389 Callback callback = new Callback(); 390 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 391 392 assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT, callback.mUserResponse); 393 } 394 395 @Test testValidComponent_fail_notExported()396 public void testValidComponent_fail_notExported() { 397 int user = 10; 398 mockUidCheck(); 399 mockCurrentUserCheck(user); 400 401 ResolveInfo r = makeResolveInfo(); 402 r.serviceInfo.permission = Manifest.permission.BIND_QUICK_SETTINGS_TILE; 403 r.serviceInfo.exported = false; 404 405 IntentMatcher im = new IntentMatcher( 406 new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT)); 407 when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L), 408 eq(user), anyInt())).thenReturn(r); 409 when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT, 410 Binder.getCallingUid(), user)).thenReturn( 411 PackageManager.COMPONENT_ENABLED_STATE_ENABLED); 412 413 Callback callback = new Callback(); 414 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 415 416 assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT, callback.mUserResponse); 417 } 418 419 @Test testValidComponent_pass()420 public void testValidComponent_pass() { 421 int user = 10; 422 mockUidCheck(); 423 mockCurrentUserCheck(user); 424 425 ResolveInfo r = makeResolveInfo(); 426 r.serviceInfo.permission = Manifest.permission.BIND_QUICK_SETTINGS_TILE; 427 r.serviceInfo.exported = true; 428 429 IntentMatcher im = new IntentMatcher( 430 new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT)); 431 when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L), 432 eq(user), anyInt())).thenReturn(r); 433 when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT, 434 Binder.getCallingUid(), user)).thenReturn( 435 PackageManager.COMPONENT_ENABLED_STATE_ENABLED); 436 437 Callback callback = new Callback(); 438 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 439 440 assertNotEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT, 441 callback.mUserResponse); 442 } 443 444 @Test testAppInForeground_fail()445 public void testAppInForeground_fail() { 446 int user = 10; 447 mockUidCheck(); 448 mockCurrentUserCheck(user); 449 mockComponentInfo(user); 450 451 when(mActivityManagerInternal.getUidProcessState(Binder.getCallingUid())).thenReturn( 452 PROCESS_STATE_FOREGROUND_SERVICE); 453 454 Callback callback = new Callback(); 455 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 456 457 assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_APP_NOT_IN_FOREGROUND, 458 callback.mUserResponse); 459 } 460 461 @Test testAppInForeground_pass()462 public void testAppInForeground_pass() { 463 int user = 10; 464 mockUidCheck(); 465 mockCurrentUserCheck(user); 466 mockComponentInfo(user); 467 468 when(mActivityManagerInternal.getUidProcessState(Binder.getCallingUid())).thenReturn( 469 PROCESS_STATE_TOP); 470 471 Callback callback = new Callback(); 472 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 473 474 assertNotEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_APP_NOT_IN_FOREGROUND, 475 callback.mUserResponse); 476 } 477 478 @Test testRequestToStatusBar()479 public void testRequestToStatusBar() throws RemoteException { 480 int user = 10; 481 mockEverything(user); 482 483 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, 484 new Callback()); 485 486 verify(mMockStatusBar).requestAddTile( 487 eq(TEST_COMPONENT), 488 eq(APP_NAME), 489 eq(TILE_LABEL), 490 eq(mIcon), 491 any() 492 ); 493 } 494 495 @Test testRequestInProgress_samePackage()496 public void testRequestInProgress_samePackage() throws RemoteException { 497 int user = 10; 498 mockEverything(user); 499 500 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, 501 new Callback()); 502 503 Callback callback = new Callback(); 504 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 505 506 assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS, 507 callback.mUserResponse); 508 } 509 510 @Test testRequestInProgress_differentPackage()511 public void testRequestInProgress_differentPackage() throws RemoteException { 512 int user = 10; 513 mockEverything(user); 514 ComponentName otherComponent = new ComponentName("a", "b"); 515 mockUidCheck(otherComponent.getPackageName()); 516 mockComponentInfo(user, otherComponent); 517 518 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, 519 new Callback()); 520 521 Callback callback = new Callback(); 522 mStatusBarManagerService.requestAddTile(otherComponent, TILE_LABEL, mIcon, user, callback); 523 524 assertNotEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS, 525 callback.mUserResponse); 526 } 527 528 @Test testResponseForwardedToCallback_tileAdded()529 public void testResponseForwardedToCallback_tileAdded() throws RemoteException { 530 int user = 10; 531 mockEverything(user); 532 533 Callback callback = new Callback(); 534 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 535 536 verify(mMockStatusBar).requestAddTile( 537 eq(TEST_COMPONENT), 538 eq(APP_NAME), 539 eq(TILE_LABEL), 540 eq(mIcon), 541 mAddTileResultCallbackCaptor.capture() 542 ); 543 544 mAddTileResultCallbackCaptor.getValue().onTileRequest( 545 StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED); 546 assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED, callback.mUserResponse); 547 } 548 549 @Test testResponseForwardedToCallback_tileNotAdded()550 public void testResponseForwardedToCallback_tileNotAdded() throws RemoteException { 551 int user = 10; 552 mockEverything(user); 553 554 Callback callback = new Callback(); 555 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 556 557 verify(mMockStatusBar).requestAddTile( 558 eq(TEST_COMPONENT), 559 eq(APP_NAME), 560 eq(TILE_LABEL), 561 eq(mIcon), 562 mAddTileResultCallbackCaptor.capture() 563 ); 564 565 mAddTileResultCallbackCaptor.getValue().onTileRequest( 566 StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED); 567 assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED, 568 callback.mUserResponse); 569 } 570 571 @Test testResponseForwardedToCallback_tileAlreadyAdded()572 public void testResponseForwardedToCallback_tileAlreadyAdded() throws RemoteException { 573 int user = 10; 574 mockEverything(user); 575 576 Callback callback = new Callback(); 577 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 578 579 verify(mMockStatusBar).requestAddTile( 580 eq(TEST_COMPONENT), 581 eq(APP_NAME), 582 eq(TILE_LABEL), 583 eq(mIcon), 584 mAddTileResultCallbackCaptor.capture() 585 ); 586 587 mAddTileResultCallbackCaptor.getValue().onTileRequest( 588 StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED); 589 assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED, 590 callback.mUserResponse); 591 } 592 593 @Test testResponseForwardedToCallback_dialogDismissed()594 public void testResponseForwardedToCallback_dialogDismissed() throws RemoteException { 595 int user = 10; 596 mockEverything(user); 597 598 Callback callback = new Callback(); 599 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 600 601 verify(mMockStatusBar).requestAddTile( 602 eq(TEST_COMPONENT), 603 eq(APP_NAME), 604 eq(TILE_LABEL), 605 eq(mIcon), 606 mAddTileResultCallbackCaptor.capture() 607 ); 608 609 mAddTileResultCallbackCaptor.getValue().onTileRequest( 610 StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED); 611 // This gets translated to TILE_NOT_ADDED 612 assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED, 613 callback.mUserResponse); 614 } 615 616 @Test testInstaDenialAfterManyDenials()617 public void testInstaDenialAfterManyDenials() throws RemoteException { 618 int user = 10; 619 mockEverything(user); 620 621 for (int i = 0; i < TileRequestTracker.MAX_NUM_DENIALS; i++) { 622 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, 623 new Callback()); 624 625 verify(mMockStatusBar, times(i + 1)).requestAddTile( 626 eq(TEST_COMPONENT), 627 eq(APP_NAME), 628 eq(TILE_LABEL), 629 eq(mIcon), 630 mAddTileResultCallbackCaptor.capture() 631 ); 632 mAddTileResultCallbackCaptor.getValue().onTileRequest( 633 StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED); 634 } 635 636 Callback callback = new Callback(); 637 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback); 638 639 // Only called MAX_NUM_DENIALS times 640 verify(mMockStatusBar, times(TileRequestTracker.MAX_NUM_DENIALS)).requestAddTile( 641 any(), 642 any(), 643 any(), 644 any(), 645 mAddTileResultCallbackCaptor.capture() 646 ); 647 assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED, 648 callback.mUserResponse); 649 } 650 651 @Test testDialogDismissalNotCountingAgainstDenials()652 public void testDialogDismissalNotCountingAgainstDenials() throws RemoteException { 653 int user = 10; 654 mockEverything(user); 655 656 for (int i = 0; i < TileRequestTracker.MAX_NUM_DENIALS * 2; i++) { 657 mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, 658 new Callback()); 659 660 verify(mMockStatusBar, times(i + 1)).requestAddTile( 661 eq(TEST_COMPONENT), 662 eq(APP_NAME), 663 eq(TILE_LABEL), 664 eq(mIcon), 665 mAddTileResultCallbackCaptor.capture() 666 ); 667 mAddTileResultCallbackCaptor.getValue().onTileRequest( 668 StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED); 669 } 670 } 671 672 @Test testSetNavBarMode_setsModeKids()673 public void testSetNavBarMode_setsModeKids() throws Exception { 674 mContext.setMockPackageManager(mPackageManager); 675 when(mPackageManager.getPackageInfo(anyString(), 676 any(PackageManager.PackageInfoFlags.class))).thenReturn(new PackageInfo()); 677 int navBarModeKids = StatusBarManager.NAV_BAR_MODE_KIDS; 678 679 mStatusBarManagerService.setNavBarMode(navBarModeKids); 680 681 assertEquals(navBarModeKids, mStatusBarManagerService.getNavBarMode()); 682 verify(mOverlayManager).setEnabledExclusiveInCategory(anyString(), anyInt()); 683 } 684 685 @Test testSetNavBarMode_setsModeNone()686 public void testSetNavBarMode_setsModeNone() throws RemoteException { 687 int navBarModeNone = StatusBarManager.NAV_BAR_MODE_DEFAULT; 688 689 mStatusBarManagerService.setNavBarMode(navBarModeNone); 690 691 assertEquals(navBarModeNone, mStatusBarManagerService.getNavBarMode()); 692 verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt()); 693 } 694 695 @Test testSetNavBarMode_invalidInputThrowsError()696 public void testSetNavBarMode_invalidInputThrowsError() throws RemoteException { 697 int navBarModeInvalid = -1; 698 699 assertThrows(IllegalArgumentException.class, 700 () -> mStatusBarManagerService.setNavBarMode(navBarModeInvalid)); 701 verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt()); 702 } 703 704 @Test testSetNavBarMode_noPackageDoesNotEnable()705 public void testSetNavBarMode_noPackageDoesNotEnable() throws Exception { 706 mContext.setMockPackageManager(mPackageManager); 707 when(mPackageManager.getPackageInfo(anyString(), 708 any(PackageManager.PackageInfoFlags.class))).thenReturn(null); 709 int navBarModeKids = StatusBarManager.NAV_BAR_MODE_KIDS; 710 711 mStatusBarManagerService.setNavBarMode(navBarModeKids); 712 713 assertEquals(navBarModeKids, mStatusBarManagerService.getNavBarMode()); 714 verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt()); 715 } 716 mockUidCheck()717 private void mockUidCheck() { 718 mockUidCheck(TEST_PACKAGE); 719 } 720 mockUidCheck(String packageName)721 private void mockUidCheck(String packageName) { 722 when(mPackageManagerInternal.getPackageUid(eq(packageName), anyLong(), anyInt())) 723 .thenReturn(Binder.getCallingUid()); 724 } 725 mockCurrentUserCheck(int user)726 private void mockCurrentUserCheck(int user) { 727 when(mActivityManagerInternal.getCurrentUserId()).thenReturn(user); 728 } 729 mockComponentInfo(int user)730 private void mockComponentInfo(int user) { 731 mockComponentInfo(user, TEST_COMPONENT); 732 } 733 makeResolveInfo()734 private ResolveInfo makeResolveInfo() { 735 ResolveInfo r = new ResolveInfo(); 736 r.serviceInfo = new ServiceInfo(); 737 r.serviceInfo.applicationInfo = mApplicationInfo; 738 return r; 739 } 740 mockComponentInfo(int user, ComponentName componentName)741 private void mockComponentInfo(int user, ComponentName componentName) { 742 ResolveInfo r = makeResolveInfo(); 743 r.serviceInfo.exported = true; 744 r.serviceInfo.permission = Manifest.permission.BIND_QUICK_SETTINGS_TILE; 745 746 IntentMatcher im = new IntentMatcher( 747 new Intent(TileService.ACTION_QS_TILE).setComponent(componentName)); 748 when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0L), 749 eq(user), anyInt())).thenReturn(r); 750 when(mPackageManagerInternal.getComponentEnabledSetting(componentName, 751 Binder.getCallingUid(), user)).thenReturn( 752 PackageManager.COMPONENT_ENABLED_STATE_ENABLED); 753 } 754 mockProcessState()755 private void mockProcessState() { 756 when(mActivityManagerInternal.getUidProcessState(Binder.getCallingUid())).thenReturn( 757 PROCESS_STATE_TOP); 758 } 759 mockHandleIncomingUser()760 private void mockHandleIncomingUser() { 761 when(mActivityManagerInternal.handleIncomingUser(anyInt(), anyInt(), anyInt(), anyBoolean(), 762 anyInt(), anyString(), anyString())).thenAnswer( 763 (Answer<Integer>) invocation -> { 764 return invocation.getArgument(2); // same user 765 } 766 ); 767 } 768 mockEverything(int user)769 private void mockEverything(int user) { 770 mockUidCheck(); 771 mockCurrentUserCheck(user); 772 mockComponentInfo(user); 773 mockProcessState(); 774 } 775 776 private static class Callback extends IAddTileResultCallback.Stub { 777 int mUserResponse = -1; 778 779 @Override onTileRequest(int userResponse)780 public void onTileRequest(int userResponse) throws RemoteException { 781 if (mUserResponse != -1) { 782 throw new IllegalStateException( 783 "Setting response to " + userResponse + " but it already has " 784 + mUserResponse); 785 } 786 mUserResponse = userResponse; 787 } 788 } 789 790 private static class IntentMatcher implements ArgumentMatcher<Intent> { 791 private final Intent mIntent; 792 IntentMatcher(Intent intent)793 IntentMatcher(Intent intent) { 794 mIntent = intent; 795 } 796 797 @Override matches(Intent argument)798 public boolean matches(Intent argument) { 799 return argument != null && argument.filterEquals(mIntent); 800 } 801 802 @Override toString()803 public String toString() { 804 return "Expected: " + mIntent; 805 } 806 } 807 } 808