1 /* 2 * Copyright (C) 2015 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.systemui.qs.external; 17 18 import static android.os.PowerExemptionManager.REASON_TILE_ONCLICK; 19 import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf; 20 import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT; 21 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 24 import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX; 25 26 import static junit.framework.Assert.assertFalse; 27 import static junit.framework.Assert.assertTrue; 28 29 import static org.junit.Assert.assertEquals; 30 import static org.junit.Assert.assertNotEquals; 31 import static org.mockito.ArgumentMatchers.eq; 32 import static org.mockito.Mockito.any; 33 import static org.mockito.Mockito.anyInt; 34 import static org.mockito.Mockito.anyString; 35 import static org.mockito.Mockito.clearInvocations; 36 import static org.mockito.Mockito.doAnswer; 37 import static org.mockito.Mockito.mock; 38 import static org.mockito.Mockito.never; 39 import static org.mockito.Mockito.times; 40 import static org.mockito.Mockito.verify; 41 import static org.mockito.Mockito.when; 42 43 import android.app.ActivityManager; 44 import android.app.compat.CompatChanges; 45 import android.content.BroadcastReceiver; 46 import android.content.ComponentName; 47 import android.content.Context; 48 import android.content.ContextWrapper; 49 import android.content.Intent; 50 import android.content.IntentFilter; 51 import android.content.ServiceConnection; 52 import android.content.pm.PackageInfo; 53 import android.content.pm.ServiceInfo; 54 import android.net.Uri; 55 import android.os.Bundle; 56 import android.os.Handler; 57 import android.os.HandlerThread; 58 import android.os.IDeviceIdleController; 59 import android.os.UserHandle; 60 import android.platform.test.annotations.DisableFlags; 61 import android.platform.test.annotations.EnableFlags; 62 import android.platform.test.flag.junit.FlagsParameterization; 63 import android.service.quicksettings.IQSService; 64 import android.service.quicksettings.IQSTileService; 65 import android.service.quicksettings.TileService; 66 67 import androidx.annotation.Nullable; 68 import androidx.test.filters.SmallTest; 69 70 import com.android.systemui.SysuiTestCase; 71 import com.android.systemui.broadcast.BroadcastDispatcher; 72 import com.android.systemui.util.concurrency.FakeExecutor; 73 import com.android.systemui.util.time.FakeSystemClock; 74 75 import org.junit.After; 76 import org.junit.Before; 77 import org.junit.Test; 78 import org.junit.runner.RunWith; 79 import org.mockito.ArgumentCaptor; 80 import org.mockito.InOrder; 81 import org.mockito.Mockito; 82 import org.mockito.MockitoSession; 83 84 import java.util.List; 85 86 import platform.test.runner.parameterized.ParameterizedAndroidJunit4; 87 import platform.test.runner.parameterized.Parameters; 88 89 @SmallTest 90 @RunWith(ParameterizedAndroidJunit4.class) 91 public class TileLifecycleManagerTest extends SysuiTestCase { 92 93 @Parameters(name = "{0}") getParams()94 public static List<FlagsParameterization> getParams() { 95 return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX); 96 } 97 98 private final PackageManagerAdapter mMockPackageManagerAdapter = 99 mock(PackageManagerAdapter.class); 100 private final BroadcastDispatcher mMockBroadcastDispatcher = 101 mock(BroadcastDispatcher.class); 102 private final IQSTileService.Stub mMockTileService = mock(IQSTileService.Stub.class); 103 private final ActivityManager mActivityManager = mock(ActivityManager.class); 104 private final IDeviceIdleController mDeviceIdleController = mock(IDeviceIdleController.class); 105 106 private ComponentName mTileServiceComponentName; 107 private Intent mTileServiceIntent; 108 private UserHandle mUser; 109 private FakeSystemClock mClock; 110 private FakeExecutor mExecutor; 111 private HandlerThread mThread; 112 private Handler mHandler; 113 private TileLifecycleManager mStateManager; 114 private TestContextWrapper mWrappedContext; 115 private MockitoSession mMockitoSession; 116 TileLifecycleManagerTest(FlagsParameterization flags)117 public TileLifecycleManagerTest(FlagsParameterization flags) { 118 super(); 119 mSetFlagsRule.setFlagsParameterization(flags); 120 } 121 122 @Before setUp()123 public void setUp() throws Exception { 124 setPackageEnabled(true); 125 mTileServiceComponentName = new ComponentName(mContext, "FakeTileService.class"); 126 mMockitoSession = mockitoSession() 127 .initMocks(this) 128 .mockStatic(CompatChanges.class) 129 .startMocking(); 130 131 // Stub.asInterface will just return itself. 132 when(mMockTileService.queryLocalInterface(anyString())).thenReturn(mMockTileService); 133 when(mMockTileService.asBinder()).thenReturn(mMockTileService); 134 135 mContext.addMockService(mTileServiceComponentName, mMockTileService); 136 137 mWrappedContext = new TestContextWrapper(mContext); 138 139 mTileServiceIntent = new Intent().setComponent(mTileServiceComponentName); 140 mUser = new UserHandle(UserHandle.myUserId()); 141 mThread = new HandlerThread("TestThread"); 142 mThread.start(); 143 mHandler = Handler.createAsync(mThread.getLooper()); 144 mClock = new FakeSystemClock(); 145 mExecutor = new FakeExecutor(mClock); 146 mStateManager = new TileLifecycleManager(mHandler, mWrappedContext, 147 mock(IQSService.class), 148 mMockPackageManagerAdapter, 149 mMockBroadcastDispatcher, 150 mTileServiceIntent, 151 mUser, 152 mActivityManager, 153 mDeviceIdleController, 154 mExecutor); 155 } 156 157 @After tearDown()158 public void tearDown() throws Exception { 159 if (mMockitoSession != null) { 160 mMockitoSession.finishMocking(); 161 } 162 if (mThread != null) { 163 mThread.quit(); 164 } 165 166 mStateManager.handleDestroy(); 167 } 168 setPackageEnabled(boolean enabled)169 private void setPackageEnabled(boolean enabled) throws Exception { 170 ServiceInfo defaultServiceInfo = null; 171 if (enabled) { 172 defaultServiceInfo = new ServiceInfo(); 173 defaultServiceInfo.metaData = new Bundle(); 174 defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, true); 175 defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_TOGGLEABLE_TILE, true); 176 } 177 when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), anyInt())) 178 .thenReturn(defaultServiceInfo); 179 when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt())) 180 .thenReturn(defaultServiceInfo); 181 PackageInfo defaultPackageInfo = new PackageInfo(); 182 when(mMockPackageManagerAdapter.getPackageInfoAsUser(anyString(), anyInt(), anyInt())) 183 .thenReturn(defaultPackageInfo); 184 } 185 verifyBind(int times)186 private void verifyBind(int times) { 187 assertEquals(times > 0, mContext.isBound(mTileServiceComponentName)); 188 } 189 190 @Test testBind()191 public void testBind() { 192 mStateManager.executeSetBindService(true); 193 mExecutor.runAllReady(); 194 verifyBind(1); 195 } 196 197 @Test testPackageReceiverExported()198 public void testPackageReceiverExported() throws Exception { 199 // Make sure that we register a receiver 200 setPackageEnabled(false); 201 mStateManager.executeSetBindService(true); 202 mExecutor.runAllReady(); 203 IntentFilter filter = mWrappedContext.mLastIntentFilter; 204 assertTrue(filter.hasAction(Intent.ACTION_PACKAGE_ADDED)); 205 assertTrue(filter.hasAction(Intent.ACTION_PACKAGE_CHANGED)); 206 assertTrue(filter.hasDataScheme("package")); 207 assertNotEquals(0, mWrappedContext.mLastFlag & Context.RECEIVER_EXPORTED); 208 } 209 210 @Test testUnbind()211 public void testUnbind() { 212 mStateManager.executeSetBindService(true); 213 mExecutor.runAllReady(); 214 mStateManager.executeSetBindService(false); 215 mExecutor.runAllReady(); 216 assertFalse(mContext.isBound(mTileServiceComponentName)); 217 } 218 219 @Test testTileServiceCallbacks()220 public void testTileServiceCallbacks() throws Exception { 221 mStateManager.executeSetBindService(true); 222 mExecutor.runAllReady(); 223 mStateManager.onTileAdded(); 224 verify(mMockTileService).onTileAdded(); 225 mStateManager.onStartListening(); 226 verify(mMockTileService).onStartListening(); 227 mStateManager.onClick(null); 228 verify(mMockTileService).onClick(null); 229 mStateManager.onStopListening(); 230 verify(mMockTileService).onStopListening(); 231 mStateManager.onTileRemoved(); 232 verify(mMockTileService).onTileRemoved(); 233 } 234 235 @Test testAddedBeforeBind()236 public void testAddedBeforeBind() throws Exception { 237 mStateManager.onTileAdded(); 238 mStateManager.executeSetBindService(true); 239 mExecutor.runAllReady(); 240 241 verifyBind(1); 242 verify(mMockTileService).onTileAdded(); 243 } 244 245 @Test testListeningBeforeBind()246 public void testListeningBeforeBind() throws Exception { 247 mStateManager.onTileAdded(); 248 mStateManager.onStartListening(); 249 mStateManager.executeSetBindService(true); 250 mExecutor.runAllReady(); 251 252 verifyBind(1); 253 verify(mMockTileService).onTileAdded(); 254 verify(mMockTileService).onStartListening(); 255 } 256 257 @Test testClickBeforeBind()258 public void testClickBeforeBind() throws Exception { 259 mStateManager.onTileAdded(); 260 mStateManager.onStartListening(); 261 mStateManager.onClick(null); 262 mStateManager.executeSetBindService(true); 263 mExecutor.runAllReady(); 264 265 verifyBind(1); 266 verify(mMockTileService).onTileAdded(); 267 verify(mMockTileService).onStartListening(); 268 verify(mMockTileService).onClick(null); 269 } 270 271 @Test testListeningNotListeningBeforeBind()272 public void testListeningNotListeningBeforeBind() throws Exception { 273 mStateManager.onTileAdded(); 274 mStateManager.onStartListening(); 275 mStateManager.onStopListening(); 276 mStateManager.executeSetBindService(true); 277 mExecutor.runAllReady(); 278 279 verifyBind(1); 280 mStateManager.executeSetBindService(false); 281 mExecutor.runAllReady(); 282 assertFalse(mContext.isBound(mTileServiceComponentName)); 283 verify(mMockTileService, never()).onStartListening(); 284 } 285 286 @Test 287 @DisableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX) testNoClickIfNotListeningAnymore()288 public void testNoClickIfNotListeningAnymore() throws Exception { 289 mStateManager.onTileAdded(); 290 mStateManager.onStartListening(); 291 mStateManager.onClick(null); 292 mStateManager.onStopListening(); 293 mStateManager.executeSetBindService(true); 294 mExecutor.runAllReady(); 295 296 verifyBind(1); 297 mStateManager.executeSetBindService(false); 298 mExecutor.runAllReady(); 299 assertFalse(mContext.isBound(mTileServiceComponentName)); 300 verify(mMockTileService, never()).onClick(null); 301 } 302 303 @Test 304 @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX) testNoClickIfNotListeningBeforeClick()305 public void testNoClickIfNotListeningBeforeClick() throws Exception { 306 mStateManager.onTileAdded(); 307 mStateManager.onStartListening(); 308 mStateManager.onStopListening(); 309 mStateManager.onClick(null); 310 mStateManager.executeSetBindService(true); 311 mExecutor.runAllReady(); 312 313 verifyBind(1); 314 mStateManager.executeSetBindService(false); 315 mExecutor.runAllReady(); 316 assertFalse(mContext.isBound(mTileServiceComponentName)); 317 verify(mMockTileService, never()).onClick(null); 318 } 319 320 @Test 321 @EnableFlags(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX) testClickIfStopListeningBeforeProcessedClick()322 public void testClickIfStopListeningBeforeProcessedClick() throws Exception { 323 mStateManager.onTileAdded(); 324 mStateManager.onStartListening(); 325 mStateManager.onClick(null); 326 mStateManager.onStopListening(); 327 mStateManager.executeSetBindService(true); 328 mExecutor.runAllReady(); 329 330 verifyBind(1); 331 mStateManager.executeSetBindService(false); 332 mExecutor.runAllReady(); 333 assertFalse(mContext.isBound(mTileServiceComponentName)); 334 InOrder inOrder = Mockito.inOrder(mMockTileService); 335 inOrder.verify(mMockTileService).onClick(null); 336 inOrder.verify(mMockTileService).onStopListening(); 337 } 338 339 @Test testComponentEnabling()340 public void testComponentEnabling() throws Exception { 341 mStateManager.onTileAdded(); 342 mStateManager.onStartListening(); 343 setPackageEnabled(false); 344 mStateManager.executeSetBindService(true); 345 mExecutor.runAllReady(); 346 // Package not available, not yet created. 347 verifyBind(0); 348 349 // Package is re-enabled. 350 setPackageEnabled(true); 351 mStateManager.onReceive( 352 mContext, 353 new Intent( 354 Intent.ACTION_PACKAGE_CHANGED, 355 Uri.fromParts( 356 "package", mTileServiceComponentName.getPackageName(), null))); 357 mExecutor.runAllReady(); 358 verifyBind(1); 359 } 360 361 @Test testKillProcess()362 public void testKillProcess() throws Exception { 363 mStateManager.onStartListening(); 364 mStateManager.executeSetBindService(true); 365 mExecutor.runAllReady(); 366 mStateManager.onBindingDied(mTileServiceComponentName); 367 mExecutor.runAllReady(); 368 mClock.advanceTime(5000); 369 mExecutor.runAllReady(); 370 371 // Two calls: one for the first bind, one for the restart. 372 verifyBind(2); 373 verify(mMockTileService, times(2)).onStartListening(); 374 } 375 376 @Test testKillProcessLowMemory()377 public void testKillProcessLowMemory() throws Exception { 378 doAnswer(invocation -> { 379 ActivityManager.MemoryInfo memoryInfo = invocation.getArgument(0); 380 memoryInfo.lowMemory = true; 381 return null; 382 }).when(mActivityManager).getMemoryInfo(any()); 383 mStateManager.onStartListening(); 384 mStateManager.executeSetBindService(true); 385 mExecutor.runAllReady(); 386 verify(mMockTileService, times(1)).onStartListening(); 387 mStateManager.onBindingDied(mTileServiceComponentName); 388 mExecutor.runAllReady(); 389 390 // Longer delay than a regular one 391 mClock.advanceTime(5000); 392 mExecutor.runAllReady(); 393 394 assertFalse(mContext.isBound(mTileServiceComponentName)); 395 396 mClock.advanceTime(20000); 397 mExecutor.runAllReady(); 398 // Two calls: one for the first bind, one for the restart. 399 verifyBind(2); 400 verify(mMockTileService, times(2)).onStartListening(); 401 } 402 403 @Test testOnServiceDisconnectedDoesnUnbind_doesntForwardToBinder()404 public void testOnServiceDisconnectedDoesnUnbind_doesntForwardToBinder() throws Exception { 405 mStateManager.executeSetBindService(true); 406 mExecutor.runAllReady(); 407 408 mStateManager.onStartListening(); 409 verify(mMockTileService).onStartListening(); 410 411 clearInvocations(mMockTileService); 412 mStateManager.onServiceDisconnected(mTileServiceComponentName); 413 mExecutor.runAllReady(); 414 415 mStateManager.onStartListening(); 416 verify(mMockTileService, never()).onStartListening(); 417 } 418 419 @Test testKillProcessLowMemory_unbound_doesntBindAgain()420 public void testKillProcessLowMemory_unbound_doesntBindAgain() throws Exception { 421 doAnswer(invocation -> { 422 ActivityManager.MemoryInfo memoryInfo = invocation.getArgument(0); 423 memoryInfo.lowMemory = true; 424 return null; 425 }).when(mActivityManager).getMemoryInfo(any()); 426 mStateManager.onStartListening(); 427 mStateManager.executeSetBindService(true); 428 mExecutor.runAllReady(); 429 verifyBind(1); 430 verify(mMockTileService, times(1)).onStartListening(); 431 432 mStateManager.onBindingDied(mTileServiceComponentName); 433 mExecutor.runAllReady(); 434 435 clearInvocations(mMockTileService); 436 mStateManager.executeSetBindService(false); 437 mExecutor.runAllReady(); 438 mClock.advanceTime(30000); 439 mExecutor.runAllReady(); 440 441 verifyBind(0); 442 verify(mMockTileService, never()).onStartListening(); 443 } 444 445 @Test testToggleableTile()446 public void testToggleableTile() throws Exception { 447 assertTrue(mStateManager.isToggleableTile()); 448 } 449 450 @Test testClickCallsDeviceIdleManager()451 public void testClickCallsDeviceIdleManager() throws Exception { 452 mStateManager.onTileAdded(); 453 mStateManager.onStartListening(); 454 mStateManager.onClick(null); 455 mStateManager.executeSetBindService(true); 456 mExecutor.runAllReady(); 457 458 verify(mMockTileService).onClick(null); 459 verify(mDeviceIdleController).addPowerSaveTempWhitelistApp( 460 mTileServiceComponentName.getPackageName(), 15000, 461 mUser.getIdentifier(), REASON_TILE_ONCLICK, "tile onclick"); 462 } 463 464 @Test testFalseBindCallsUnbind()465 public void testFalseBindCallsUnbind() { 466 Context falseContext = mock(Context.class); 467 when(falseContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(false); 468 TileLifecycleManager manager = new TileLifecycleManager(mHandler, falseContext, 469 mock(IQSService.class), 470 mMockPackageManagerAdapter, 471 mMockBroadcastDispatcher, 472 mTileServiceIntent, 473 mUser, 474 mActivityManager, 475 mDeviceIdleController, 476 mExecutor); 477 478 manager.executeSetBindService(true); 479 mExecutor.runAllReady(); 480 481 ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class); 482 verify(falseContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any()); 483 484 verify(falseContext).unbindService(captor.getValue()); 485 } 486 487 @Test testVersionUDoesNotBindsAllowBackgroundActivity()488 public void testVersionUDoesNotBindsAllowBackgroundActivity() { 489 mockChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, true); 490 Context falseContext = mock(Context.class); 491 TileLifecycleManager manager = new TileLifecycleManager(mHandler, falseContext, 492 mock(IQSService.class), 493 mMockPackageManagerAdapter, 494 mMockBroadcastDispatcher, 495 mTileServiceIntent, 496 mUser, 497 mActivityManager, 498 mDeviceIdleController, 499 mExecutor); 500 501 manager.executeSetBindService(true); 502 mExecutor.runAllReady(); 503 int flags = Context.BIND_AUTO_CREATE 504 | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE 505 | Context.BIND_WAIVE_PRIORITY; 506 507 verify(falseContext).bindServiceAsUser(any(), any(), eq(flags), any()); 508 } 509 510 @Test testVersionLessThanUBindsAllowBackgroundActivity()511 public void testVersionLessThanUBindsAllowBackgroundActivity() { 512 mockChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, false); 513 Context falseContext = mock(Context.class); 514 TileLifecycleManager manager = new TileLifecycleManager(mHandler, falseContext, 515 mock(IQSService.class), 516 mMockPackageManagerAdapter, 517 mMockBroadcastDispatcher, 518 mTileServiceIntent, 519 mUser, 520 mActivityManager, 521 mDeviceIdleController, 522 mExecutor); 523 524 manager.executeSetBindService(true); 525 mExecutor.runAllReady(); 526 int flags = Context.BIND_AUTO_CREATE 527 | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE 528 | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS 529 | Context.BIND_WAIVE_PRIORITY; 530 531 verify(falseContext).bindServiceAsUser(any(), any(), eq(flags), any()); 532 } 533 534 @Test testNullBindingCallsUnbind()535 public void testNullBindingCallsUnbind() { 536 Context mockContext = mock(Context.class); 537 // Binding has to succeed 538 when(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true); 539 TileLifecycleManager manager = new TileLifecycleManager(mHandler, mockContext, 540 mock(IQSService.class), 541 mMockPackageManagerAdapter, 542 mMockBroadcastDispatcher, 543 mTileServiceIntent, 544 mUser, 545 mActivityManager, 546 mDeviceIdleController, 547 mExecutor); 548 549 manager.executeSetBindService(true); 550 mExecutor.runAllReady(); 551 552 ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class); 553 verify(mockContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any()); 554 555 captor.getValue().onNullBinding(mTileServiceComponentName); 556 mExecutor.runAllReady(); 557 verify(mockContext).unbindService(captor.getValue()); 558 } 559 mockChangeEnabled(long changeId, boolean enabled)560 private void mockChangeEnabled(long changeId, boolean enabled) { 561 doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(), 562 any(UserHandle.class))); 563 } 564 565 private static class TestContextWrapper extends ContextWrapper { 566 private IntentFilter mLastIntentFilter; 567 private int mLastFlag; 568 TestContextWrapper(Context base)569 TestContextWrapper(Context base) { 570 super(base); 571 } 572 573 @Override registerReceiverAsUser(@ullable BroadcastReceiver receiver, UserHandle user, IntentFilter filter, @Nullable String broadcastPermission, @Nullable Handler scheduler, int flags)574 public Intent registerReceiverAsUser(@Nullable BroadcastReceiver receiver, UserHandle user, 575 IntentFilter filter, @Nullable String broadcastPermission, 576 @Nullable Handler scheduler, int flags) { 577 mLastIntentFilter = filter; 578 mLastFlag = flags; 579 return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission, 580 scheduler, flags); 581 } 582 } 583 } 584