1 /* 2 * Copyright (C) 2017 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.systemui.statusbar.phone; 18 19 import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertNull; 24 import static org.junit.Assert.assertTrue; 25 import static org.junit.Assert.fail; 26 import static org.mockito.ArgumentMatchers.any; 27 import static org.mockito.ArgumentMatchers.isNotNull; 28 import static org.mockito.ArgumentMatchers.isNull; 29 import static org.mockito.Mockito.doReturn; 30 import static org.mockito.Mockito.inOrder; 31 import static org.mockito.Mockito.mock; 32 import static org.mockito.Mockito.never; 33 import static org.mockito.Mockito.times; 34 import static org.mockito.Mockito.verify; 35 import static org.mockito.Mockito.when; 36 37 import android.content.ComponentName; 38 import android.content.Context; 39 import android.hardware.display.ColorDisplayManager; 40 import android.hardware.display.NightDisplayListener; 41 import android.os.Handler; 42 import android.os.UserHandle; 43 import android.testing.AndroidTestingRunner; 44 import android.testing.TestableLooper; 45 import android.testing.TestableLooper.RunWithLooper; 46 47 import androidx.test.filters.SmallTest; 48 49 import com.android.systemui.R; 50 import com.android.systemui.SysuiTestCase; 51 import com.android.systemui.qs.AutoAddTracker; 52 import com.android.systemui.qs.QSTileHost; 53 import com.android.systemui.qs.ReduceBrightColorsController; 54 import com.android.systemui.qs.SecureSetting; 55 import com.android.systemui.statusbar.policy.CastController; 56 import com.android.systemui.statusbar.policy.CastController.CastDevice; 57 import com.android.systemui.statusbar.policy.DataSaverController; 58 import com.android.systemui.statusbar.policy.DeviceControlsController; 59 import com.android.systemui.statusbar.policy.HotspotController; 60 import com.android.systemui.statusbar.policy.WalletController; 61 import com.android.systemui.util.settings.FakeSettings; 62 import com.android.systemui.util.settings.SecureSettings; 63 64 import org.junit.After; 65 import org.junit.Before; 66 import org.junit.Test; 67 import org.junit.runner.RunWith; 68 import org.mockito.Answers; 69 import org.mockito.InOrder; 70 import org.mockito.Mock; 71 import org.mockito.MockitoAnnotations; 72 73 import java.util.Collections; 74 import java.util.List; 75 76 import javax.inject.Named; 77 78 @RunWith(AndroidTestingRunner.class) 79 @RunWithLooper 80 @SmallTest 81 public class AutoTileManagerTest extends SysuiTestCase { 82 83 private static final String TEST_SETTING = "setting"; 84 private static final String TEST_SPEC = "spec"; 85 private static final String TEST_SETTING_COMPONENT = "setting_component"; 86 private static final String TEST_COMPONENT = "test_pkg/test_cls"; 87 private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")"; 88 private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR; 89 90 private static final int USER = 0; 91 92 @Mock private QSTileHost mQsTileHost; 93 @Mock private AutoAddTracker mAutoAddTracker; 94 @Mock private CastController mCastController; 95 @Mock private HotspotController mHotspotController; 96 @Mock private DataSaverController mDataSaverController; 97 @Mock private ManagedProfileController mManagedProfileController; 98 @Mock private NightDisplayListener mNightDisplayListener; 99 @Mock private ReduceBrightColorsController mReduceBrightColorsController; 100 @Mock private DeviceControlsController mDeviceControlsController; 101 @Mock private WalletController mWalletController; 102 @Mock(answer = Answers.RETURNS_SELF) 103 private AutoAddTracker.Builder mAutoAddTrackerBuilder; 104 @Mock private Context mUserContext; 105 private final boolean mIsReduceBrightColorsAvailable = true; 106 107 private AutoTileManager mAutoTileManager; 108 private SecureSettings mSecureSettings; 109 110 @Before setUp()111 public void setUp() throws Exception { 112 MockitoAnnotations.initMocks(this); 113 mSecureSettings = new FakeSettings(); 114 115 mContext.getOrCreateTestableResources().addOverride( 116 R.array.config_quickSettingsAutoAdd, 117 new String[] { 118 TEST_SETTING + SEPARATOR + TEST_SPEC, 119 TEST_SETTING_COMPONENT + SEPARATOR + TEST_CUSTOM_SPEC 120 } 121 ); 122 mContext.getOrCreateTestableResources().addOverride( 123 com.android.internal.R.bool.config_nightDisplayAvailable, true); 124 125 when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker); 126 when(mQsTileHost.getUserContext()).thenReturn(mUserContext); 127 when(mUserContext.getUser()).thenReturn(UserHandle.of(USER)); 128 129 mAutoTileManager = createAutoTileManager(mContext); 130 mAutoTileManager.init(); 131 } 132 133 @After tearDown()134 public void tearDown() { 135 mAutoTileManager.destroy(); 136 } 137 createAutoTileManager( Context context, AutoAddTracker.Builder autoAddTrackerBuilder, HotspotController hotspotController, DataSaverController dataSaverController, ManagedProfileController managedProfileController, NightDisplayListener nightDisplayListener, CastController castController, ReduceBrightColorsController reduceBrightColorsController, DeviceControlsController deviceControlsController, WalletController walletController, @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable)138 private AutoTileManager createAutoTileManager( 139 Context context, 140 AutoAddTracker.Builder autoAddTrackerBuilder, 141 HotspotController hotspotController, 142 DataSaverController dataSaverController, 143 ManagedProfileController managedProfileController, 144 NightDisplayListener nightDisplayListener, 145 CastController castController, 146 ReduceBrightColorsController reduceBrightColorsController, 147 DeviceControlsController deviceControlsController, 148 WalletController walletController, 149 @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) { 150 return new AutoTileManager(context, autoAddTrackerBuilder, mQsTileHost, 151 Handler.createAsync(TestableLooper.get(this).getLooper()), 152 mSecureSettings, 153 hotspotController, 154 dataSaverController, 155 managedProfileController, 156 nightDisplayListener, 157 castController, 158 reduceBrightColorsController, 159 deviceControlsController, 160 walletController, 161 isReduceBrightColorsAvailable); 162 } 163 createAutoTileManager(Context context)164 private AutoTileManager createAutoTileManager(Context context) { 165 return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController, 166 mDataSaverController, mManagedProfileController, mNightDisplayListener, 167 mCastController, mReduceBrightColorsController, mDeviceControlsController, 168 mWalletController, mIsReduceBrightColorsAvailable); 169 } 170 171 @Test testCreatedAutoTileManagerIsNotInitialized()172 public void testCreatedAutoTileManagerIsNotInitialized() { 173 AutoAddTracker.Builder builder = mock(AutoAddTracker.Builder.class, Answers.RETURNS_SELF); 174 AutoAddTracker tracker = mock(AutoAddTracker.class); 175 when(builder.build()).thenReturn(tracker); 176 HotspotController hC = mock(HotspotController.class); 177 DataSaverController dSC = mock(DataSaverController.class); 178 ManagedProfileController mPC = mock(ManagedProfileController.class); 179 NightDisplayListener nDS = mock(NightDisplayListener.class); 180 CastController cC = mock(CastController.class); 181 ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class); 182 DeviceControlsController dCC = mock(DeviceControlsController.class); 183 WalletController wC = mock(WalletController.class); 184 185 AutoTileManager manager = 186 createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDS, cC, rBC, 187 dCC, wC, true); 188 189 verify(tracker, never()).initialize(); 190 verify(hC, never()).addCallback(any()); 191 verify(dSC, never()).addCallback(any()); 192 verify(mPC, never()).addCallback(any()); 193 verify(nDS, never()).setCallback(any()); 194 verify(cC, never()).addCallback(any()); 195 verify(rBC, never()).addCallback(any()); 196 verify(dCC, never()).setCallback(any()); 197 verify(wC, never()).getWalletPosition(); 198 assertNull(manager.getSecureSettingForKey(TEST_SETTING)); 199 assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT)); 200 } 201 202 @Test testChangeUserWhenNotInitializedThrows()203 public void testChangeUserWhenNotInitializedThrows() { 204 AutoTileManager manager = createAutoTileManager(mock(Context.class)); 205 206 try { 207 manager.changeUser(UserHandle.of(USER + 1)); 208 fail(); 209 } catch (Exception e) { 210 // This should throw and take this path 211 } 212 } 213 214 @Test testChangeUserCallbacksStoppedAndStarted()215 public void testChangeUserCallbacksStoppedAndStarted() throws Exception { 216 TestableLooper.get(this).runWithLooper(() -> 217 mAutoTileManager.changeUser(UserHandle.of(USER + 1)) 218 ); 219 220 InOrder inOrderHotspot = inOrder(mHotspotController); 221 inOrderHotspot.verify(mHotspotController).removeCallback(any()); 222 inOrderHotspot.verify(mHotspotController).addCallback(any()); 223 224 InOrder inOrderDataSaver = inOrder(mDataSaverController); 225 inOrderDataSaver.verify(mDataSaverController).removeCallback(any()); 226 inOrderDataSaver.verify(mDataSaverController).addCallback(any()); 227 228 InOrder inOrderManagedProfile = inOrder(mManagedProfileController); 229 inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any()); 230 inOrderManagedProfile.verify(mManagedProfileController).addCallback(any()); 231 232 if (ColorDisplayManager.isNightDisplayAvailable(mContext)) { 233 InOrder inOrderNightDisplay = inOrder(mNightDisplayListener); 234 inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull()); 235 inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull()); 236 } 237 238 InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController); 239 inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any()); 240 inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any()); 241 242 InOrder inOrderCast = inOrder(mCastController); 243 inOrderCast.verify(mCastController).removeCallback(any()); 244 inOrderCast.verify(mCastController).addCallback(any()); 245 246 InOrder inOrderDevices = inOrder(mDeviceControlsController); 247 inOrderDevices.verify(mDeviceControlsController).removeCallback(); 248 inOrderDevices.verify(mDeviceControlsController).setCallback(any()); 249 250 verify(mWalletController, times(2)).getWalletPosition(); 251 252 SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); 253 assertEquals(USER + 1, setting.getCurrentUser()); 254 assertTrue(setting.isListening()); 255 } 256 257 @Test testChangeUserSomeCallbacksNotAdded()258 public void testChangeUserSomeCallbacksNotAdded() throws Exception { 259 when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true); 260 when(mAutoAddTracker.isAdded("work")).thenReturn(true); 261 when(mAutoAddTracker.isAdded("cast")).thenReturn(true); 262 when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true); 263 264 TestableLooper.get(this).runWithLooper(() -> 265 mAutoTileManager.changeUser(UserHandle.of(USER + 1)) 266 ); 267 268 verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1)); 269 270 InOrder inOrderHotspot = inOrder(mHotspotController); 271 inOrderHotspot.verify(mHotspotController).removeCallback(any()); 272 inOrderHotspot.verify(mHotspotController, never()).addCallback(any()); 273 274 InOrder inOrderDataSaver = inOrder(mDataSaverController); 275 inOrderDataSaver.verify(mDataSaverController).removeCallback(any()); 276 inOrderDataSaver.verify(mDataSaverController).addCallback(any()); 277 278 InOrder inOrderManagedProfile = inOrder(mManagedProfileController); 279 inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any()); 280 inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any()); 281 282 if (ColorDisplayManager.isNightDisplayAvailable(mContext)) { 283 InOrder inOrderNightDisplay = inOrder(mNightDisplayListener); 284 inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull()); 285 inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull()); 286 } 287 288 InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController); 289 inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any()); 290 inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any()); 291 292 InOrder inOrderCast = inOrder(mCastController); 293 inOrderCast.verify(mCastController).removeCallback(any()); 294 inOrderCast.verify(mCastController, never()).addCallback(any()); 295 296 InOrder inOrderDevices = inOrder(mDeviceControlsController); 297 inOrderDevices.verify(mDeviceControlsController).removeCallback(); 298 inOrderDevices.verify(mDeviceControlsController).setCallback(any()); 299 300 verify(mWalletController, times(2)).getWalletPosition(); 301 302 SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); 303 assertEquals(USER + 1, setting.getCurrentUser()); 304 assertFalse(setting.isListening()); 305 } 306 307 @Test testGetCurrentUserId()308 public void testGetCurrentUserId() throws Exception { 309 assertEquals(USER, mAutoTileManager.getCurrentUserId()); 310 311 TestableLooper.get(this).runWithLooper(() -> 312 mAutoTileManager.changeUser(UserHandle.of(USER + 100)) 313 ); 314 315 assertEquals(USER + 100, mAutoTileManager.getCurrentUserId()); 316 } 317 318 @Test nightTileAdded_whenActivated()319 public void nightTileAdded_whenActivated() { 320 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 321 return; 322 } 323 mAutoTileManager.mNightDisplayCallback.onActivated(true); 324 verify(mQsTileHost).addTile("night"); 325 } 326 327 @Test nightTileNotAdded_whenDeactivated()328 public void nightTileNotAdded_whenDeactivated() { 329 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 330 return; 331 } 332 mAutoTileManager.mNightDisplayCallback.onActivated(false); 333 verify(mQsTileHost, never()).addTile("night"); 334 } 335 336 @Test nightTileAdded_whenNightModeTwilight()337 public void nightTileAdded_whenNightModeTwilight() { 338 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 339 return; 340 } 341 mAutoTileManager.mNightDisplayCallback.onAutoModeChanged( 342 ColorDisplayManager.AUTO_MODE_TWILIGHT); 343 verify(mQsTileHost).addTile("night"); 344 } 345 346 @Test nightTileAdded_whenNightModeCustom()347 public void nightTileAdded_whenNightModeCustom() { 348 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 349 return; 350 } 351 mAutoTileManager.mNightDisplayCallback.onAutoModeChanged( 352 ColorDisplayManager.AUTO_MODE_CUSTOM_TIME); 353 verify(mQsTileHost).addTile("night"); 354 } 355 356 @Test nightTileNotAdded_whenNightModeDisabled()357 public void nightTileNotAdded_whenNightModeDisabled() { 358 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 359 return; 360 } 361 mAutoTileManager.mNightDisplayCallback.onAutoModeChanged( 362 ColorDisplayManager.AUTO_MODE_DISABLED); 363 verify(mQsTileHost, never()).addTile("night"); 364 } 365 366 @Test reduceBrightColorsTileAdded_whenActivated()367 public void reduceBrightColorsTileAdded_whenActivated() { 368 mAutoTileManager.mReduceBrightColorsCallback.onActivated(true); 369 verify(mQsTileHost).addTile("reduce_brightness"); 370 } 371 372 @Test reduceBrightColorsTileNotAdded_whenDeactivated()373 public void reduceBrightColorsTileNotAdded_whenDeactivated() { 374 mAutoTileManager.mReduceBrightColorsCallback.onActivated(false); 375 verify(mQsTileHost, never()).addTile("reduce_brightness"); 376 } 377 buildFakeCastDevice(boolean isCasting)378 private static List<CastDevice> buildFakeCastDevice(boolean isCasting) { 379 CastDevice cd = new CastDevice(); 380 cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED; 381 return Collections.singletonList(cd); 382 } 383 384 @Test castTileAdded_whenDeviceIsCasting()385 public void castTileAdded_whenDeviceIsCasting() { 386 doReturn(buildFakeCastDevice(true)).when(mCastController).getCastDevices(); 387 mAutoTileManager.mCastCallback.onCastDevicesChanged(); 388 verify(mQsTileHost).addTile("cast"); 389 } 390 391 @Test castTileNotAdded_whenDeviceIsNotCasting()392 public void castTileNotAdded_whenDeviceIsNotCasting() { 393 doReturn(buildFakeCastDevice(false)).when(mCastController).getCastDevices(); 394 mAutoTileManager.mCastCallback.onCastDevicesChanged(); 395 verify(mQsTileHost, never()).addTile("cast"); 396 } 397 398 @Test testSettingTileAdded_onChanged()399 public void testSettingTileAdded_onChanged() { 400 changeValue(TEST_SETTING, 1); 401 verify(mAutoAddTracker).setTileAdded(TEST_SPEC); 402 verify(mQsTileHost).addTile(TEST_SPEC); 403 } 404 405 @Test testSettingTileAddedComponentAtEnd_onChanged()406 public void testSettingTileAddedComponentAtEnd_onChanged() { 407 changeValue(TEST_SETTING_COMPONENT, 1); 408 verify(mAutoAddTracker).setTileAdded(TEST_CUSTOM_SPEC); 409 verify(mQsTileHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT) 410 , /* end */ true); 411 } 412 413 @Test testSettingTileAdded_onlyOnce()414 public void testSettingTileAdded_onlyOnce() { 415 changeValue(TEST_SETTING, 1); 416 changeValue(TEST_SETTING, 2); 417 verify(mAutoAddTracker).setTileAdded(TEST_SPEC); 418 verify(mQsTileHost).addTile(TEST_SPEC); 419 } 420 421 @Test testSettingTileNotAdded_onChangedTo0()422 public void testSettingTileNotAdded_onChangedTo0() { 423 changeValue(TEST_SETTING, 0); 424 verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC); 425 verify(mQsTileHost, never()).addTile(TEST_SPEC); 426 } 427 428 @Test testSettingTileNotAdded_ifPreviouslyAdded()429 public void testSettingTileNotAdded_ifPreviouslyAdded() { 430 when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true); 431 432 changeValue(TEST_SETTING, 1); 433 verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC); 434 verify(mQsTileHost, never()).addTile(TEST_SPEC); 435 } 436 437 @Test testEmptyArray_doesNotCrash()438 public void testEmptyArray_doesNotCrash() { 439 mContext.getOrCreateTestableResources().addOverride( 440 R.array.config_quickSettingsAutoAdd, new String[0]); 441 createAutoTileManager(mContext).destroy(); 442 } 443 444 @Test testMissingConfig_doesNotCrash()445 public void testMissingConfig_doesNotCrash() { 446 mContext.getOrCreateTestableResources().addOverride( 447 R.array.config_quickSettingsAutoAdd, null); 448 createAutoTileManager(mContext).destroy(); 449 } 450 451 // Will only notify if it's listening changeValue(String key, int value)452 private void changeValue(String key, int value) { 453 mSecureSettings.putIntForUser(key, value, USER); 454 TestableLooper.get(this).processAllMessages(); 455 } 456 } 457