1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.wm.shell.sysui; 18 19 import static org.junit.Assert.assertThrows; 20 import static org.junit.Assert.assertTrue; 21 import static org.mockito.Mockito.mock; 22 23 import android.content.Context; 24 import android.content.pm.UserInfo; 25 import android.content.res.Configuration; 26 import android.graphics.Rect; 27 import android.os.Binder; 28 import android.os.Bundle; 29 import android.os.IBinder; 30 import android.testing.AndroidTestingRunner; 31 import android.testing.TestableLooper; 32 33 import androidx.annotation.NonNull; 34 import androidx.test.filters.SmallTest; 35 import androidx.test.platform.app.InstrumentationRegistry; 36 37 import com.android.wm.shell.ShellTestCase; 38 import com.android.wm.shell.TestShellExecutor; 39 import com.android.wm.shell.common.DisplayInsetsController; 40 import com.android.wm.shell.common.ExternalInterfaceBinder; 41 42 import org.junit.After; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.Mock; 47 import org.mockito.MockitoAnnotations; 48 49 import java.util.ArrayList; 50 import java.util.List; 51 import java.util.Locale; 52 53 @SmallTest 54 @RunWith(AndroidTestingRunner.class) 55 @TestableLooper.RunWithLooper(setAsMainLooper = true) 56 public class ShellControllerTest extends ShellTestCase { 57 58 private static final int TEST_USER_ID = 100; 59 private static final String EXTRA_TEST_BINDER = "test_binder"; 60 61 @Mock 62 private ShellInit mShellInit; 63 @Mock 64 private ShellCommandHandler mShellCommandHandler; 65 @Mock 66 private Context mTestUserContext; 67 @Mock 68 private DisplayInsetsController mDisplayInsetsController; 69 70 private TestShellExecutor mExecutor; 71 private ShellController mController; 72 private TestConfigurationChangeListener mConfigChangeListener; 73 private TestKeyguardChangeListener mKeyguardChangeListener; 74 private TestUserChangeListener mUserChangeListener; 75 private TestDisplayImeChangeListener mDisplayImeChangeListener; 76 77 78 @Before setUp()79 public void setUp() { 80 MockitoAnnotations.initMocks(this); 81 mKeyguardChangeListener = new TestKeyguardChangeListener(); 82 mConfigChangeListener = new TestConfigurationChangeListener(); 83 mUserChangeListener = new TestUserChangeListener(); 84 mDisplayImeChangeListener = new TestDisplayImeChangeListener(); 85 mExecutor = new TestShellExecutor(); 86 mController = new ShellController(mContext, mShellInit, mShellCommandHandler, 87 mDisplayInsetsController, mExecutor); 88 mController.onConfigurationChanged(getConfigurationCopy()); 89 } 90 91 @After tearDown()92 public void tearDown() { 93 // Do nothing 94 } 95 96 @Test testAddExternalInterface_ensureCallback()97 public void testAddExternalInterface_ensureCallback() { 98 Binder callback = new Binder(); 99 ExternalInterfaceBinder wrapper = new ExternalInterfaceBinder() { 100 @Override 101 public void invalidate() { 102 // Do nothing 103 } 104 105 @Override 106 public IBinder asBinder() { 107 return callback; 108 } 109 }; 110 mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this); 111 112 Bundle b = new Bundle(); 113 mController.asShell().createExternalInterfaces(b); 114 mExecutor.flushAll(); 115 assertTrue(b.getIBinder(EXTRA_TEST_BINDER) == callback); 116 } 117 118 @Test testAddExternalInterface_disallowDuplicateKeys()119 public void testAddExternalInterface_disallowDuplicateKeys() { 120 Binder callback = new Binder(); 121 ExternalInterfaceBinder wrapper = new ExternalInterfaceBinder() { 122 @Override 123 public void invalidate() { 124 // Do nothing 125 } 126 127 @Override 128 public IBinder asBinder() { 129 return callback; 130 } 131 }; 132 mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this); 133 assertThrows(IllegalArgumentException.class, () -> { 134 mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this); 135 }); 136 } 137 138 @Test testAddDisplayImeChangeListener_ensureCallback()139 public void testAddDisplayImeChangeListener_ensureCallback() { 140 mController.asShell().addDisplayImeChangeListener( 141 mDisplayImeChangeListener, mExecutor); 142 143 final Rect bounds = new Rect(10, 20, 30, 40); 144 mController.onImeBoundsChanged(bounds); 145 mController.onImeVisibilityChanged(true); 146 mExecutor.flushAll(); 147 148 assertTrue(mDisplayImeChangeListener.boundsChanged == 1); 149 assertTrue(bounds.equals(mDisplayImeChangeListener.lastBounds)); 150 assertTrue(mDisplayImeChangeListener.visibilityChanged == 1); 151 assertTrue(mDisplayImeChangeListener.lastVisibility); 152 } 153 154 @Test testDoubleAddDisplayImeChangeListener_ensureSingleCallback()155 public void testDoubleAddDisplayImeChangeListener_ensureSingleCallback() { 156 mController.asShell().addDisplayImeChangeListener( 157 mDisplayImeChangeListener, mExecutor); 158 mController.asShell().addDisplayImeChangeListener( 159 mDisplayImeChangeListener, mExecutor); 160 161 mController.onImeVisibilityChanged(true); 162 mExecutor.flushAll(); 163 assertTrue(mDisplayImeChangeListener.visibilityChanged == 1); 164 } 165 166 @Test testAddRemoveDisplayImeChangeListener_ensureNoCallback()167 public void testAddRemoveDisplayImeChangeListener_ensureNoCallback() { 168 mController.asShell().addDisplayImeChangeListener( 169 mDisplayImeChangeListener, mExecutor); 170 mController.asShell().removeDisplayImeChangeListener(mDisplayImeChangeListener); 171 172 mController.onImeVisibilityChanged(true); 173 mExecutor.flushAll(); 174 assertTrue(mDisplayImeChangeListener.visibilityChanged == 0); 175 } 176 177 @Test testAddUserChangeListener_ensureCallback()178 public void testAddUserChangeListener_ensureCallback() { 179 mController.addUserChangeListener(mUserChangeListener); 180 181 mController.onUserChanged(TEST_USER_ID, mTestUserContext); 182 assertTrue(mUserChangeListener.userChanged == 1); 183 assertTrue(mUserChangeListener.lastUserContext == mTestUserContext); 184 } 185 186 @Test testDoubleAddUserChangeListener_ensureSingleCallback()187 public void testDoubleAddUserChangeListener_ensureSingleCallback() { 188 mController.addUserChangeListener(mUserChangeListener); 189 mController.addUserChangeListener(mUserChangeListener); 190 191 mController.onUserChanged(TEST_USER_ID, mTestUserContext); 192 assertTrue(mUserChangeListener.userChanged == 1); 193 assertTrue(mUserChangeListener.lastUserContext == mTestUserContext); 194 } 195 196 @Test testAddRemoveUserChangeListener_ensureNoCallback()197 public void testAddRemoveUserChangeListener_ensureNoCallback() { 198 mController.addUserChangeListener(mUserChangeListener); 199 mController.removeUserChangeListener(mUserChangeListener); 200 201 mController.onUserChanged(TEST_USER_ID, mTestUserContext); 202 assertTrue(mUserChangeListener.userChanged == 0); 203 assertTrue(mUserChangeListener.lastUserContext == null); 204 } 205 206 @Test testUserProfilesChanged()207 public void testUserProfilesChanged() { 208 mController.addUserChangeListener(mUserChangeListener); 209 210 ArrayList<UserInfo> profiles = new ArrayList<>(); 211 profiles.add(mock(UserInfo.class)); 212 profiles.add(mock(UserInfo.class)); 213 mController.onUserProfilesChanged(profiles); 214 assertTrue(mUserChangeListener.lastUserProfiles.equals(profiles)); 215 } 216 217 @Test testAddKeyguardChangeListener_ensureCallback()218 public void testAddKeyguardChangeListener_ensureCallback() { 219 mController.addKeyguardChangeListener(mKeyguardChangeListener); 220 221 mController.onKeyguardVisibilityChanged(true, false, false); 222 assertTrue(mKeyguardChangeListener.visibilityChanged == 1); 223 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); 224 } 225 226 @Test testDoubleAddKeyguardChangeListener_ensureSingleCallback()227 public void testDoubleAddKeyguardChangeListener_ensureSingleCallback() { 228 mController.addKeyguardChangeListener(mKeyguardChangeListener); 229 mController.addKeyguardChangeListener(mKeyguardChangeListener); 230 231 mController.onKeyguardVisibilityChanged(true, false, false); 232 assertTrue(mKeyguardChangeListener.visibilityChanged == 1); 233 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); 234 } 235 236 @Test testAddRemoveKeyguardChangeListener_ensureNoCallback()237 public void testAddRemoveKeyguardChangeListener_ensureNoCallback() { 238 mController.addKeyguardChangeListener(mKeyguardChangeListener); 239 mController.removeKeyguardChangeListener(mKeyguardChangeListener); 240 241 mController.onKeyguardVisibilityChanged(true, false, false); 242 assertTrue(mKeyguardChangeListener.visibilityChanged == 0); 243 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); 244 } 245 246 @Test testKeyguardVisibilityChanged()247 public void testKeyguardVisibilityChanged() { 248 mController.addKeyguardChangeListener(mKeyguardChangeListener); 249 250 mController.onKeyguardVisibilityChanged(true, true, true); 251 assertTrue(mKeyguardChangeListener.visibilityChanged == 1); 252 assertTrue(mKeyguardChangeListener.lastAnimatingDismiss); 253 assertTrue(mKeyguardChangeListener.lastOccluded); 254 assertTrue(mKeyguardChangeListener.lastAnimatingDismiss); 255 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); 256 } 257 258 @Test testKeyguardDismissAnimationFinished()259 public void testKeyguardDismissAnimationFinished() { 260 mController.addKeyguardChangeListener(mKeyguardChangeListener); 261 262 mController.onKeyguardDismissAnimationFinished(); 263 assertTrue(mKeyguardChangeListener.visibilityChanged == 0); 264 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 1); 265 } 266 267 @Test testAddConfigurationChangeListener_ensureCallback()268 public void testAddConfigurationChangeListener_ensureCallback() { 269 mController.addConfigurationChangeListener(mConfigChangeListener); 270 271 Configuration newConfig = getConfigurationCopy(); 272 newConfig.densityDpi = 200; 273 mController.onConfigurationChanged(newConfig); 274 assertTrue(mConfigChangeListener.configChanges == 1); 275 } 276 277 @Test testDoubleAddConfigurationChangeListener_ensureSingleCallback()278 public void testDoubleAddConfigurationChangeListener_ensureSingleCallback() { 279 mController.addConfigurationChangeListener(mConfigChangeListener); 280 mController.addConfigurationChangeListener(mConfigChangeListener); 281 282 Configuration newConfig = getConfigurationCopy(); 283 newConfig.densityDpi = 200; 284 mController.onConfigurationChanged(newConfig); 285 assertTrue(mConfigChangeListener.configChanges == 1); 286 } 287 288 @Test testAddRemoveConfigurationChangeListener_ensureNoCallback()289 public void testAddRemoveConfigurationChangeListener_ensureNoCallback() { 290 mController.addConfigurationChangeListener(mConfigChangeListener); 291 mController.removeConfigurationChangeListener(mConfigChangeListener); 292 293 Configuration newConfig = getConfigurationCopy(); 294 newConfig.densityDpi = 200; 295 mController.onConfigurationChanged(newConfig); 296 assertTrue(mConfigChangeListener.configChanges == 0); 297 } 298 299 @Test testMultipleConfigurationChangeListeners()300 public void testMultipleConfigurationChangeListeners() { 301 TestConfigurationChangeListener listener2 = new TestConfigurationChangeListener(); 302 mController.addConfigurationChangeListener(mConfigChangeListener); 303 mController.addConfigurationChangeListener(listener2); 304 305 Configuration newConfig = getConfigurationCopy(); 306 newConfig.densityDpi = 200; 307 mController.onConfigurationChanged(newConfig); 308 assertTrue(mConfigChangeListener.configChanges == 1); 309 assertTrue(listener2.configChanges == 1); 310 } 311 312 @Test testRemoveListenerDuringCallback()313 public void testRemoveListenerDuringCallback() { 314 TestConfigurationChangeListener badListener = new TestConfigurationChangeListener() { 315 @Override 316 public void onConfigurationChanged(Configuration newConfiguration) { 317 mController.removeConfigurationChangeListener(this); 318 } 319 }; 320 mController.addConfigurationChangeListener(badListener); 321 mController.addConfigurationChangeListener(mConfigChangeListener); 322 323 // Ensure we don't fail just because a listener was removed mid-callback 324 Configuration newConfig = getConfigurationCopy(); 325 newConfig.densityDpi = 200; 326 mController.onConfigurationChanged(newConfig); 327 } 328 329 @Test testDensityChangeCallback()330 public void testDensityChangeCallback() { 331 mController.addConfigurationChangeListener(mConfigChangeListener); 332 333 Configuration newConfig = getConfigurationCopy(); 334 newConfig.densityDpi = 200; 335 mController.onConfigurationChanged(newConfig); 336 assertTrue(mConfigChangeListener.configChanges == 1); 337 assertTrue(mConfigChangeListener.densityChanges == 1); 338 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 339 assertTrue(mConfigChangeListener.themeChanges == 0); 340 assertTrue(mConfigChangeListener.localeChanges == 0); 341 } 342 343 @Test testFontScaleChangeCallback()344 public void testFontScaleChangeCallback() { 345 mController.addConfigurationChangeListener(mConfigChangeListener); 346 347 Configuration newConfig = getConfigurationCopy(); 348 newConfig.fontScale = 2; 349 mController.onConfigurationChanged(newConfig); 350 assertTrue(mConfigChangeListener.configChanges == 1); 351 assertTrue(mConfigChangeListener.densityChanges == 1); 352 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 353 assertTrue(mConfigChangeListener.themeChanges == 0); 354 assertTrue(mConfigChangeListener.localeChanges == 0); 355 } 356 357 @Test testSmallestWidthChangeCallback()358 public void testSmallestWidthChangeCallback() { 359 mController.addConfigurationChangeListener(mConfigChangeListener); 360 361 Configuration newConfig = getConfigurationCopy(); 362 newConfig.smallestScreenWidthDp = 100; 363 mController.onConfigurationChanged(newConfig); 364 assertTrue(mConfigChangeListener.configChanges == 1); 365 assertTrue(mConfigChangeListener.densityChanges == 0); 366 assertTrue(mConfigChangeListener.smallestWidthChanges == 1); 367 assertTrue(mConfigChangeListener.themeChanges == 0); 368 assertTrue(mConfigChangeListener.localeChanges == 0); 369 } 370 371 @Test testThemeChangeCallback()372 public void testThemeChangeCallback() { 373 mController.addConfigurationChangeListener(mConfigChangeListener); 374 375 Configuration newConfig = getConfigurationCopy(); 376 newConfig.assetsSeq++; 377 mController.onConfigurationChanged(newConfig); 378 assertTrue(mConfigChangeListener.configChanges == 1); 379 assertTrue(mConfigChangeListener.densityChanges == 0); 380 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 381 assertTrue(mConfigChangeListener.themeChanges == 1); 382 assertTrue(mConfigChangeListener.localeChanges == 0); 383 } 384 385 @Test testNightModeChangeCallback()386 public void testNightModeChangeCallback() { 387 mController.addConfigurationChangeListener(mConfigChangeListener); 388 389 Configuration newConfig = getConfigurationCopy(); 390 newConfig.uiMode = Configuration.UI_MODE_NIGHT_YES; 391 mController.onConfigurationChanged(newConfig); 392 assertTrue(mConfigChangeListener.configChanges == 1); 393 assertTrue(mConfigChangeListener.densityChanges == 0); 394 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 395 assertTrue(mConfigChangeListener.themeChanges == 1); 396 assertTrue(mConfigChangeListener.localeChanges == 0); 397 } 398 399 @Test testLocaleChangeCallback()400 public void testLocaleChangeCallback() { 401 mController.addConfigurationChangeListener(mConfigChangeListener); 402 403 Configuration newConfig = getConfigurationCopy(); 404 // Just change the locales to be different 405 if (newConfig.locale == Locale.CANADA) { 406 newConfig.locale = Locale.US; 407 } else { 408 newConfig.locale = Locale.CANADA; 409 } 410 mController.onConfigurationChanged(newConfig); 411 assertTrue(mConfigChangeListener.configChanges == 1); 412 assertTrue(mConfigChangeListener.densityChanges == 0); 413 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 414 assertTrue(mConfigChangeListener.themeChanges == 0); 415 assertTrue(mConfigChangeListener.localeChanges == 1); 416 } 417 getConfigurationCopy()418 private Configuration getConfigurationCopy() { 419 final Configuration c = new Configuration(InstrumentationRegistry.getInstrumentation() 420 .getTargetContext().getResources().getConfiguration()); 421 // In tests this might be undefined so make sure it's valid 422 c.assetsSeq = 1; 423 return c; 424 } 425 426 private class TestConfigurationChangeListener implements ConfigurationChangeListener { 427 // Counts of number of times each of the callbacks are called 428 public int configChanges; 429 public int densityChanges; 430 public int smallestWidthChanges; 431 public int themeChanges; 432 public int localeChanges; 433 434 @Override onConfigurationChanged(Configuration newConfiguration)435 public void onConfigurationChanged(Configuration newConfiguration) { 436 configChanges++; 437 } 438 439 @Override onDensityOrFontScaleChanged()440 public void onDensityOrFontScaleChanged() { 441 densityChanges++; 442 } 443 444 @Override onSmallestScreenWidthChanged()445 public void onSmallestScreenWidthChanged() { 446 smallestWidthChanges++; 447 } 448 449 @Override onThemeChanged()450 public void onThemeChanged() { 451 themeChanges++; 452 } 453 454 @Override onLocaleOrLayoutDirectionChanged()455 public void onLocaleOrLayoutDirectionChanged() { 456 localeChanges++; 457 } 458 } 459 460 private class TestKeyguardChangeListener implements KeyguardChangeListener { 461 // Counts of number of times each of the callbacks are called 462 public int visibilityChanged; 463 public boolean lastVisibility; 464 public boolean lastOccluded; 465 public boolean lastAnimatingDismiss; 466 public int dismissAnimationFinished; 467 468 @Override onKeyguardVisibilityChanged(boolean visible, boolean occluded, boolean animatingDismiss)469 public void onKeyguardVisibilityChanged(boolean visible, boolean occluded, 470 boolean animatingDismiss) { 471 lastVisibility = visible; 472 lastOccluded = occluded; 473 lastAnimatingDismiss = animatingDismiss; 474 visibilityChanged++; 475 } 476 477 @Override onKeyguardDismissAnimationFinished()478 public void onKeyguardDismissAnimationFinished() { 479 dismissAnimationFinished++; 480 } 481 } 482 483 private class TestUserChangeListener implements UserChangeListener { 484 // Counts of number of times each of the callbacks are called 485 public int userChanged; 486 public int lastUserId; 487 public Context lastUserContext; 488 public int userProfilesChanged; 489 public List<? extends UserInfo> lastUserProfiles; 490 491 492 @Override onUserChanged(int newUserId, @NonNull Context userContext)493 public void onUserChanged(int newUserId, @NonNull Context userContext) { 494 userChanged++; 495 lastUserId = newUserId; 496 lastUserContext = userContext; 497 } 498 499 @Override onUserProfilesChanged(@onNull List<UserInfo> profiles)500 public void onUserProfilesChanged(@NonNull List<UserInfo> profiles) { 501 userProfilesChanged++; 502 lastUserProfiles = profiles; 503 } 504 } 505 506 private static class TestDisplayImeChangeListener implements DisplayImeChangeListener { 507 public int boundsChanged = 0; 508 public Rect lastBounds; 509 public int visibilityChanged = 0; 510 public boolean lastVisibility = false; 511 512 @Override onImeBoundsChanged(int displayId, Rect bounds)513 public void onImeBoundsChanged(int displayId, Rect bounds) { 514 boundsChanged++; 515 lastBounds = bounds; 516 } 517 518 @Override onImeVisibilityChanged(int displayId, boolean isShowing)519 public void onImeVisibilityChanged(int displayId, boolean isShowing) { 520 visibilityChanged++; 521 lastVisibility = isShowing; 522 } 523 } 524 } 525