1 /*
2  * Copyright 2020 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 package com.android.server.audio;
17 
18 import static org.mockito.ArgumentMatchers.anyBoolean;
19 import static org.mockito.ArgumentMatchers.anyInt;
20 import static org.mockito.ArgumentMatchers.anyString;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.reset;
23 import static org.mockito.Mockito.spy;
24 import static org.mockito.Mockito.times;
25 import static org.mockito.Mockito.verify;
26 import static org.mockito.Mockito.when;
27 
28 import android.app.AppOpsManager;
29 import android.content.Context;
30 import android.media.AudioSystem;
31 import android.os.Looper;
32 import android.os.PermissionEnforcer;
33 import android.os.UserHandle;
34 import android.util.Log;
35 
36 import androidx.test.InstrumentationRegistry;
37 import androidx.test.filters.MediumTest;
38 import androidx.test.runner.AndroidJUnit4;
39 
40 import org.junit.Assert;
41 import org.junit.Before;
42 import org.junit.Rule;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 import org.mockito.junit.MockitoJUnit;
46 import org.mockito.junit.MockitoRule;
47 import org.mockito.Mock;
48 import org.mockito.Spy;
49 
50 @MediumTest
51 @RunWith(AndroidJUnit4.class)
52 public class AudioServiceTest {
53     private static final String TAG = "AudioServiceTest";
54 
55     private static final int MAX_MESSAGE_HANDLING_DELAY_MS = 100;
56 
57     @Rule
58     public final MockitoRule mockito = MockitoJUnit.rule();
59 
60     private Context mContext;
61     private AudioSystemAdapter mAudioSystem;
62     private SettingsAdapter mSettingsAdapter;
63 
64     @Spy private NoOpSystemServerAdapter mSpySystemServer;
65     @Mock private AppOpsManager mMockAppOpsManager;
66     @Mock private AudioPolicyFacade mMockAudioPolicy;
67     @Mock private PermissionEnforcer mMockPermissionEnforcer;
68 
69     // the class being unit-tested here
70     private AudioService mAudioService;
71 
72     private static boolean sLooperPrepared = false;
73 
74     @Before
setUp()75     public void setUp() throws Exception {
76         if (!sLooperPrepared) {
77             Looper.prepare();
78             sLooperPrepared = true;
79         }
80         mContext = InstrumentationRegistry.getTargetContext();
81         mAudioSystem = new NoOpAudioSystemAdapter();
82         mSettingsAdapter = new NoOpSettingsAdapter();
83         when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString()))
84                 .thenReturn(AppOpsManager.MODE_ALLOWED);
85         mAudioService = new AudioService(mContext, mAudioSystem, mSpySystemServer,
86                 mSettingsAdapter, mMockAudioPolicy, null, mMockAppOpsManager,
87                 mMockPermissionEnforcer);
88     }
89 
90     /**
91      * Test muting the mic reports the expected value, and the corresponding intent was fired
92      * @throws Exception
93      */
94     @Test
testMuteMicrophone()95     public void testMuteMicrophone() throws Exception {
96         Log.i(TAG, "running testMuteMicrophone");
97         Assert.assertNotNull(mAudioService);
98         final NoOpAudioSystemAdapter testAudioSystem = (NoOpAudioSystemAdapter) mAudioSystem;
99         testAudioSystem.configureMuteMicrophoneToFail(false);
100         for (boolean muted : new boolean[] { true, false}) {
101             testAudioSystem.configureIsMicrophoneMuted(!muted);
102             mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
103                     UserHandle.getCallingUserId(), null);
104             Assert.assertEquals("mic mute reporting wrong value",
105                     muted, mAudioService.isMicrophoneMuted());
106             // verify the intent for mic mute changed is supposed to be fired
107             Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
108             verify(mSpySystemServer, times(1))
109                     .sendMicrophoneMuteChangedIntent();
110             reset(mSpySystemServer);
111         }
112     }
113 
114     /**
115      * Test muting the mic with simulated failure reports the expected value, and the corresponding
116      * intent was fired
117      * @throws Exception
118      */
119     @Test
testMuteMicrophoneWhenFail()120     public void testMuteMicrophoneWhenFail() throws Exception {
121         Log.i(TAG, "running testMuteMicrophoneWhenFail");
122         Assert.assertNotNull(mAudioService);
123         final NoOpAudioSystemAdapter testAudioSystem = (NoOpAudioSystemAdapter) mAudioSystem;
124         testAudioSystem.configureMuteMicrophoneToFail(true);
125         for (boolean muted : new boolean[] { true, false}) {
126             testAudioSystem.configureIsMicrophoneMuted(!muted);
127             mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
128                     UserHandle.getCallingUserId(), null);
129             Assert.assertEquals("mic mute reporting wrong value",
130                     !muted, mAudioService.isMicrophoneMuted());
131             // verify the intent for mic mute changed is supposed to be fired
132             Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
133             verify(mSpySystemServer, times(1))
134                     .sendMicrophoneMuteChangedIntent();
135             reset(mSpySystemServer);
136         }
137     }
138 
139     @Test
testRingNotifAlias()140     public void testRingNotifAlias() throws Exception {
141         Log.i(TAG, "running testRingNotifAlias");
142         Assert.assertNotNull(mAudioService);
143         // TODO add initialization message that can be caught here instead of sleeping
144         Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // wait for full AudioService initialization
145 
146         // test with aliasing RING and NOTIFICATION
147         mAudioService.setNotifAliasRingForTest(true);
148         final int ringMaxVol = mAudioService.getStreamMaxVolume(AudioSystem.STREAM_RING);
149         final int ringMinVol = mAudioService.getStreamMinVolume(AudioSystem.STREAM_RING);
150         final int ringVol = ringMinVol + 1;
151         // set a value for NOTIFICATION so it's not at the target test value (ringMaxVol)
152         mAudioService.setStreamVolume(AudioSystem.STREAM_NOTIFICATION,
153                 ringVol, 0, "bla");
154         mAudioService.setStreamVolume(AudioSystem.STREAM_RING, ringMaxVol, 0, "bla");
155         Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
156         Assert.assertEquals(ringMaxVol,
157                 mAudioService.getStreamVolume(AudioSystem.STREAM_NOTIFICATION));
158 
159         // test with no aliasing between RING and NOTIFICATION
160         mAudioService.setNotifAliasRingForTest(false);
161         mAudioService.setStreamVolume(AudioSystem.STREAM_RING, ringVol, 0, "bla");
162         mAudioService.setStreamVolume(AudioSystem.STREAM_NOTIFICATION, ringMaxVol, 0, "bla");
163         Assert.assertEquals(ringVol, mAudioService.getStreamVolume(AudioSystem.STREAM_RING));
164         Assert.assertEquals(ringMaxVol, mAudioService.getStreamVolume(
165                 AudioSystem.STREAM_NOTIFICATION));
166     }
167 
168     @Test
testAudioPolicyException()169     public void testAudioPolicyException() throws Exception {
170         Log.i(TAG, "running testAudioPolicyException");
171         Assert.assertNotNull(mAudioService);
172         // Ensure that AudioPolicy inavailability doesn't bring down SystemServer
173         when(mMockAudioPolicy.isHotwordStreamSupported(anyBoolean())).thenThrow(
174                     new IllegalStateException(), new IllegalStateException());
175         Assert.assertEquals(false, mAudioService.isHotwordStreamSupported(false));
176         Assert.assertEquals(false, mAudioService.isHotwordStreamSupported(true));
177     }
178 }
179