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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.ArgumentMatchers.anyBoolean; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.ArgumentMatchers.isNotNull; 26 import static org.mockito.ArgumentMatchers.isNull; 27 import static org.mockito.Mockito.doNothing; 28 import static org.mockito.Mockito.doReturn; 29 import static org.mockito.Mockito.inOrder; 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.spy; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.when; 34 35 import android.content.ComponentName; 36 import android.content.ContentResolver; 37 import android.content.Context; 38 import android.content.ContextWrapper; 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.provider.Settings; 44 import android.testing.AndroidTestingRunner; 45 import android.testing.TestableContentResolver; 46 import android.testing.TestableContext; 47 import android.testing.TestableLooper; 48 import android.testing.TestableLooper.RunWithLooper; 49 50 import androidx.test.filters.SmallTest; 51 52 import com.android.systemui.R; 53 import com.android.systemui.SysuiTestCase; 54 import com.android.systemui.qs.AutoAddTracker; 55 import com.android.systemui.qs.QSTileHost; 56 import com.android.systemui.qs.SecureSetting; 57 import com.android.systemui.statusbar.phone.AutoTileManagerTest.MyContextWrapper; 58 import com.android.systemui.statusbar.policy.CastController; 59 import com.android.systemui.statusbar.policy.CastController.CastDevice; 60 import com.android.systemui.statusbar.policy.DataSaverController; 61 import com.android.systemui.statusbar.policy.HotspotController; 62 63 import org.junit.After; 64 import org.junit.Before; 65 import org.junit.Test; 66 import org.junit.runner.RunWith; 67 import org.mockito.Answers; 68 import org.mockito.InOrder; 69 import org.mockito.Mock; 70 import org.mockito.MockitoAnnotations; 71 72 import java.util.Collections; 73 import java.util.List; 74 75 @RunWith(AndroidTestingRunner.class) 76 @RunWithLooper 77 @SmallTest 78 public class AutoTileManagerTest extends SysuiTestCase { 79 80 private static final String TEST_SETTING = "setting"; 81 private static final String TEST_SPEC = "spec"; 82 private static final String TEST_SETTING_COMPONENT = "setting_component"; 83 private static final String TEST_COMPONENT = "test_pkg/test_cls"; 84 private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")"; 85 private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR; 86 87 private static final int USER = 0; 88 89 @Mock private QSTileHost mQsTileHost; 90 @Mock private AutoAddTracker mAutoAddTracker; 91 @Mock private CastController mCastController; 92 @Mock private HotspotController mHotspotController; 93 @Mock private DataSaverController mDataSaverController; 94 @Mock private ManagedProfileController mManagedProfileController; 95 @Mock private NightDisplayListener mNightDisplayListener; 96 @Mock(answer = Answers.RETURNS_SELF) 97 private AutoAddTracker.Builder mAutoAddTrackerBuilder; 98 @Mock private Context mUserContext; 99 100 private AutoTileManager mAutoTileManager; 101 102 @Before setUp()103 public void setUp() throws Exception { 104 MockitoAnnotations.initMocks(this); 105 106 mContext.getOrCreateTestableResources().addOverride( 107 R.array.config_quickSettingsAutoAdd, 108 new String[] { 109 TEST_SETTING + SEPARATOR + TEST_SPEC, 110 TEST_SETTING_COMPONENT + SEPARATOR + TEST_CUSTOM_SPEC 111 } 112 ); 113 114 when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker); 115 when(mQsTileHost.getUserContext()).thenReturn(mUserContext); 116 when(mUserContext.getUser()).thenReturn(UserHandle.of(USER)); 117 118 mAutoTileManager = createAutoTileManager(new 119 MyContextWrapper(mContext)); 120 } 121 122 @After tearDown()123 public void tearDown() { 124 mAutoTileManager.destroy(); 125 } 126 createAutoTileManager(Context context)127 private AutoTileManager createAutoTileManager(Context context) { 128 return new AutoTileManager(context, mAutoAddTrackerBuilder, mQsTileHost, 129 Handler.createAsync(TestableLooper.get(this).getLooper()), 130 mHotspotController, 131 mDataSaverController, 132 mManagedProfileController, 133 mNightDisplayListener, 134 mCastController); 135 } 136 137 @Test testChangeUserCallbacksStoppedAndStarted()138 public void testChangeUserCallbacksStoppedAndStarted() throws Exception { 139 TestableLooper.get(this).runWithLooper(() -> 140 mAutoTileManager.changeUser(UserHandle.of(USER + 1)) 141 ); 142 143 InOrder inOrderHotspot = inOrder(mHotspotController); 144 inOrderHotspot.verify(mHotspotController).removeCallback(any()); 145 inOrderHotspot.verify(mHotspotController).addCallback(any()); 146 147 InOrder inOrderDataSaver = inOrder(mDataSaverController); 148 inOrderDataSaver.verify(mDataSaverController).removeCallback(any()); 149 inOrderDataSaver.verify(mDataSaverController).addCallback(any()); 150 151 InOrder inOrderManagedProfile = inOrder(mManagedProfileController); 152 inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any()); 153 inOrderManagedProfile.verify(mManagedProfileController).addCallback(any()); 154 155 if (ColorDisplayManager.isNightDisplayAvailable(mContext)) { 156 InOrder inOrderNightDisplay = inOrder(mNightDisplayListener); 157 inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull()); 158 inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull()); 159 } 160 161 InOrder inOrderCast = inOrder(mCastController); 162 inOrderCast.verify(mCastController).removeCallback(any()); 163 inOrderCast.verify(mCastController).addCallback(any()); 164 165 SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); 166 assertEquals(USER + 1, setting.getCurrentUser()); 167 assertTrue(setting.isListening()); 168 } 169 170 @Test testChangeUserSomeCallbacksNotAdded()171 public void testChangeUserSomeCallbacksNotAdded() throws Exception { 172 when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true); 173 when(mAutoAddTracker.isAdded("work")).thenReturn(true); 174 when(mAutoAddTracker.isAdded("cast")).thenReturn(true); 175 when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true); 176 177 TestableLooper.get(this).runWithLooper(() -> 178 mAutoTileManager.changeUser(UserHandle.of(USER + 1)) 179 ); 180 181 verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1)); 182 183 InOrder inOrderHotspot = inOrder(mHotspotController); 184 inOrderHotspot.verify(mHotspotController).removeCallback(any()); 185 inOrderHotspot.verify(mHotspotController, never()).addCallback(any()); 186 187 InOrder inOrderDataSaver = inOrder(mDataSaverController); 188 inOrderDataSaver.verify(mDataSaverController).removeCallback(any()); 189 inOrderDataSaver.verify(mDataSaverController).addCallback(any()); 190 191 InOrder inOrderManagedProfile = inOrder(mManagedProfileController); 192 inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any()); 193 inOrderManagedProfile.verify(mManagedProfileController, never()).addCallback(any()); 194 195 if (ColorDisplayManager.isNightDisplayAvailable(mContext)) { 196 InOrder inOrderNightDisplay = inOrder(mNightDisplayListener); 197 inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull()); 198 inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull()); 199 } 200 201 InOrder inOrderCast = inOrder(mCastController); 202 inOrderCast.verify(mCastController).removeCallback(any()); 203 inOrderCast.verify(mCastController, never()).addCallback(any()); 204 205 SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING); 206 assertEquals(USER + 1, setting.getCurrentUser()); 207 assertFalse(setting.isListening()); 208 } 209 210 @Test testGetCurrentUserId()211 public void testGetCurrentUserId() throws Exception { 212 assertEquals(USER, mAutoTileManager.getCurrentUserId()); 213 214 TestableLooper.get(this).runWithLooper(() -> 215 mAutoTileManager.changeUser(UserHandle.of(USER + 100)) 216 ); 217 218 assertEquals(USER + 100, mAutoTileManager.getCurrentUserId()); 219 } 220 221 @Test nightTileAdded_whenActivated()222 public void nightTileAdded_whenActivated() { 223 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 224 return; 225 } 226 mAutoTileManager.mNightDisplayCallback.onActivated(true); 227 verify(mQsTileHost).addTile("night"); 228 } 229 230 @Test nightTileNotAdded_whenDeactivated()231 public void nightTileNotAdded_whenDeactivated() { 232 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 233 return; 234 } 235 mAutoTileManager.mNightDisplayCallback.onActivated(false); 236 verify(mQsTileHost, never()).addTile("night"); 237 } 238 239 @Test nightTileAdded_whenNightModeTwilight()240 public void nightTileAdded_whenNightModeTwilight() { 241 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 242 return; 243 } 244 mAutoTileManager.mNightDisplayCallback.onAutoModeChanged( 245 ColorDisplayManager.AUTO_MODE_TWILIGHT); 246 verify(mQsTileHost).addTile("night"); 247 } 248 249 @Test nightTileAdded_whenNightModeCustom()250 public void nightTileAdded_whenNightModeCustom() { 251 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 252 return; 253 } 254 mAutoTileManager.mNightDisplayCallback.onAutoModeChanged( 255 ColorDisplayManager.AUTO_MODE_CUSTOM_TIME); 256 verify(mQsTileHost).addTile("night"); 257 } 258 259 @Test nightTileNotAdded_whenNightModeDisabled()260 public void nightTileNotAdded_whenNightModeDisabled() { 261 if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) { 262 return; 263 } 264 mAutoTileManager.mNightDisplayCallback.onAutoModeChanged( 265 ColorDisplayManager.AUTO_MODE_DISABLED); 266 verify(mQsTileHost, never()).addTile("night"); 267 } 268 buildFakeCastDevice(boolean isCasting)269 private static List<CastDevice> buildFakeCastDevice(boolean isCasting) { 270 CastDevice cd = new CastDevice(); 271 cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED; 272 return Collections.singletonList(cd); 273 } 274 275 @Test castTileAdded_whenDeviceIsCasting()276 public void castTileAdded_whenDeviceIsCasting() { 277 doReturn(buildFakeCastDevice(true)).when(mCastController).getCastDevices(); 278 mAutoTileManager.mCastCallback.onCastDevicesChanged(); 279 verify(mQsTileHost).addTile("cast"); 280 } 281 282 @Test castTileNotAdded_whenDeviceIsNotCasting()283 public void castTileNotAdded_whenDeviceIsNotCasting() { 284 doReturn(buildFakeCastDevice(false)).when(mCastController).getCastDevices(); 285 mAutoTileManager.mCastCallback.onCastDevicesChanged(); 286 verify(mQsTileHost, never()).addTile("cast"); 287 } 288 289 @Test testSettingTileAdded_onChanged()290 public void testSettingTileAdded_onChanged() { 291 changeValue(TEST_SETTING, 1); 292 waitForIdleSync(); 293 verify(mAutoAddTracker).setTileAdded(TEST_SPEC); 294 verify(mQsTileHost).addTile(TEST_SPEC); 295 } 296 297 @Test testSettingTileAddedComponentAtEnd_onChanged()298 public void testSettingTileAddedComponentAtEnd_onChanged() { 299 changeValue(TEST_SETTING_COMPONENT, 1); 300 waitForIdleSync(); 301 verify(mAutoAddTracker).setTileAdded(TEST_CUSTOM_SPEC); 302 verify(mQsTileHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT) 303 , /* end */ true); 304 } 305 306 @Test testSettingTileAdded_onlyOnce()307 public void testSettingTileAdded_onlyOnce() { 308 changeValue(TEST_SETTING, 1); 309 waitForIdleSync(); 310 TestableLooper.get(this).processAllMessages(); 311 changeValue(TEST_SETTING, 2); 312 waitForIdleSync(); 313 verify(mAutoAddTracker).setTileAdded(TEST_SPEC); 314 verify(mQsTileHost).addTile(TEST_SPEC); 315 } 316 317 @Test testSettingTileNotAdded_onChangedTo0()318 public void testSettingTileNotAdded_onChangedTo0() { 319 changeValue(TEST_SETTING, 0); 320 waitForIdleSync(); 321 verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC); 322 verify(mQsTileHost, never()).addTile(TEST_SPEC); 323 } 324 325 @Test testSettingTileNotAdded_ifPreviouslyAdded()326 public void testSettingTileNotAdded_ifPreviouslyAdded() { 327 when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true); 328 329 changeValue(TEST_SETTING, 1); 330 waitForIdleSync(); 331 verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC); 332 verify(mQsTileHost, never()).addTile(TEST_SPEC); 333 } 334 335 @Test testEmptyArray_doesNotCrash()336 public void testEmptyArray_doesNotCrash() { 337 mContext.getOrCreateTestableResources().addOverride( 338 R.array.config_quickSettingsAutoAdd, new String[0]); 339 createAutoTileManager(mContext).destroy(); 340 } 341 342 @Test testMissingConfig_doesNotCrash()343 public void testMissingConfig_doesNotCrash() { 344 mContext.getOrCreateTestableResources().addOverride( 345 R.array.config_quickSettingsAutoAdd, null); 346 createAutoTileManager(mContext).destroy(); 347 } 348 349 // Will only notify if it's listening changeValue(String key, int value)350 private void changeValue(String key, int value) { 351 SecureSetting s = mAutoTileManager.getSecureSettingForKey(key); 352 Settings.Secure.putInt(mContext.getContentResolver(), key, value); 353 if (s != null && s.isListening()) { 354 s.onChange(false); 355 } 356 } 357 358 class MyContextWrapper extends ContextWrapper { 359 360 private TestableContentResolver mSpiedTCR; 361 MyContextWrapper(TestableContext context)362 MyContextWrapper(TestableContext context) { 363 super(context); 364 mSpiedTCR = spy(context.getContentResolver()); 365 doNothing().when(mSpiedTCR).registerContentObserver(any(), anyBoolean(), any(), 366 anyInt()); 367 doNothing().when(mSpiedTCR).unregisterContentObserver(any()); 368 } 369 370 @Override getContentResolver()371 public ContentResolver getContentResolver() { 372 return mSpiedTCR; 373 } 374 } 375 } 376