RadioTunerTest.java revision df77643cd8024b381c7df2f509175c7be719fae9
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 */
16package android.hardware.radio.tests.functional;
17
18import android.Manifest;
19import android.content.Context;
20import android.content.pm.PackageManager;
21import android.hardware.radio.ProgramSelector;
22import android.hardware.radio.RadioManager;
23import android.hardware.radio.RadioTuner;
24import android.support.test.InstrumentationRegistry;
25import android.support.test.runner.AndroidJUnit4;
26import android.test.suitebuilder.annotation.MediumTest;
27import android.util.Log;
28
29import java.lang.reflect.Constructor;
30import java.util.ArrayList;
31import java.util.List;
32
33import org.junit.After;
34import org.junit.Before;
35import org.junit.Test;
36import org.junit.runner.RunWith;
37import org.mockito.ArgumentCaptor;
38import org.mockito.Mock;
39import org.mockito.Mockito;
40import org.mockito.MockitoAnnotations;
41
42import static org.junit.Assert.*;
43import static org.junit.Assume.*;
44import static org.mockito.Matchers.any;
45import static org.mockito.Matchers.anyInt;
46import static org.mockito.Mockito.after;
47import static org.mockito.Mockito.atLeast;
48import static org.mockito.Mockito.atMost;
49import static org.mockito.Mockito.never;
50import static org.mockito.Mockito.timeout;
51import static org.mockito.Mockito.times;
52import static org.mockito.Mockito.verify;
53import static org.mockito.Mockito.verifyNoMoreInteractions;
54import static org.testng.Assert.assertThrows;
55
56/**
57 * A test for broadcast radio API.
58 */
59@RunWith(AndroidJUnit4.class)
60@MediumTest
61public 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
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
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
117    private void openTuner() {
118        openTuner(true);
119    }
120
121    private void resetCallback() {
122        verify(mCallback, atLeast(0)).onMetadataChanged(any());
123        verifyNoMoreInteractions(mCallback);
124        Mockito.reset(mCallback);
125    }
126
127    private void openTuner(boolean withAudio) {
128        assertNull(mRadioTuner);
129
130        // find FM band and build its config
131        mModule = mModules.get(0);
132        for (RadioManager.BandDescriptor band : mModule.getBands()) {
133            int bandType = band.getType();
134            if (bandType == RadioManager.BAND_AM || bandType == RadioManager.BAND_AM_HD) {
135                mAmBandDescriptor = (RadioManager.AmBandDescriptor)band;
136            }
137            if (bandType == RadioManager.BAND_FM || bandType == RadioManager.BAND_FM_HD) {
138                mFmBandDescriptor = (RadioManager.FmBandDescriptor)band;
139            }
140        }
141        assertNotNull(mAmBandDescriptor);
142        assertNotNull(mFmBandDescriptor);
143        mAmBandConfig = new RadioManager.AmBandConfig.Builder(mAmBandDescriptor).build();
144        mFmBandConfig = new RadioManager.FmBandConfig.Builder(mFmBandDescriptor).build();
145
146        mRadioTuner = mRadioManager.openTuner(mModule.getId(),
147                mFmBandConfig, withAudio, mCallback, null);
148        assertNotNull(mRadioTuner);
149        verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
150        resetCallback();
151
152        boolean isAntennaConnected = mRadioTuner.isAntennaConnected();
153        assertTrue(isAntennaConnected);
154    }
155
156    @Test
157    public void testOpenTuner() {
158        openTuner();
159    }
160
161    @Test
162    public void testReopenTuner() throws Throwable {
163        openTuner();
164        mRadioTuner.close();
165        mRadioTuner = null;
166        Thread.sleep(100);  // TODO(b/36122635): force reopen
167        openTuner();
168    }
169
170    @Test
171    public void testDoubleClose() {
172        openTuner();
173        mRadioTuner.close();
174        mRadioTuner.close();
175    }
176
177    @Test
178    public void testUseAfterClose() {
179        openTuner();
180        mRadioTuner.close();
181        int ret = mRadioTuner.cancel();
182        assertEquals(RadioManager.STATUS_INVALID_OPERATION, ret);
183    }
184
185    @Test
186    public void testSetAndGetConfiguration() {
187        openTuner();
188
189        // set
190        int ret = mRadioTuner.setConfiguration(mAmBandConfig);
191        assertEquals(RadioManager.STATUS_OK, ret);
192        verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
193
194        // get
195        RadioManager.BandConfig[] config = new RadioManager.BandConfig[1];
196        ret = mRadioTuner.getConfiguration(config);
197        assertEquals(RadioManager.STATUS_OK, ret);
198
199        assertEquals(mAmBandConfig, config[0]);
200    }
201
202    @Test
203    public void testSetBadConfiguration() throws Throwable {
204        openTuner();
205
206        // set bad config
207        Constructor<RadioManager.AmBandConfig> configConstr =
208                RadioManager.AmBandConfig.class.getDeclaredConstructor(
209                        int.class, int.class, int.class, int.class, int.class, boolean.class);
210        configConstr.setAccessible(true);
211        RadioManager.AmBandConfig badConfig = configConstr.newInstance(
212                0 /*region*/, RadioManager.BAND_AM /*type*/,
213                10000 /*lowerLimit*/, 1 /*upperLimit*/, 100 /*spacing*/, false /*stereo*/);
214        int ret = mRadioTuner.setConfiguration(badConfig);
215        assertEquals(RadioManager.STATUS_BAD_VALUE, ret);
216        verify(mCallback, never()).onConfigurationChanged(any());
217
218        // set null config
219        ret = mRadioTuner.setConfiguration(null);
220        assertEquals(RadioManager.STATUS_BAD_VALUE, ret);
221        verify(mCallback, never()).onConfigurationChanged(any());
222
223        // setting good config should recover
224        ret = mRadioTuner.setConfiguration(mAmBandConfig);
225        assertEquals(RadioManager.STATUS_OK, ret);
226        verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
227    }
228
229    @Test
230    public void testMute() {
231        openTuner();
232
233        boolean isMuted = mRadioTuner.getMute();
234        assertFalse(isMuted);
235
236        int ret = mRadioTuner.setMute(true);
237        assertEquals(RadioManager.STATUS_OK, ret);
238        isMuted = mRadioTuner.getMute();
239        assertTrue(isMuted);
240
241        ret = mRadioTuner.setMute(false);
242        assertEquals(RadioManager.STATUS_OK, ret);
243        isMuted = mRadioTuner.getMute();
244        assertFalse(isMuted);
245    }
246
247    @Test
248    public void testMuteNoAudio() {
249        openTuner(false);
250
251        int ret = mRadioTuner.setMute(false);
252        assertEquals(RadioManager.STATUS_ERROR, ret);
253
254        boolean isMuted = mRadioTuner.getMute();
255        assertTrue(isMuted);
256    }
257
258    @Test
259    public void testStep() {
260        openTuner();
261
262        int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
263        assertEquals(RadioManager.STATUS_OK, ret);
264        verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
265
266        resetCallback();
267
268        ret = mRadioTuner.step(RadioTuner.DIRECTION_UP, false);
269        assertEquals(RadioManager.STATUS_OK, ret);
270        verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
271    }
272
273    @Test
274    public void testTuneAndGetPI() {
275        openTuner();
276
277        int channel = mFmBandConfig.getLowerLimit() + mFmBandConfig.getSpacing();
278
279        // test tune
280        int ret = mRadioTuner.tune(channel, 0);
281        assertEquals(RadioManager.STATUS_OK, ret);
282        ArgumentCaptor<RadioManager.ProgramInfo> infoc =
283                ArgumentCaptor.forClass(RadioManager.ProgramInfo.class);
284        verify(mCallback, timeout(kTuneCallbackTimeoutMs))
285                .onProgramInfoChanged(infoc.capture());
286        assertEquals(channel, infoc.getValue().getChannel());
287
288        // test getProgramInformation
289        RadioManager.ProgramInfo[] info = new RadioManager.ProgramInfo[1];
290        ret = mRadioTuner.getProgramInformation(info);
291        assertEquals(RadioManager.STATUS_OK, ret);
292        assertNotNull(info[0]);
293        assertEquals(channel, info[0].getChannel());
294    }
295
296    @Test
297    public void testDummyCancel() {
298        openTuner();
299
300        int ret = mRadioTuner.cancel();
301        assertEquals(RadioManager.STATUS_OK, ret);
302    }
303
304    @Test
305    public void testLateCancel() {
306        openTuner();
307
308        int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, false);
309        assertEquals(RadioManager.STATUS_OK, ret);
310        verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
311
312        int cancelRet = mRadioTuner.cancel();
313        assertEquals(RadioManager.STATUS_OK, cancelRet);
314    }
315
316    @Test
317    public void testScanAndCancel() {
318        openTuner();
319
320        /* There is a possible race condition between scan and cancel commands - the scan may finish
321         * before cancel command is issued. Thus we accept both outcomes in this test.
322         */
323        int scanRet = mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, true);
324        int cancelRet = mRadioTuner.cancel();
325
326        assertEquals(RadioManager.STATUS_OK, scanRet);
327        assertEquals(RadioManager.STATUS_OK, cancelRet);
328
329        verify(mCallback, after(kCancelTimeoutMs).atMost(1)).onError(RadioTuner.ERROR_CANCELLED);
330        verify(mCallback, atMost(1)).onProgramInfoChanged(any());
331    }
332
333    @Test
334    public void testStartBackgroundScan() {
335        openTuner();
336
337        boolean ret = mRadioTuner.startBackgroundScan();
338        boolean isSupported = mModule.isBackgroundScanningSupported();
339        assertEquals(isSupported, ret);
340    }
341
342    @Test
343    public void testGetProgramList() {
344        openTuner();
345
346        try {
347            List<RadioManager.ProgramInfo> list = mRadioTuner.getProgramList(null);
348            assertNotNull(list);
349        } catch (IllegalStateException e) {
350            // the list may or may not be ready at this point
351            Log.i(TAG, "Background list is not ready");
352        }
353    }
354
355    @Test
356    public void testTuneFromProgramList() {
357        openTuner();
358
359        List<RadioManager.ProgramInfo> list;
360
361        try {
362            list = mRadioTuner.getProgramList(null);
363            assertNotNull(list);
364        } catch (IllegalStateException e) {
365            Log.i(TAG, "Background list is not ready, trying to fix it");
366
367            boolean success = mRadioTuner.startBackgroundScan();
368            assertTrue(success);
369            verify(mCallback, timeout(kFullScanTimeoutMs)).onBackgroundScanComplete();
370
371            list = mRadioTuner.getProgramList(null);
372            assertNotNull(list);
373        }
374
375        if (list.isEmpty()) {
376            Log.i(TAG, "Program list is empty, can't test tune");
377            return;
378        }
379
380        ProgramSelector sel = list.get(0).getSelector();
381        mRadioTuner.tune(sel);
382        ArgumentCaptor<RadioManager.ProgramInfo> infoc =
383                ArgumentCaptor.forClass(RadioManager.ProgramInfo.class);
384        verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(infoc.capture());
385        assertEquals(sel, infoc.getValue().getSelector());
386    }
387
388    @Test
389    public void testForcedAnalog() {
390        openTuner();
391
392        boolean isSupported = true;
393        boolean isForced;
394        try {
395            isForced = mRadioTuner.isAnalogForced();
396            assertFalse(isForced);
397        } catch (IllegalStateException ex) {
398            Log.i(TAG, "Forced analog switch is not supported by this tuner");
399            isSupported = false;
400        }
401
402        if (isSupported) {
403            mRadioTuner.setAnalogForced(true);
404            isForced = mRadioTuner.isAnalogForced();
405            assertTrue(isForced);
406
407            mRadioTuner.setAnalogForced(false);
408            isForced = mRadioTuner.isAnalogForced();
409            assertFalse(isForced);
410        } else {
411            assertThrows(IllegalStateException.class, () -> mRadioTuner.setAnalogForced(true));
412        }
413    }
414}
415