• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package android.hardware.radio.tests.functional;
17 
18 import static org.junit.Assert.*;
19 import static org.junit.Assume.*;
20 import static org.mockito.Matchers.any;
21 import static org.mockito.Matchers.anyBoolean;
22 import static org.mockito.Matchers.anyInt;
23 import static org.mockito.Mockito.after;
24 import static org.mockito.Mockito.atMost;
25 import static org.mockito.Mockito.never;
26 import static org.mockito.Mockito.timeout;
27 import static org.mockito.Mockito.verify;
28 import static org.testng.Assert.assertThrows;
29 
30 import android.Manifest;
31 import android.content.Context;
32 import android.content.pm.PackageManager;
33 import android.hardware.radio.ProgramSelector;
34 import android.hardware.radio.RadioManager;
35 import android.hardware.radio.RadioTuner;
36 import android.test.suitebuilder.annotation.MediumTest;
37 import android.util.Log;
38 
39 import androidx.test.InstrumentationRegistry;
40 import androidx.test.runner.AndroidJUnit4;
41 
42 import org.junit.After;
43 import org.junit.Before;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.mockito.ArgumentCaptor;
47 import org.mockito.Mock;
48 import org.mockito.Mockito;
49 import org.mockito.MockitoAnnotations;
50 
51 import java.util.ArrayList;
52 import java.util.HashMap;
53 import java.util.List;
54 import java.util.Map;
55 
56 /**
57  * A test for broadcast radio API.
58  */
59 @RunWith(AndroidJUnit4.class)
60 @MediumTest
61 public class RadioTunerTest {
62     private static final String TAG = "BroadcastRadioTests.RadioTuner";
63 
64     public final Context mContext = InstrumentationRegistry.getContext();
65 
66     private final int kConfigCallbackTimeoutMs = 10000;
67     private final int kCancelTimeoutMs = 1000;
68     private final int kTuneCallbackTimeoutMs = 30000;
69     private final int kFullScanTimeoutMs = 60000;
70 
71     private RadioManager mRadioManager;
72     private RadioTuner mRadioTuner;
73     private RadioManager.ModuleProperties mModule;
74     private final List<RadioManager.ModuleProperties> mModules = new ArrayList<>();
75     @Mock private RadioTuner.Callback mCallback;
76 
77     RadioManager.AmBandDescriptor mAmBandDescriptor;
78     RadioManager.FmBandDescriptor mFmBandDescriptor;
79 
80     RadioManager.BandConfig mAmBandConfig;
81     RadioManager.BandConfig mFmBandConfig;
82 
83     @Before
setup()84     public void setup() {
85         MockitoAnnotations.initMocks(this);
86 
87         // check if radio is supported and skip the test if it's not
88         PackageManager packageManager = mContext.getPackageManager();
89         boolean isRadioSupported = packageManager.hasSystemFeature(
90                 PackageManager.FEATURE_BROADCAST_RADIO);
91         assumeTrue(isRadioSupported);
92 
93         // Check radio access permission
94         int res = mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_BROADCAST_RADIO);
95         assertEquals("ACCESS_BROADCAST_RADIO permission not granted",
96                 PackageManager.PERMISSION_GRANTED, res);
97 
98         mRadioManager = (RadioManager)mContext.getSystemService(Context.RADIO_SERVICE);
99         assertNotNull(mRadioManager);
100 
101         int status = mRadioManager.listModules(mModules);
102         assertEquals(RadioManager.STATUS_OK, status);
103         assertFalse(mModules.isEmpty());
104     }
105 
106     @After
tearDown()107     public void tearDown() {
108         mRadioManager = null;
109         mModules.clear();
110         if (mRadioTuner != null) {
111             mRadioTuner.close();
112             mRadioTuner = null;
113         }
114         resetCallback();
115     }
116 
openTuner()117     private void openTuner() {
118         openTuner(true);
119     }
120 
resetCallback()121     private void resetCallback() {
122         verify(mCallback, never()).onError(anyInt());
123         verify(mCallback, never()).onTuneFailed(anyInt(), any());
124         verify(mCallback, never()).onControlChanged(anyBoolean());
125         Mockito.reset(mCallback);
126     }
127 
openTuner(boolean withAudio)128     private void openTuner(boolean withAudio) {
129         assertNull(mRadioTuner);
130 
131         // find FM band and build its config
132         mModule = mModules.get(0);
133 
134         for (RadioManager.BandDescriptor band : mModule.getBands()) {
135             Log.d(TAG, "Band: " + band);
136             int bandType = band.getType();
137             if (bandType == RadioManager.BAND_AM || bandType == RadioManager.BAND_AM_HD) {
138                 mAmBandDescriptor = (RadioManager.AmBandDescriptor)band;
139             }
140             if (bandType == RadioManager.BAND_FM || bandType == RadioManager.BAND_FM_HD) {
141                 mFmBandDescriptor = (RadioManager.FmBandDescriptor)band;
142             }
143         }
144         assertNotNull(mAmBandDescriptor);
145         assertNotNull(mFmBandDescriptor);
146         mAmBandConfig = new RadioManager.AmBandConfig.Builder(mAmBandDescriptor).build();
147         mFmBandConfig = new RadioManager.FmBandConfig.Builder(mFmBandDescriptor).build();
148 
149         mRadioTuner = mRadioManager.openTuner(mModule.getId(),
150                 mFmBandConfig, withAudio, mCallback, null);
151         if (!withAudio) {
152             // non-audio sessions might not be supported - if so, then skip the test
153             assumeNotNull(mRadioTuner);
154         }
155         assertNotNull(mRadioTuner);
156         verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
157         resetCallback();
158 
159         boolean isAntennaConnected = mRadioTuner.isAntennaConnected();
160         assertTrue(isAntennaConnected);
161     }
162 
163     @Test
testOpenTuner()164     public void testOpenTuner() {
165         openTuner();
166     }
167 
168     @Test
testReopenTuner()169     public void testReopenTuner() throws Throwable {
170         openTuner();
171         mRadioTuner.close();
172         mRadioTuner = null;
173         Thread.sleep(100);  // TODO(b/36122635): force reopen
174         openTuner();
175     }
176 
177     @Test
testDoubleClose()178     public void testDoubleClose() {
179         openTuner();
180         mRadioTuner.close();
181         mRadioTuner.close();
182     }
183 
184     @Test
testUseAfterClose()185     public void testUseAfterClose() {
186         openTuner();
187         mRadioTuner.close();
188         int ret = mRadioTuner.cancel();
189         assertEquals(RadioManager.STATUS_INVALID_OPERATION, ret);
190     }
191 
192     @Test
testSetAndGetConfiguration()193     public void testSetAndGetConfiguration() {
194         openTuner();
195 
196         // set
197         int ret = mRadioTuner.setConfiguration(mAmBandConfig);
198         assertEquals(RadioManager.STATUS_OK, ret);
199         verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
200 
201         // get
202         RadioManager.BandConfig[] config = new RadioManager.BandConfig[1];
203         ret = mRadioTuner.getConfiguration(config);
204         assertEquals(RadioManager.STATUS_OK, ret);
205 
206         assertEquals(mAmBandConfig, config[0]);
207     }
208 
209     @Test
testSetBadConfiguration()210     public void testSetBadConfiguration() throws Throwable {
211         openTuner();
212 
213         // set null config
214         int ret = mRadioTuner.setConfiguration(null);
215         assertEquals(RadioManager.STATUS_BAD_VALUE, ret);
216         verify(mCallback, never()).onConfigurationChanged(any());
217 
218         // setting good config should recover
219         ret = mRadioTuner.setConfiguration(mAmBandConfig);
220         assertEquals(RadioManager.STATUS_OK, ret);
221         verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
222     }
223 
224     @Test
testMute()225     public void testMute() {
226         openTuner();
227 
228         boolean isMuted = mRadioTuner.getMute();
229         assertFalse(isMuted);
230 
231         int ret = mRadioTuner.setMute(true);
232         assertEquals(RadioManager.STATUS_OK, ret);
233         isMuted = mRadioTuner.getMute();
234         assertTrue(isMuted);
235 
236         ret = mRadioTuner.setMute(false);
237         assertEquals(RadioManager.STATUS_OK, ret);
238         isMuted = mRadioTuner.getMute();
239         assertFalse(isMuted);
240     }
241 
242     @Test
testMuteNoAudio()243     public void testMuteNoAudio() {
244         openTuner(false);
245 
246         int ret = mRadioTuner.setMute(false);
247         assertEquals(RadioManager.STATUS_ERROR, ret);
248 
249         boolean isMuted = mRadioTuner.getMute();
250         assertTrue(isMuted);
251     }
252 
253     @Test
testStep()254     public void testStep() {
255         openTuner();
256 
257         int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
258         assertEquals(RadioManager.STATUS_OK, ret);
259         verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
260 
261         resetCallback();
262 
263         ret = mRadioTuner.step(RadioTuner.DIRECTION_UP, false);
264         assertEquals(RadioManager.STATUS_OK, ret);
265         verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
266     }
267 
268     @Test
testStepLoop()269     public void testStepLoop() {
270         openTuner();
271 
272         for (int i = 0; i < 10; i++) {
273             Log.d(TAG, "step loop iteration " + (i + 1));
274 
275             int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
276             assertEquals(RadioManager.STATUS_OK, ret);
277             verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
278 
279             resetCallback();
280         }
281     }
282 
283     @Test
testTuneAndGetPI()284     public void testTuneAndGetPI() {
285         openTuner();
286 
287         int channel = mFmBandConfig.getLowerLimit() + mFmBandConfig.getSpacing();
288 
289         // test tune
290         int ret = mRadioTuner.tune(channel, 0);
291         assertEquals(RadioManager.STATUS_OK, ret);
292         ArgumentCaptor<RadioManager.ProgramInfo> infoc =
293                 ArgumentCaptor.forClass(RadioManager.ProgramInfo.class);
294         verify(mCallback, timeout(kTuneCallbackTimeoutMs))
295                 .onProgramInfoChanged(infoc.capture());
296         assertEquals(channel, infoc.getValue().getChannel());
297 
298         // test getProgramInformation
299         RadioManager.ProgramInfo[] info = new RadioManager.ProgramInfo[1];
300         ret = mRadioTuner.getProgramInformation(info);
301         assertEquals(RadioManager.STATUS_OK, ret);
302         assertNotNull(info[0]);
303         assertEquals(channel, info[0].getChannel());
304         Log.d(TAG, "PI: " + info[0].toString());
305     }
306 
307     @Test
testDummyCancel()308     public void testDummyCancel() {
309         openTuner();
310 
311         int ret = mRadioTuner.cancel();
312         assertEquals(RadioManager.STATUS_OK, ret);
313     }
314 
315     @Test
testLateCancel()316     public void testLateCancel() {
317         openTuner();
318 
319         int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, false);
320         assertEquals(RadioManager.STATUS_OK, ret);
321         verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
322 
323         int cancelRet = mRadioTuner.cancel();
324         assertEquals(RadioManager.STATUS_OK, cancelRet);
325     }
326 
327     @Test
testScanAndCancel()328     public void testScanAndCancel() {
329         openTuner();
330 
331         /* There is a possible race condition between scan and cancel commands - the scan may finish
332          * before cancel command is issued. Thus we accept both outcomes in this test.
333          */
334         int scanRet = mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, true);
335         int cancelRet = mRadioTuner.cancel();
336 
337         assertEquals(RadioManager.STATUS_OK, scanRet);
338         assertEquals(RadioManager.STATUS_OK, cancelRet);
339 
340         verify(mCallback, after(kCancelTimeoutMs).atMost(1)).onError(RadioTuner.ERROR_CANCELLED);
341         verify(mCallback, atMost(1)).onProgramInfoChanged(any());
342     }
343 
344     @Test
testStartBackgroundScan()345     public void testStartBackgroundScan() {
346         openTuner();
347 
348         boolean ret = mRadioTuner.startBackgroundScan();
349         boolean isSupported = mModule.isBackgroundScanningSupported();
350         assertEquals(isSupported, ret);
351     }
352 
353     @Test
testGetProgramList()354     public void testGetProgramList() {
355         openTuner();
356 
357         try {
358             Map<String, String> filter = new HashMap<>();
359             filter.put("com.google.dummy", "dummy");
360             List<RadioManager.ProgramInfo> list = mRadioTuner.getProgramList(filter);
361             assertNotNull(list);
362         } catch (IllegalStateException e) {
363             // the list may or may not be ready at this point
364             Log.i(TAG, "Background list is not ready");
365         }
366     }
367 
368     @Test
testTuneFromProgramList()369     public void testTuneFromProgramList() {
370         openTuner();
371 
372         List<RadioManager.ProgramInfo> list;
373 
374         try {
375             list = mRadioTuner.getProgramList(null);
376             assertNotNull(list);
377         } catch (IllegalStateException e) {
378             Log.i(TAG, "Background list is not ready, trying to fix it");
379 
380             boolean success = mRadioTuner.startBackgroundScan();
381             assertTrue(success);
382             verify(mCallback, timeout(kFullScanTimeoutMs)).onBackgroundScanComplete();
383 
384             list = mRadioTuner.getProgramList(null);
385             assertNotNull(list);
386         }
387 
388         if (list.isEmpty()) {
389             Log.i(TAG, "Program list is empty, can't test tune");
390             return;
391         }
392 
393         ProgramSelector sel = list.get(0).getSelector();
394         mRadioTuner.tune(sel);
395         ArgumentCaptor<RadioManager.ProgramInfo> infoc =
396                 ArgumentCaptor.forClass(RadioManager.ProgramInfo.class);
397         verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(infoc.capture());
398         assertEquals(sel, infoc.getValue().getSelector());
399     }
400 
401     @Test
testForcedAnalog()402     public void testForcedAnalog() {
403         openTuner();
404 
405         boolean isSupported = true;
406         boolean isForced;
407         try {
408             isForced = mRadioTuner.isAnalogForced();
409             assertFalse(isForced);
410         } catch (IllegalStateException ex) {
411             Log.i(TAG, "Forced analog switch is not supported by this tuner");
412             isSupported = false;
413         }
414 
415         if (isSupported) {
416             mRadioTuner.setAnalogForced(true);
417             isForced = mRadioTuner.isAnalogForced();
418             assertTrue(isForced);
419 
420             mRadioTuner.setAnalogForced(false);
421             isForced = mRadioTuner.isAnalogForced();
422             assertFalse(isForced);
423         } else {
424             assertThrows(IllegalStateException.class, () -> mRadioTuner.setAnalogForced(true));
425         }
426     }
427 }
428