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