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