1 /* 2 * Copyright (C) 2021 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.media.systemsounds; 18 19 import static org.mockito.ArgumentMatchers.any; 20 import static org.mockito.Mockito.doReturn; 21 import static org.mockito.Mockito.never; 22 import static org.mockito.Mockito.times; 23 import static org.mockito.Mockito.verify; 24 import static org.mockito.Mockito.when; 25 26 import android.Manifest; 27 import android.app.ActivityManager; 28 import android.app.WindowConfiguration; 29 import android.content.pm.ActivityInfo; 30 import android.content.pm.PackageManager; 31 import android.media.AudioManager; 32 33 import androidx.test.filters.SmallTest; 34 import androidx.test.runner.AndroidJUnit4; 35 36 import com.android.systemui.SysuiTestCase; 37 import com.android.systemui.shared.system.ActivityManagerWrapper; 38 import com.android.systemui.shared.system.TaskStackChangeListener; 39 import com.android.systemui.shared.system.TaskStackChangeListeners; 40 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 import org.mockito.ArgumentCaptor; 45 import org.mockito.Mock; 46 import org.mockito.MockitoAnnotations; 47 48 49 @SmallTest 50 @RunWith(AndroidJUnit4.class) 51 public class HomeSoundEffectControllerTest extends SysuiTestCase { 52 53 private static final String HOME_PACKAGE_NAME = "com.android.apps.home"; 54 private static final String NON_HOME_PACKAGE_NAME = "com.android.apps.not.home"; 55 private static final int HOME_TASK_ID = 0; 56 private static final int NON_HOME_TASK_ID = 1; 57 58 private @Mock AudioManager mAudioManager; 59 private @Mock TaskStackChangeListeners mTaskStackChangeListeners; 60 private @Mock ActivityManagerWrapper mActivityManagerWrapper; 61 private @Mock PackageManager mPackageManager; 62 63 private ActivityManager.RunningTaskInfo mTaskAStandardActivity; 64 private ActivityManager.RunningTaskInfo mTaskAExceptionActivity; 65 private ActivityManager.RunningTaskInfo mHomeTaskHomeActivity; 66 private ActivityManager.RunningTaskInfo mHomeTaskStandardActivity; 67 private ActivityManager.RunningTaskInfo mEmptyTask; 68 private HomeSoundEffectController mController; 69 private TaskStackChangeListener mTaskStackChangeListener; 70 71 @Before setUp()72 public void setUp() throws Exception { 73 MockitoAnnotations.initMocks(this); 74 mTaskAStandardActivity = createRunningTaskInfo(NON_HOME_PACKAGE_NAME, 75 WindowConfiguration.ACTIVITY_TYPE_STANDARD, NON_HOME_TASK_ID, 76 true /* playHomeTransitionSound */); 77 mTaskAExceptionActivity = createRunningTaskInfo(NON_HOME_PACKAGE_NAME, 78 WindowConfiguration.ACTIVITY_TYPE_STANDARD, NON_HOME_TASK_ID, 79 false /* playHomeTransitionSound */); 80 mHomeTaskHomeActivity = createRunningTaskInfo(HOME_PACKAGE_NAME, 81 WindowConfiguration.ACTIVITY_TYPE_HOME, HOME_TASK_ID, 82 true /* playHomeTransitionSound */); 83 mHomeTaskStandardActivity = createRunningTaskInfo(HOME_PACKAGE_NAME, 84 WindowConfiguration.ACTIVITY_TYPE_STANDARD, HOME_TASK_ID, 85 true /* playHomeTransitionSound */); 86 mEmptyTask = new ActivityManager.RunningTaskInfo(); 87 mContext.setMockPackageManager(mPackageManager); 88 when(mPackageManager.checkPermission(Manifest.permission.DISABLE_SYSTEM_SOUND_EFFECTS, 89 NON_HOME_PACKAGE_NAME)).thenReturn(PackageManager.PERMISSION_GRANTED); 90 mController = new HomeSoundEffectController(mContext, mAudioManager, 91 mTaskStackChangeListeners, mActivityManagerWrapper, mPackageManager); 92 } 93 94 @Test testHomeSoundEffectNotPlayedWhenHomeFirstMovesToFront()95 public void testHomeSoundEffectNotPlayedWhenHomeFirstMovesToFront() { 96 // When HomeSoundEffectController is started and the home sound effect is enabled, 97 startController(true /* isHomeSoundEffectEnabled */); 98 99 // And the home task moves to the front, 100 when(mActivityManagerWrapper.getRunningTask()).thenReturn(mHomeTaskHomeActivity); 101 mTaskStackChangeListener.onTaskStackChanged(); 102 103 // Then no home sound effect should be played. 104 verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); 105 } 106 107 /** 108 * Task A (playHomeTransitionSound = true) -> HOME 109 * Expectation: Home sound is played 110 */ 111 @Test testHomeSoundEffectPlayedWhenEnabled()112 public void testHomeSoundEffectPlayedWhenEnabled() { 113 // When HomeSoundEffectController is started and the home sound effect is enabled, 114 startController(true /* isHomeSoundEffectEnabled */); 115 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 116 mTaskAStandardActivity, 117 mHomeTaskHomeActivity); 118 119 // And first a task different from the home task moves to front, 120 mTaskStackChangeListener.onTaskStackChanged(); 121 122 // And the home task moves to the front, 123 mTaskStackChangeListener.onTaskStackChanged(); 124 125 // Then the home sound effect should be played. 126 verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); 127 } 128 129 /** 130 * Task A (playHomeTransitionSound = true) -> HOME -> HOME 131 * Expectation: Home sound is played once after HOME moves to front the first time 132 */ 133 @Test testHomeSoundEffectNotPlayedTwiceInRow()134 public void testHomeSoundEffectNotPlayedTwiceInRow() { 135 // When HomeSoundEffectController is started and the home sound effect is enabled, 136 startController(true /* isHomeSoundEffectEnabled */); 137 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 138 mTaskAStandardActivity, 139 mHomeTaskHomeActivity, 140 mHomeTaskHomeActivity); 141 142 143 // And first a task different from the home task moves to front, 144 mTaskStackChangeListener.onTaskStackChanged(); 145 146 // And the home task moves to the front, 147 mTaskStackChangeListener.onTaskStackChanged(); 148 149 // Then the home sound effect should be played. 150 verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); 151 152 // If the home task moves to front a second time in a row, 153 mTaskStackChangeListener.onTaskStackChanged(); 154 155 // Then no home sound effect should be played. 156 verify(mAudioManager, times(1)).playSoundEffect(AudioManager.FX_HOME); 157 } 158 159 @Test testHomeSoundEffectNotPlayedWhenNonHomeTaskMovesToFront()160 public void testHomeSoundEffectNotPlayedWhenNonHomeTaskMovesToFront() { 161 // When HomeSoundEffectController is started and the home sound effect is enabled, 162 startController(true /* isHomeSoundEffectEnabled */); 163 164 // And a standard, non-home task, moves to the front, 165 when(mActivityManagerWrapper.getRunningTask()).thenReturn(mTaskAStandardActivity); 166 mTaskStackChangeListener.onTaskStackChanged(); 167 168 // Then no home sound effect should be played. 169 verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); 170 } 171 172 /** 173 * Task A (playHomeTransitionSound = true) -> HOME -> HOME (activity type standard) 174 * Expectation: Home sound is played once after HOME moves to front 175 */ 176 @Test testHomeSoundEffectNotPlayedWhenOtherHomeActivityMovesToFront()177 public void testHomeSoundEffectNotPlayedWhenOtherHomeActivityMovesToFront() { 178 // When HomeSoundEffectController is started and the home sound effect is enabled, 179 startController(true /* isHomeSoundEffectEnabled */); 180 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 181 mTaskAStandardActivity, 182 mHomeTaskHomeActivity, 183 mHomeTaskStandardActivity); 184 185 // And first a task different from the home task moves to front, 186 mTaskStackChangeListener.onTaskStackChanged(); 187 188 // And the home task moves to the front, 189 mTaskStackChangeListener.onTaskStackChanged(); 190 191 // Then the home sound effect should be played. 192 verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); 193 194 // If the home task moves to front a second time in a row, 195 mTaskStackChangeListener.onTaskStackChanged(); 196 197 // Then no home sound effect should be played. 198 verify(mAudioManager, times(1)).playSoundEffect(AudioManager.FX_HOME); 199 } 200 201 /** 202 * Task A (playHomeTransitionSound = true) -> HOME (activity type standard) 203 * Expectation: Home sound is not played 204 */ 205 @Test testHomeSoundEffectNotPlayedWhenOtherHomeActivityMovesToFrontOfOtherApp()206 public void testHomeSoundEffectNotPlayedWhenOtherHomeActivityMovesToFrontOfOtherApp() { 207 // When HomeSoundEffectController is started and the home sound effect is enabled, 208 startController(true /* isHomeSoundEffectEnabled */); 209 210 // And first a task different from the home task moves to front, 211 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 212 mTaskAStandardActivity, 213 mHomeTaskStandardActivity); 214 mTaskStackChangeListener.onTaskStackChanged(); 215 216 // And an activity from the home package but not the home root activity moves to front 217 mTaskStackChangeListener.onTaskStackChanged(); 218 219 // Then no home sound effect should be played. 220 verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); 221 } 222 223 @Test testHomeSoundEffectDisabled()224 public void testHomeSoundEffectDisabled() { 225 // When HomeSoundEffectController is started and the home sound effect is disabled, 226 startController(false /* isHomeSoundEffectEnabled */); 227 228 // Then no TaskStackListener should be registered 229 verify(mTaskStackChangeListeners, never()).registerTaskStackListener( 230 any(TaskStackChangeListener.class)); 231 } 232 233 /** 234 * Task A (playHomeTransitionSound = false) -> HOME 235 * Expectation: Home sound is not played 236 */ 237 @Test testHomeSoundEffectNotPlayedWhenHomeActivityMovesToFrontAfterException()238 public void testHomeSoundEffectNotPlayedWhenHomeActivityMovesToFrontAfterException() { 239 // When HomeSoundEffectController is started and the home sound effect is enabled, 240 startController(true /* isHomeSoundEffectEnabled */); 241 242 // And first a task different from the home task moves to front, which has 243 // {@link ActivityInfo#PRIVATE_FLAG_HOME_TRANSITION_SOUND} set to <code>false</code> 244 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 245 mTaskAExceptionActivity, 246 mHomeTaskHomeActivity); 247 mTaskStackChangeListener.onTaskStackChanged(); 248 249 // And the home task moves to the front, 250 mTaskStackChangeListener.onTaskStackChanged(); 251 252 // Then no home sound effect should be played because the last package is an exception. 253 verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); 254 } 255 256 /** 257 * HOME -> Task A (playHomeTransitionSound = true) -> Task A (playHomeTransitionSound = false) 258 * -> HOME 259 * Expectation: Home sound is not played 260 */ 261 @Test testHomeSoundEffectNotPlayedWhenHomeActivityMovesToFrontAfterException2()262 public void testHomeSoundEffectNotPlayedWhenHomeActivityMovesToFrontAfterException2() { 263 // When HomeSoundEffectController is started and the home sound effect is enabled, 264 startController(true /* isHomeSoundEffectEnabled */); 265 266 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 267 mTaskAStandardActivity, 268 mTaskAExceptionActivity, 269 mHomeTaskHomeActivity); 270 271 // And first a task different from the home task moves to front 272 mTaskStackChangeListener.onTaskStackChanged(); 273 274 // Then a different activity from the same task moves to front, which has 275 // {@link ActivityInfo#PRIVATE_FLAG_HOME_TRANSITION_SOUND} set to <code>false</code> 276 mTaskStackChangeListener.onTaskStackChanged(); 277 278 // And the home task moves to the front, 279 mTaskStackChangeListener.onTaskStackChanged(); 280 281 // Then no home sound effect should be played. 282 verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); 283 } 284 285 /** 286 * HOME -> Task A (playHomeTransitionSound = false) -> Task A (playHomeTransitionSound = true) 287 * -> HOME 288 * Expectation: Home sound is played 289 */ 290 @Test testHomeSoundEffectPlayedWhenHomeActivityMovesToFrontAfterException()291 public void testHomeSoundEffectPlayedWhenHomeActivityMovesToFrontAfterException() { 292 // When HomeSoundEffectController is started and the home sound effect is enabled, 293 startController(true /* isHomeSoundEffectEnabled */); 294 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 295 mTaskAExceptionActivity, 296 mTaskAStandardActivity, 297 mHomeTaskHomeActivity); 298 299 // And first a task different from the home task moves to front, 300 // the topActivity of this task has {@link ActivityInfo#PRIVATE_FLAG_HOME_TRANSITION_SOUND} 301 // set to <code>false</code> 302 mTaskStackChangeListener.onTaskStackChanged(); 303 304 // Then a different activity from the same task moves to front, which has 305 // {@link ActivityInfo#PRIVATE_FLAG_HOME_TRANSITION_SOUND} set to <code>true</code> 306 mTaskStackChangeListener.onTaskStackChanged(); 307 308 // And the home task moves to the front, 309 mTaskStackChangeListener.onTaskStackChanged(); 310 311 // Then the home sound effect should be played. 312 verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); 313 } 314 315 /** 316 * HOME -> Task A (playHomeTransitionSound = false) -> Task A (empty task, no top activity) 317 * -> HOME 318 * Expectation: Home sound is not played 319 */ 320 @Test testHomeSoundEffectNotPlayedWhenEmptyTaskMovesToFrontAfterException()321 public void testHomeSoundEffectNotPlayedWhenEmptyTaskMovesToFrontAfterException() { 322 // When HomeSoundEffectController is started and the home sound effect is enabled, 323 startController(true /* isHomeSoundEffectEnabled */); 324 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 325 mTaskAExceptionActivity, 326 mEmptyTask, 327 mHomeTaskHomeActivity); 328 329 // And first a task different from the home task moves to front, whose topActivity has 330 // {@link ActivityInfo#PRIVATE_FLAG_HOME_TRANSITION_SOUND} set to <code>false</code> 331 mTaskStackChangeListener.onTaskStackChanged(); 332 333 // Then a task with no topActivity moves to front, 334 mTaskStackChangeListener.onTaskStackChanged(); 335 336 // And the home task moves to the front, 337 mTaskStackChangeListener.onTaskStackChanged(); 338 339 // Then no home sound effect should be played. 340 verify(mAudioManager, never()).playSoundEffect(AudioManager.FX_HOME); 341 } 342 343 /** 344 * HOME -> Task A (playHomeTransitionSound = true) -> Task A (empty task, no top activity) 345 * -> HOME 346 * Expectation: Home sound is played 347 */ 348 @Test testHomeSoundEffectPlayedWhenEmptyTaskMovesToFrontAfterStandardActivity()349 public void testHomeSoundEffectPlayedWhenEmptyTaskMovesToFrontAfterStandardActivity() { 350 // When HomeSoundEffectController is started and the home sound effect is enabled, 351 startController(true /* isHomeSoundEffectEnabled */); 352 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 353 mTaskAStandardActivity, 354 mEmptyTask, 355 mHomeTaskHomeActivity); 356 357 // And first a task different from the home task moves to front, whose topActivity has 358 // {@link ActivityInfo#PRIVATE_FLAG_HOME_TRANSITION_SOUND} set to <code>false</code> 359 mTaskStackChangeListener.onTaskStackChanged(); 360 361 // Then a task with no topActivity moves to front, 362 mTaskStackChangeListener.onTaskStackChanged(); 363 364 // And the home task moves to the front, 365 mTaskStackChangeListener.onTaskStackChanged(); 366 367 // Then the home sound effect should be played. 368 verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); 369 } 370 371 /** 372 * HOME -> Task A (playHomeTransitionSound = false, no permission) -> HOME 373 * Expectation: Home sound is played 374 */ 375 @Test testHomeSoundEffectPlayedWhenFlagSetButPermissionNotGranted()376 public void testHomeSoundEffectPlayedWhenFlagSetButPermissionNotGranted() { 377 // When HomeSoundEffectController is started and the home sound effect is enabled, 378 startController(true /* isHomeSoundEffectEnabled */); 379 when(mActivityManagerWrapper.getRunningTask()).thenReturn( 380 mTaskAStandardActivity, 381 mHomeTaskHomeActivity); 382 when(mPackageManager.checkPermission(Manifest.permission.DISABLE_SYSTEM_SOUND_EFFECTS, 383 NON_HOME_PACKAGE_NAME)).thenReturn(PackageManager.PERMISSION_DENIED); 384 385 // And first a task different from the home task moves to front, whose topActivity has 386 // {@link ActivityInfo#PRIVATE_FLAG_HOME_TRANSITION_SOUND} set to <code>true</code>, 387 // but the app doesn't have the right permission granted 388 mTaskStackChangeListener.onTaskStackChanged(); 389 390 391 // And the home task moves to the front, 392 mTaskStackChangeListener.onTaskStackChanged(); 393 394 // Then the home sound effect should be played. 395 verify(mAudioManager).playSoundEffect(AudioManager.FX_HOME); 396 } 397 398 /** 399 * Sets {@link AudioManager#isHomeSoundEffectEnabled()} and starts HomeSoundEffectController. 400 * If the home sound effect is enabled, the registered TaskStackChangeListener is extracted. 401 */ startController(boolean isHomeSoundEffectEnabled)402 private void startController(boolean isHomeSoundEffectEnabled) { 403 // Configure home sound effect to be enabled 404 doReturn(isHomeSoundEffectEnabled).when(mAudioManager).isHomeSoundEffectEnabled(); 405 406 mController.start(); 407 408 if (isHomeSoundEffectEnabled) { 409 // Construct controller. Save the TaskStackListener for injecting events. 410 final ArgumentCaptor<TaskStackChangeListener> listenerCaptor = 411 ArgumentCaptor.forClass(TaskStackChangeListener.class); 412 verify(mTaskStackChangeListeners).registerTaskStackListener(listenerCaptor.capture()); 413 mTaskStackChangeListener = listenerCaptor.getValue(); 414 } 415 } 416 createRunningTaskInfo(String packageName, int activityType, int taskId, boolean playHomeTransitionSound)417 private ActivityManager.RunningTaskInfo createRunningTaskInfo(String packageName, 418 int activityType, int taskId, boolean playHomeTransitionSound) { 419 ActivityManager.RunningTaskInfo res = new ActivityManager.RunningTaskInfo(); 420 res.topActivityInfo = new ActivityInfo(); 421 res.topActivityInfo.packageName = packageName; 422 res.topActivityType = activityType; 423 res.taskId = taskId; 424 if (!playHomeTransitionSound) { 425 // set the flag to 0 426 res.topActivityInfo.privateFlags &= ~ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND; 427 } else { 428 res.topActivityInfo.privateFlags |= ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND; 429 } 430 return res; 431 } 432 } 433