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