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.os.Binder; 27 import android.os.Bundle; 28 import android.os.IBinder; 29 import android.testing.AndroidTestingRunner; 30 import android.testing.TestableLooper; 31 32 import androidx.annotation.NonNull; 33 import androidx.test.filters.SmallTest; 34 import androidx.test.platform.app.InstrumentationRegistry; 35 36 import com.android.wm.shell.ShellTestCase; 37 import com.android.wm.shell.TestShellExecutor; 38 import com.android.wm.shell.common.ExternalInterfaceBinder; 39 import com.android.wm.shell.common.ShellExecutor; 40 41 import org.junit.After; 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 import org.mockito.Mock; 46 import org.mockito.MockitoAnnotations; 47 48 import java.util.ArrayList; 49 import java.util.List; 50 import java.util.Locale; 51 52 @SmallTest 53 @RunWith(AndroidTestingRunner.class) 54 @TestableLooper.RunWithLooper(setAsMainLooper = true) 55 public class ShellControllerTest extends ShellTestCase { 56 57 private static final int TEST_USER_ID = 100; 58 private static final String EXTRA_TEST_BINDER = "test_binder"; 59 60 @Mock 61 private ShellInit mShellInit; 62 @Mock 63 private ShellCommandHandler mShellCommandHandler; 64 @Mock 65 private Context mTestUserContext; 66 67 private TestShellExecutor mExecutor; 68 private ShellController mController; 69 private TestConfigurationChangeListener mConfigChangeListener; 70 private TestKeyguardChangeListener mKeyguardChangeListener; 71 private TestUserChangeListener mUserChangeListener; 72 73 74 @Before setUp()75 public void setUp() { 76 MockitoAnnotations.initMocks(this); 77 mKeyguardChangeListener = new TestKeyguardChangeListener(); 78 mConfigChangeListener = new TestConfigurationChangeListener(); 79 mUserChangeListener = new TestUserChangeListener(); 80 mExecutor = new TestShellExecutor(); 81 mController = new ShellController(mContext, mShellInit, mShellCommandHandler, mExecutor); 82 mController.onConfigurationChanged(getConfigurationCopy()); 83 } 84 85 @After tearDown()86 public void tearDown() { 87 // Do nothing 88 } 89 90 @Test testAddExternalInterface_ensureCallback()91 public void testAddExternalInterface_ensureCallback() { 92 Binder callback = new Binder(); 93 ExternalInterfaceBinder wrapper = new ExternalInterfaceBinder() { 94 @Override 95 public void invalidate() { 96 // Do nothing 97 } 98 99 @Override 100 public IBinder asBinder() { 101 return callback; 102 } 103 }; 104 mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this); 105 106 Bundle b = new Bundle(); 107 mController.asShell().createExternalInterfaces(b); 108 mExecutor.flushAll(); 109 assertTrue(b.getIBinder(EXTRA_TEST_BINDER) == callback); 110 } 111 112 @Test testAddExternalInterface_disallowDuplicateKeys()113 public void testAddExternalInterface_disallowDuplicateKeys() { 114 Binder callback = new Binder(); 115 ExternalInterfaceBinder wrapper = new ExternalInterfaceBinder() { 116 @Override 117 public void invalidate() { 118 // Do nothing 119 } 120 121 @Override 122 public IBinder asBinder() { 123 return callback; 124 } 125 }; 126 mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this); 127 assertThrows(IllegalArgumentException.class, () -> { 128 mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this); 129 }); 130 } 131 132 @Test testAddUserChangeListener_ensureCallback()133 public void testAddUserChangeListener_ensureCallback() { 134 mController.addUserChangeListener(mUserChangeListener); 135 136 mController.onUserChanged(TEST_USER_ID, mTestUserContext); 137 assertTrue(mUserChangeListener.userChanged == 1); 138 assertTrue(mUserChangeListener.lastUserContext == mTestUserContext); 139 } 140 141 @Test testDoubleAddUserChangeListener_ensureSingleCallback()142 public void testDoubleAddUserChangeListener_ensureSingleCallback() { 143 mController.addUserChangeListener(mUserChangeListener); 144 mController.addUserChangeListener(mUserChangeListener); 145 146 mController.onUserChanged(TEST_USER_ID, mTestUserContext); 147 assertTrue(mUserChangeListener.userChanged == 1); 148 assertTrue(mUserChangeListener.lastUserContext == mTestUserContext); 149 } 150 151 @Test testAddRemoveUserChangeListener_ensureNoCallback()152 public void testAddRemoveUserChangeListener_ensureNoCallback() { 153 mController.addUserChangeListener(mUserChangeListener); 154 mController.removeUserChangeListener(mUserChangeListener); 155 156 mController.onUserChanged(TEST_USER_ID, mTestUserContext); 157 assertTrue(mUserChangeListener.userChanged == 0); 158 assertTrue(mUserChangeListener.lastUserContext == null); 159 } 160 161 @Test testUserProfilesChanged()162 public void testUserProfilesChanged() { 163 mController.addUserChangeListener(mUserChangeListener); 164 165 ArrayList<UserInfo> profiles = new ArrayList<>(); 166 profiles.add(mock(UserInfo.class)); 167 profiles.add(mock(UserInfo.class)); 168 mController.onUserProfilesChanged(profiles); 169 assertTrue(mUserChangeListener.lastUserProfiles.equals(profiles)); 170 } 171 172 @Test testAddKeyguardChangeListener_ensureCallback()173 public void testAddKeyguardChangeListener_ensureCallback() { 174 mController.addKeyguardChangeListener(mKeyguardChangeListener); 175 176 mController.onKeyguardVisibilityChanged(true, false, false); 177 assertTrue(mKeyguardChangeListener.visibilityChanged == 1); 178 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); 179 } 180 181 @Test testDoubleAddKeyguardChangeListener_ensureSingleCallback()182 public void testDoubleAddKeyguardChangeListener_ensureSingleCallback() { 183 mController.addKeyguardChangeListener(mKeyguardChangeListener); 184 mController.addKeyguardChangeListener(mKeyguardChangeListener); 185 186 mController.onKeyguardVisibilityChanged(true, false, false); 187 assertTrue(mKeyguardChangeListener.visibilityChanged == 1); 188 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); 189 } 190 191 @Test testAddRemoveKeyguardChangeListener_ensureNoCallback()192 public void testAddRemoveKeyguardChangeListener_ensureNoCallback() { 193 mController.addKeyguardChangeListener(mKeyguardChangeListener); 194 mController.removeKeyguardChangeListener(mKeyguardChangeListener); 195 196 mController.onKeyguardVisibilityChanged(true, false, false); 197 assertTrue(mKeyguardChangeListener.visibilityChanged == 0); 198 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); 199 } 200 201 @Test testKeyguardVisibilityChanged()202 public void testKeyguardVisibilityChanged() { 203 mController.addKeyguardChangeListener(mKeyguardChangeListener); 204 205 mController.onKeyguardVisibilityChanged(true, true, true); 206 assertTrue(mKeyguardChangeListener.visibilityChanged == 1); 207 assertTrue(mKeyguardChangeListener.lastAnimatingDismiss); 208 assertTrue(mKeyguardChangeListener.lastOccluded); 209 assertTrue(mKeyguardChangeListener.lastAnimatingDismiss); 210 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); 211 } 212 213 @Test testKeyguardDismissAnimationFinished()214 public void testKeyguardDismissAnimationFinished() { 215 mController.addKeyguardChangeListener(mKeyguardChangeListener); 216 217 mController.onKeyguardDismissAnimationFinished(); 218 assertTrue(mKeyguardChangeListener.visibilityChanged == 0); 219 assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 1); 220 } 221 222 @Test testAddConfigurationChangeListener_ensureCallback()223 public void testAddConfigurationChangeListener_ensureCallback() { 224 mController.addConfigurationChangeListener(mConfigChangeListener); 225 226 Configuration newConfig = getConfigurationCopy(); 227 newConfig.densityDpi = 200; 228 mController.onConfigurationChanged(newConfig); 229 assertTrue(mConfigChangeListener.configChanges == 1); 230 } 231 232 @Test testDoubleAddConfigurationChangeListener_ensureSingleCallback()233 public void testDoubleAddConfigurationChangeListener_ensureSingleCallback() { 234 mController.addConfigurationChangeListener(mConfigChangeListener); 235 mController.addConfigurationChangeListener(mConfigChangeListener); 236 237 Configuration newConfig = getConfigurationCopy(); 238 newConfig.densityDpi = 200; 239 mController.onConfigurationChanged(newConfig); 240 assertTrue(mConfigChangeListener.configChanges == 1); 241 } 242 243 @Test testAddRemoveConfigurationChangeListener_ensureNoCallback()244 public void testAddRemoveConfigurationChangeListener_ensureNoCallback() { 245 mController.addConfigurationChangeListener(mConfigChangeListener); 246 mController.removeConfigurationChangeListener(mConfigChangeListener); 247 248 Configuration newConfig = getConfigurationCopy(); 249 newConfig.densityDpi = 200; 250 mController.onConfigurationChanged(newConfig); 251 assertTrue(mConfigChangeListener.configChanges == 0); 252 } 253 254 @Test testMultipleConfigurationChangeListeners()255 public void testMultipleConfigurationChangeListeners() { 256 TestConfigurationChangeListener listener2 = new TestConfigurationChangeListener(); 257 mController.addConfigurationChangeListener(mConfigChangeListener); 258 mController.addConfigurationChangeListener(listener2); 259 260 Configuration newConfig = getConfigurationCopy(); 261 newConfig.densityDpi = 200; 262 mController.onConfigurationChanged(newConfig); 263 assertTrue(mConfigChangeListener.configChanges == 1); 264 assertTrue(listener2.configChanges == 1); 265 } 266 267 @Test testRemoveListenerDuringCallback()268 public void testRemoveListenerDuringCallback() { 269 TestConfigurationChangeListener badListener = new TestConfigurationChangeListener() { 270 @Override 271 public void onConfigurationChanged(Configuration newConfiguration) { 272 mController.removeConfigurationChangeListener(this); 273 } 274 }; 275 mController.addConfigurationChangeListener(badListener); 276 mController.addConfigurationChangeListener(mConfigChangeListener); 277 278 // Ensure we don't fail just because a listener was removed mid-callback 279 Configuration newConfig = getConfigurationCopy(); 280 newConfig.densityDpi = 200; 281 mController.onConfigurationChanged(newConfig); 282 } 283 284 @Test testDensityChangeCallback()285 public void testDensityChangeCallback() { 286 mController.addConfigurationChangeListener(mConfigChangeListener); 287 288 Configuration newConfig = getConfigurationCopy(); 289 newConfig.densityDpi = 200; 290 mController.onConfigurationChanged(newConfig); 291 assertTrue(mConfigChangeListener.configChanges == 1); 292 assertTrue(mConfigChangeListener.densityChanges == 1); 293 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 294 assertTrue(mConfigChangeListener.themeChanges == 0); 295 assertTrue(mConfigChangeListener.localeChanges == 0); 296 } 297 298 @Test testFontScaleChangeCallback()299 public void testFontScaleChangeCallback() { 300 mController.addConfigurationChangeListener(mConfigChangeListener); 301 302 Configuration newConfig = getConfigurationCopy(); 303 newConfig.fontScale = 2; 304 mController.onConfigurationChanged(newConfig); 305 assertTrue(mConfigChangeListener.configChanges == 1); 306 assertTrue(mConfigChangeListener.densityChanges == 1); 307 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 308 assertTrue(mConfigChangeListener.themeChanges == 0); 309 assertTrue(mConfigChangeListener.localeChanges == 0); 310 } 311 312 @Test testSmallestWidthChangeCallback()313 public void testSmallestWidthChangeCallback() { 314 mController.addConfigurationChangeListener(mConfigChangeListener); 315 316 Configuration newConfig = getConfigurationCopy(); 317 newConfig.smallestScreenWidthDp = 100; 318 mController.onConfigurationChanged(newConfig); 319 assertTrue(mConfigChangeListener.configChanges == 1); 320 assertTrue(mConfigChangeListener.densityChanges == 0); 321 assertTrue(mConfigChangeListener.smallestWidthChanges == 1); 322 assertTrue(mConfigChangeListener.themeChanges == 0); 323 assertTrue(mConfigChangeListener.localeChanges == 0); 324 } 325 326 @Test testThemeChangeCallback()327 public void testThemeChangeCallback() { 328 mController.addConfigurationChangeListener(mConfigChangeListener); 329 330 Configuration newConfig = getConfigurationCopy(); 331 newConfig.assetsSeq++; 332 mController.onConfigurationChanged(newConfig); 333 assertTrue(mConfigChangeListener.configChanges == 1); 334 assertTrue(mConfigChangeListener.densityChanges == 0); 335 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 336 assertTrue(mConfigChangeListener.themeChanges == 1); 337 assertTrue(mConfigChangeListener.localeChanges == 0); 338 } 339 340 @Test testNightModeChangeCallback()341 public void testNightModeChangeCallback() { 342 mController.addConfigurationChangeListener(mConfigChangeListener); 343 344 Configuration newConfig = getConfigurationCopy(); 345 newConfig.uiMode = Configuration.UI_MODE_NIGHT_YES; 346 mController.onConfigurationChanged(newConfig); 347 assertTrue(mConfigChangeListener.configChanges == 1); 348 assertTrue(mConfigChangeListener.densityChanges == 0); 349 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 350 assertTrue(mConfigChangeListener.themeChanges == 1); 351 assertTrue(mConfigChangeListener.localeChanges == 0); 352 } 353 354 @Test testLocaleChangeCallback()355 public void testLocaleChangeCallback() { 356 mController.addConfigurationChangeListener(mConfigChangeListener); 357 358 Configuration newConfig = getConfigurationCopy(); 359 // Just change the locales to be different 360 if (newConfig.locale == Locale.CANADA) { 361 newConfig.locale = Locale.US; 362 } else { 363 newConfig.locale = Locale.CANADA; 364 } 365 mController.onConfigurationChanged(newConfig); 366 assertTrue(mConfigChangeListener.configChanges == 1); 367 assertTrue(mConfigChangeListener.densityChanges == 0); 368 assertTrue(mConfigChangeListener.smallestWidthChanges == 0); 369 assertTrue(mConfigChangeListener.themeChanges == 0); 370 assertTrue(mConfigChangeListener.localeChanges == 1); 371 } 372 getConfigurationCopy()373 private Configuration getConfigurationCopy() { 374 final Configuration c = new Configuration(InstrumentationRegistry.getInstrumentation() 375 .getTargetContext().getResources().getConfiguration()); 376 // In tests this might be undefined so make sure it's valid 377 c.assetsSeq = 1; 378 return c; 379 } 380 381 private class TestConfigurationChangeListener implements ConfigurationChangeListener { 382 // Counts of number of times each of the callbacks are called 383 public int configChanges; 384 public int densityChanges; 385 public int smallestWidthChanges; 386 public int themeChanges; 387 public int localeChanges; 388 389 @Override onConfigurationChanged(Configuration newConfiguration)390 public void onConfigurationChanged(Configuration newConfiguration) { 391 configChanges++; 392 } 393 394 @Override onDensityOrFontScaleChanged()395 public void onDensityOrFontScaleChanged() { 396 densityChanges++; 397 } 398 399 @Override onSmallestScreenWidthChanged()400 public void onSmallestScreenWidthChanged() { 401 smallestWidthChanges++; 402 } 403 404 @Override onThemeChanged()405 public void onThemeChanged() { 406 themeChanges++; 407 } 408 409 @Override onLocaleOrLayoutDirectionChanged()410 public void onLocaleOrLayoutDirectionChanged() { 411 localeChanges++; 412 } 413 } 414 415 private class TestKeyguardChangeListener implements KeyguardChangeListener { 416 // Counts of number of times each of the callbacks are called 417 public int visibilityChanged; 418 public boolean lastVisibility; 419 public boolean lastOccluded; 420 public boolean lastAnimatingDismiss; 421 public int dismissAnimationFinished; 422 423 @Override onKeyguardVisibilityChanged(boolean visible, boolean occluded, boolean animatingDismiss)424 public void onKeyguardVisibilityChanged(boolean visible, boolean occluded, 425 boolean animatingDismiss) { 426 lastVisibility = visible; 427 lastOccluded = occluded; 428 lastAnimatingDismiss = animatingDismiss; 429 visibilityChanged++; 430 } 431 432 @Override onKeyguardDismissAnimationFinished()433 public void onKeyguardDismissAnimationFinished() { 434 dismissAnimationFinished++; 435 } 436 } 437 438 private class TestUserChangeListener implements UserChangeListener { 439 // Counts of number of times each of the callbacks are called 440 public int userChanged; 441 public int lastUserId; 442 public Context lastUserContext; 443 public int userProfilesChanged; 444 public List<? extends UserInfo> lastUserProfiles; 445 446 447 @Override onUserChanged(int newUserId, @NonNull Context userContext)448 public void onUserChanged(int newUserId, @NonNull Context userContext) { 449 userChanged++; 450 lastUserId = newUserId; 451 lastUserContext = userContext; 452 } 453 454 @Override onUserProfilesChanged(@onNull List<UserInfo> profiles)455 public void onUserProfilesChanged(@NonNull List<UserInfo> profiles) { 456 userProfilesChanged++; 457 lastUserProfiles = profiles; 458 } 459 } 460 } 461