xref: /aosp_15_r20/cts/tests/tests/broadcastradio/src/android/broadcastradio/cts/AbstractRadioTestCase.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2022 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 android.broadcastradio.cts;
18 
19 import static com.google.common.truth.Truth.assertWithMessage;
20 import static com.google.common.truth.TruthJUnit.assume;
21 
22 import static org.junit.Assume.assumeTrue;
23 
24 import android.Manifest;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.hardware.radio.ProgramSelector;
28 import android.hardware.radio.RadioManager;
29 import android.hardware.radio.RadioTuner;
30 import android.platform.test.flag.junit.CheckFlagsRule;
31 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
32 import android.util.Log;
33 
34 import androidx.test.platform.app.InstrumentationRegistry;
35 
36 import com.android.compatibility.common.util.SafeCleanerRule;
37 
38 import com.google.common.truth.Expect;
39 
40 import org.junit.After;
41 import org.junit.Before;
42 import org.junit.Rule;
43 
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.concurrent.CountDownLatch;
47 import java.util.concurrent.TimeUnit;
48 
49 abstract class AbstractRadioTestCase {
50 
51     protected static final String TAG = AbstractRadioTestCase.class.getSimpleName();
52 
53     protected static final int HANDLER_CALLBACK_MS = 100;
54     protected static final int CANCEL_TIMEOUT_MS = 2_000;
55     protected static final int TUNE_CALLBACK_TIMEOUT_MS  = 30_000;
56     protected static final int PROGRAM_LIST_COMPLETE_TIMEOUT_MS = 60_000;
57 
58     private Context mContext;
59 
60     protected RadioManager mRadioManager;
61     protected RadioTuner mRadioTuner;
62     protected TestRadioTunerCallback mCallback;
63 
64     protected RadioManager.BandConfig mAmBandConfig;
65     protected RadioManager.BandConfig mFmBandConfig;
66     protected RadioManager.ModuleProperties mModule;
67 
68     @Rule
69     public final Expect mExpect = Expect.create();
70     @Rule
71     public SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
72             .run(() -> {
73                 if (mRadioTuner != null) {
74                     mRadioTuner.close();
75                 }
76                 assertNoTunerFailureAndResetCallback("cleaning up");
77             });
78     @Rule
79     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
80 
81     @Before
setup()82     public void setup() {
83         InstrumentationRegistry.getInstrumentation()
84                 .getUiAutomation()
85                 .adoptShellPermissionIdentity(Manifest.permission.ACCESS_BROADCAST_RADIO);
86 
87         mContext = InstrumentationRegistry.getInstrumentation().getContext();
88         PackageManager packageManager = mContext.getPackageManager();
89         boolean isRadioSupported = packageManager.hasSystemFeature(
90                 PackageManager.FEATURE_BROADCAST_RADIO);
91 
92         assumeTrue("Radio supported", isRadioSupported);
93 
94         mRadioManager = mContext.getSystemService(RadioManager.class);
95 
96         assertWithMessage("Radio manager exists").that(mRadioManager).isNotNull();
97 
98         mCallback = new TestRadioTunerCallback();
99     }
100 
101     @After
cleanUp()102     public void cleanUp() {
103         InstrumentationRegistry.getInstrumentation().getUiAutomation()
104                 .dropShellPermissionIdentity();
105     }
106 
assertNoTunerFailureAndResetCallback(String operation)107     protected void assertNoTunerFailureAndResetCallback(String operation) {
108         if (mCallback == null) {
109             return;
110         }
111         assertWithMessage("Error callback when " + operation)
112                 .that(mCallback.errorCount).isEqualTo(0);
113         assertWithMessage("Tune failure callback when " + operation)
114                 .that(mCallback.tuneFailureCount).isEqualTo(0);
115         assertWithMessage("Control changed callback when " + operation)
116                 .that(mCallback.controlChangeCount).isEqualTo(0);
117         mCallback.reset();
118     }
119 
openAmFmTuner()120     protected void openAmFmTuner() throws Exception {
121         openAmFmTuner(/* withAudio= */ true);
122     }
123 
openAmFmTuner(boolean withAudio)124     protected void openAmFmTuner(boolean withAudio) throws Exception {
125         setAmFmConfig();
126 
127         assume().withMessage("AM/FM radio module exists").that(mModule).isNotNull();
128 
129         mRadioTuner = mRadioManager.openTuner(mModule.getId(), mFmBandConfig, withAudio, mCallback,
130                 /* handler= */ null);
131 
132         if (!withAudio) {
133             assume().withMessage("Non-audio radio tuner").that(mRadioTuner).isNotNull();
134         }
135 
136         mExpect.withMessage("Radio tuner opened").that(mRadioTuner).isNotNull();
137         // Opening tuner will invoke program info change callback only when there exists a tuner
138         // before with non-null program info in broadcast radio service.
139         mCallback.waitForProgramInfoChangeCallback(HANDLER_CALLBACK_MS);
140 
141         assertNoTunerFailureAndResetCallback("opening AM/FM tuner");
142     }
143 
setAmFmConfig()144     protected void setAmFmConfig() {
145         mModule = null;
146         List<RadioManager.ModuleProperties> modules = new ArrayList<>();
147         mRadioManager.listModules(modules);
148         RadioManager.AmBandDescriptor amBandDescriptor = null;
149         RadioManager.FmBandDescriptor fmBandDescriptor = null;
150         for (int moduleIndex = 0; moduleIndex < modules.size(); moduleIndex++) {
151             for (RadioManager.BandDescriptor band : modules.get(moduleIndex).getBands()) {
152                 int bandType = band.getType();
153                 if (bandType == RadioManager.BAND_AM || bandType == RadioManager.BAND_AM_HD) {
154                     amBandDescriptor = (RadioManager.AmBandDescriptor) band;
155                 }
156                 if (bandType == RadioManager.BAND_FM || bandType == RadioManager.BAND_FM_HD) {
157                     fmBandDescriptor = (RadioManager.FmBandDescriptor) band;
158                 }
159             }
160             if (amBandDescriptor != null && fmBandDescriptor != null) {
161                 mModule = modules.get(moduleIndex);
162                 mAmBandConfig = new RadioManager.AmBandConfig.Builder(amBandDescriptor).build();
163                 mFmBandConfig = new RadioManager.FmBandConfig.Builder(fmBandDescriptor).build();
164                 break;
165             }
166         }
167     }
168 
169     static final class TestRadioTunerCallback extends RadioTuner.Callback {
170 
171         public int errorCount;
172         public int error = RadioManager.STATUS_OK;
173         public int tuneFailureCount;
174         public int tunerFailureResult = RadioTuner.TUNER_RESULT_OK;
175         public ProgramSelector tunerFailureSelector;
176         public int controlChangeCount;
177         public RadioManager.ProgramInfo currentProgramInfo;
178         public int configFlagCount;
179         private CountDownLatch mProgramInfoChangeLatch = new CountDownLatch(1);
180 
reset()181         public void reset() {
182             resetTuneFailureCallback();
183             controlChangeCount = 0;
184             currentProgramInfo = null;
185             configFlagCount = 0;
186             resetProgramInfoChangeCallback();
187         }
188 
resetTuneFailureCallback()189         public void resetTuneFailureCallback() {
190             errorCount = 0;
191             error = RadioManager.STATUS_OK;
192             tuneFailureCount = 0;
193             tunerFailureResult = RadioTuner.TUNER_RESULT_OK;
194             tunerFailureSelector = null;
195         }
196 
resetProgramInfoChangeCallback()197         public void resetProgramInfoChangeCallback() {
198             mProgramInfoChangeLatch = new CountDownLatch(1);
199         }
200 
waitForProgramInfoChangeCallback(int timeoutMs)201         public boolean waitForProgramInfoChangeCallback(int timeoutMs) throws InterruptedException {
202             return waitForCallback(mProgramInfoChangeLatch, timeoutMs);
203         }
204 
waitForCallback(CountDownLatch latch, int timeoutMs)205         private boolean waitForCallback(CountDownLatch latch, int timeoutMs)
206                 throws InterruptedException {
207             Log.v(TAG, "Waiting " + timeoutMs + " ms for latch " + latch);
208             if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
209                 Log.e(TAG, latch + " not called in " + timeoutMs + " ms");
210                 return false;
211             }
212             return true;
213         }
214 
215         @Override
onError(int status)216         public void onError(int status) {
217             Log.e(TAG, "onError(" + status + ")");
218             error = status;
219             errorCount++;
220         }
221 
222         @Override
onTuneFailed(int result, ProgramSelector selector)223         public void onTuneFailed(int result, ProgramSelector selector) {
224             Log.e(TAG, "onTuneFailed(" + result + ", " + selector + ")");
225             tunerFailureResult = result;
226             tunerFailureSelector = selector;
227             tuneFailureCount++;
228         }
229 
230         @Override
onProgramInfoChanged(RadioManager.ProgramInfo info)231         public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
232             currentProgramInfo = info;
233             mProgramInfoChangeLatch.countDown();
234         }
235 
236         @Override
onConfigFlagUpdated(int flag, boolean value)237         public void onConfigFlagUpdated(int flag, boolean value) {
238             configFlagCount++;
239         }
240 
241         @Override
onControlChanged(boolean control)242         public void onControlChanged(boolean control) {
243             Log.e(TAG, "onControlChanged(" + control + ")");
244             controlChangeCount++;
245         }
246     }
247 }
248