Add tests for Tuner resource share & transfer
- testLnbAddAndRemoveCallback
- testTransferOwner
- testCloseFrontend
Bug: 192010866
Test: above cts tests
Change-Id: I93ed92f46a29feb0dfee8441be1a66c37a3dc3a4
diff --git a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java
index ac0f87f..7af10b2 100644
--- a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java
+++ b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
@@ -123,6 +124,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -167,6 +169,7 @@
public void onServiceConnected(ComponentName componentName, IBinder service) {
mBlockingQueue.offer(service);
}
+
@Override
public void onServiceDisconnected(ComponentName componentName){}
@@ -177,6 +180,86 @@
}
}
+ private class TunerTestOnTuneEventListener implements OnTuneEventListener {
+ public static final int INVALID_TUNE_EVENT = -1;
+ private static final int SLEEP_TIME_MS = 100;
+ private static final int TIMEOUT_MS = 500;
+ private final ReentrantLock mLock = new ReentrantLock();
+ private final ConditionVariable mCV = new ConditionVariable();
+ private int mLastTuneEvent = INVALID_TUNE_EVENT;
+
+ @Override
+ public void onTuneEvent(int tuneEvent) {
+ synchronized (mLock) {
+ mLastTuneEvent = tuneEvent;
+ mCV.open();
+ }
+ }
+
+ public void resetLastTuneEvent() {
+ synchronized (mLock) {
+ mLastTuneEvent = INVALID_TUNE_EVENT;
+ }
+ }
+
+ public int getLastTuneEvent() {
+ try {
+ // yield to let the callback handling execute
+ Thread.sleep(SLEEP_TIME_MS);
+ } catch (Exception e) {
+ // ignore exception
+ }
+ synchronized (mLock) {
+ mCV.block(TIMEOUT_MS);
+ mCV.close();
+ return mLastTuneEvent;
+ }
+ }
+ }
+
+ private class TunerTestLnbCallback implements LnbCallback {
+ public static final int INVALID_LNB_EVENT = -1;
+ private static final int SLEEP_TIME_MS = 100;
+ private static final int TIMEOUT_MS = 500;
+ private final ReentrantLock mDMLock = new ReentrantLock();
+ private final ConditionVariable mDMCV = new ConditionVariable();
+ private boolean mOnDiseqcMessageCalled = false;
+
+ // will not test this as there is no good way to trigger this
+ @Override
+ public void onEvent(int lnbEventType) {}
+
+ // will test this instead
+ @Override
+ public void onDiseqcMessage(byte[] diseqcMessage) {
+ synchronized (mDMLock) {
+ mOnDiseqcMessageCalled = true;
+ mDMCV.open();
+ }
+ }
+
+ public void resetOnDiseqcMessageCalled() {
+ synchronized (mDMLock) {
+ mOnDiseqcMessageCalled = false;
+ }
+ }
+
+ public boolean getOnDiseqcMessageCalled() {
+ try {
+ // yield to let the callback handling execute
+ Thread.sleep(SLEEP_TIME_MS);
+ } catch (Exception e) {
+ // ignore exception
+ }
+
+ synchronized (mDMLock) {
+ mDMCV.block(TIMEOUT_MS);
+ mDMCV.close();
+ return mOnDiseqcMessageCalled;
+ }
+ }
+ }
+
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
@@ -441,6 +524,57 @@
}
@Test
+ public void testLnbAddAndRemoveCallback() throws Exception {
+ TunerTestLnbCallback lnbCB1 = new TunerTestLnbCallback();
+ Lnb lnb = mTuner.openLnb(getExecutor(), lnbCB1);
+ if (lnb == null) {
+ return;
+ }
+
+ assertEquals(lnb.setVoltage(Lnb.VOLTAGE_5V), Tuner.RESULT_SUCCESS);
+ assertEquals(lnb.setTone(Lnb.TONE_NONE), Tuner.RESULT_SUCCESS);
+ assertEquals(
+ lnb.setSatellitePosition(Lnb.POSITION_A), Tuner.RESULT_SUCCESS);
+ lnb.sendDiseqcMessage(new byte[] {1, 2});
+ assertTrue(lnbCB1.getOnDiseqcMessageCalled());
+ lnbCB1.resetOnDiseqcMessageCalled();
+
+ List<Integer> ids = mTuner.getFrontendIds();
+ assertFalse(ids.isEmpty());
+ FrontendInfo info = mTuner.getFrontendInfoById(ids.get(0));
+ FrontendSettings feSettings = createFrontendSettings(info);
+ int res = mTuner.tune(feSettings);
+ assertEquals(Tuner.RESULT_SUCCESS, res);
+
+ // create sharee
+ Tuner sharee = new Tuner(mContext, null, 100);
+ sharee.shareFrontendFromTuner(mTuner);
+ TunerTestLnbCallback lnbCB2 = new TunerTestLnbCallback();
+
+ // add it as sharee
+ lnb.addCallback(lnbCB2, getExecutor());
+
+ // check callback
+ lnb.sendDiseqcMessage(new byte[] {1, 2});
+ assertTrue(lnbCB1.getOnDiseqcMessageCalled());
+ lnbCB1.resetOnDiseqcMessageCalled();
+ assertTrue(lnbCB2.getOnDiseqcMessageCalled());
+ lnbCB2.resetOnDiseqcMessageCalled();
+
+ // remove sharee the sharee (should succeed)
+ assertTrue(lnb.removeCallback(lnbCB2));
+
+ // check callback (only the original owner gets callback
+ lnb.sendDiseqcMessage(new byte[] {1, 2});
+ assertTrue(lnbCB1.getOnDiseqcMessageCalled());
+ lnbCB1.resetOnDiseqcMessageCalled();
+ assertFalse(lnbCB2.getOnDiseqcMessageCalled());
+ lnbCB2.resetOnDiseqcMessageCalled();
+
+ sharee.close();
+ }
+
+ @Test
public void testOpenLnbByname() throws Exception {
Lnb lnb = mTuner.openLnbByName("default", getExecutor(), getLnbCallback());
if (lnb != null) {
@@ -1055,10 +1189,20 @@
Filter.TYPE_TS, Filter.SUBTYPE_SECTION, 1000, getExecutor(), getFilterCallback());
assertNotNull(f);
+ // setup onTuneCallback
+ TunerTestOnTuneEventListener cb100 = new TunerTestOnTuneEventListener();
+ TunerTestOnTuneEventListener cb200 = new TunerTestOnTuneEventListener();
+
// tune again on the owner
info = tuner200.getFrontendInfoById(ids.get(1));
+ tuner100.setOnTuneEventListener(getExecutor(), cb100);
+ tuner200.setOnTuneEventListener(getExecutor(), cb200);
res = tuner200.tune(feSettings);
assertEquals(Tuner.RESULT_SUCCESS, res);
+ assertEquals(OnTuneEventListener.SIGNAL_LOCKED, cb100.getLastTuneEvent());
+ assertEquals(OnTuneEventListener.SIGNAL_LOCKED, cb200.getLastTuneEvent());
+ tuner100.clearOnTuneEventListener();
+ tuner200.clearOnTuneEventListener();
// now let the higher priority tuner steal the resource
Tuner tuner300 = new Tuner(mContext, null, 300);
@@ -1111,6 +1255,207 @@
tuner300.close();
}
+ private void testTransferFeOwnershipSingleTuner() {
+ List<Integer> ids = mTuner.getFrontendIds();
+ if (ids == null) {
+ return;
+ }
+ assertFalse(ids.isEmpty());
+ FrontendInfo info = mTuner.getFrontendInfoById(ids.get(0));
+ FrontendSettings feSettings = createFrontendSettings(info);
+
+ // SCENARIO 1 - transfer and close the previous owner
+
+ // First create a tuner and tune() to acquire frontend resource
+ Tuner tunerA = new Tuner(mContext, null, 100);
+ int res = tunerA.tune(feSettings);
+ assertEquals(Tuner.RESULT_SUCCESS, res);
+
+ // Create another tuner and share frontend from tunerA
+ Tuner tunerB = new Tuner(mContext, null, 500);
+ tunerB.shareFrontendFromTuner(tunerA);
+ DvrRecorder d = tunerB.openDvrRecorder(100, getExecutor(), getRecordListener());
+ assertNotNull(d);
+
+ // Call transferOwner in the wrong configurations and confirm it fails
+ assertEquals(Tuner.RESULT_INVALID_STATE, tunerB.transferOwner(tunerA));
+ Tuner nonSharee = new Tuner(mContext, null, 300);
+ assertEquals(Tuner.RESULT_INVALID_STATE, tunerA.transferOwner(nonSharee));
+ nonSharee.close();
+
+ // Now call it correctly to transfer ownership from tunerA to tunerB
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.transferOwner(tunerB));
+
+ // Close the original owner (tunerA)
+ tunerA.close();
+
+ // Confirm the new owner (tunerB) is still functional
+ assertNotNull(tunerB.getFrontendInfo());
+
+ // Close the new owner (tunerB)
+ d.close();
+ tunerB.close();
+
+ // SCENARIO 2 - transfer and closeFrontend and tune on the previous owner
+
+ // First create a tuner and tune() to acquire frontend resource
+ tunerA = new Tuner(mContext, null, 200);
+ res = tunerA.tune(feSettings);
+ assertEquals(Tuner.RESULT_SUCCESS, res);
+
+ // Create another tuner and share frontend from tunerA
+ tunerB = new Tuner(mContext, null, 100);
+ tunerB.shareFrontendFromTuner(tunerA);
+ assertNotNull(tunerB.getFrontendInfo());
+
+ // Transfer ownership from tunerA to tunerB
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.transferOwner(tunerB));
+
+ // Close frontend for the original owner (tunerA)
+ tunerA.closeFrontend();
+
+ // Confirm tune works without going through Tuner.close() even after transferOwner()
+ // The purpose isn't to get tunerB's frontend revoked, but doing so as singletuner
+ // based test has wider coverage
+ res = tunerA.tune(feSettings); // this should reclaim tunerB
+ assertEquals(Tuner.RESULT_SUCCESS, res);
+
+ // Confirm tuberB is revoked
+ assertNull(tunerB.getFrontendInfo());
+
+ // Close tunerA
+ tunerA.close();
+
+ // close TunerB just in case
+ tunerB.close();
+ }
+
+ private void testTransferFeAndCiCamOwnership() {
+ List<Integer> ids = mTuner.getFrontendIds();
+ assertNotNull(ids);
+ assertFalse(ids.isEmpty());
+ FrontendInfo info = mTuner.getFrontendInfoById(ids.get(0));
+ FrontendSettings feSettings = createFrontendSettings(info);
+
+ // Create tuner and tune to get frontend resource
+ Tuner tunerA = new Tuner(mContext, null, 100);
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.tune(feSettings));
+
+ int ciCamId = 0;
+ boolean linkCiCamToFrontendSupported = false;
+
+ // connect CiCam to Frontend
+ if (TunerVersionChecker.isHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1)) {
+ // TODO: get real CiCam id from MediaCas
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.connectFrontendToCiCam(ciCamId));
+ linkCiCamToFrontendSupported = true;
+ } else {
+ assertEquals(Tuner.INVALID_LTS_ID, tunerA.connectFrontendToCiCam(ciCamId));
+ }
+
+ // connect CiCam to Demux
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.connectCiCam(ciCamId));
+
+ // start another tuner and connect the same CiCam to its own demux
+ Tuner tunerB = new Tuner(mContext, null, 400);
+ tunerB.shareFrontendFromTuner(tunerA);
+ assertNotNull(tunerB.getFrontendInfo());
+ assertEquals(Tuner.RESULT_SUCCESS, tunerB.connectCiCam(ciCamId));
+
+ // unlink CiCam to Demux in tunerA and transfer ownership
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.disconnectCiCam());
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.transferOwner(tunerB));
+
+ // close the original owner
+ tunerA.close();
+
+ // disconnect CiCam from demux
+ assertEquals(Tuner.RESULT_SUCCESS, tunerB.disconnectCiCam());
+
+ // let Tuner.close() handle the release of CiCam
+ tunerB.close();
+
+ // now that the CiCam is released, disconnectFrontendToCiCam() should fail
+ assertEquals(Tuner.RESULT_UNAVAILABLE, tunerB.disconnectFrontendToCiCam(ciCamId));
+
+ // see if tune still works just in case
+ tunerA = new Tuner(mContext, null, 100);
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.tune(feSettings));
+ tunerA.close();
+ }
+
+ private void testTransferFeAndLnbOwnership() {
+ List<Integer> ids = mTuner.getFrontendIds();
+ assertNotNull(ids);
+ assertFalse(ids.isEmpty());
+ FrontendInfo info = mTuner.getFrontendInfoById(ids.get(0));
+ FrontendSettings feSettings = createFrontendSettings(info);
+
+ // Create tuner and tune to acquire frontend resource
+ Tuner tunerA = new Tuner(mContext, null, 100);
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.tune(feSettings));
+
+ // Open Lnb and check the callback
+ TunerTestLnbCallback lnbCB1 = new TunerTestLnbCallback();
+ Lnb lnbA = tunerA.openLnb(getExecutor(), lnbCB1);
+ assertNotNull(lnbA);
+ lnbA.setVoltage(Lnb.VOLTAGE_5V);
+ lnbA.setTone(Lnb.TONE_CONTINUOUS);
+ lnbA.sendDiseqcMessage(new byte[] {1, 2});
+ assertTrue(lnbCB1.getOnDiseqcMessageCalled());
+ lnbCB1.resetOnDiseqcMessageCalled();
+
+ // Create another tuner and share from tunerB
+ Tuner tunerB = new Tuner(mContext, null, 300);
+ tunerB.shareFrontendFromTuner(tunerA);
+
+ // add sharee and check the callback
+ TunerTestLnbCallback lnbCB2 = new TunerTestLnbCallback();
+ lnbA.addCallback(lnbCB2, getExecutor());
+ lnbA.sendDiseqcMessage(new byte[] {1, 2});
+ assertTrue(lnbCB1.getOnDiseqcMessageCalled());
+ lnbCB1.resetOnDiseqcMessageCalled();
+ assertTrue(lnbCB2.getOnDiseqcMessageCalled());
+ lnbCB2.resetOnDiseqcMessageCalled();
+
+ // transfer owner and check callback
+ assertEquals(Tuner.RESULT_SUCCESS, tunerA.transferOwner(tunerB));
+ lnbA.sendDiseqcMessage(new byte[] {1, 2});
+ assertTrue(lnbCB1.getOnDiseqcMessageCalled());
+ lnbCB1.resetOnDiseqcMessageCalled();
+ assertTrue(lnbCB2.getOnDiseqcMessageCalled());
+ lnbCB2.resetOnDiseqcMessageCalled();
+
+ // remove the owner callback (just for testing)
+ assertTrue(lnbA.removeCallback(lnbCB2));
+
+ // remove sharee and check callback
+ assertTrue(lnbA.removeCallback(lnbCB1));
+ lnbA.sendDiseqcMessage(new byte[] {1, 2});
+ assertFalse(lnbCB1.getOnDiseqcMessageCalled());
+ lnbCB1.resetOnDiseqcMessageCalled();
+ assertFalse(lnbCB2.getOnDiseqcMessageCalled());
+ lnbCB2.resetOnDiseqcMessageCalled();
+
+ // close the original owner
+ tunerA.close();
+
+ // confirm the new owner is still intact
+ int[] statusCapabilities = info.getStatusCapabilities();
+ assertNotNull(statusCapabilities);
+ FrontendStatus status = tunerB.getFrontendStatus(statusCapabilities);
+ assertNotNull(status);
+
+ tunerB.close();
+ }
+
+ @Test
+ public void testTransferOwner() throws Exception {
+ testTransferFeOwnershipSingleTuner();
+ testTransferFeAndCiCamOwnership();
+ testTransferFeAndLnbOwnership();
+ }
+
@Test
public void testClose() throws Exception {
Tuner other = new Tuner(mContext, null, 100);
@@ -1171,6 +1516,73 @@
}
@Test
+ public void testCloseFrontend() throws Exception {
+ List<Integer> ids = mTuner.getFrontendIds();
+ if (ids == null) {
+ return;
+ }
+
+ // SCENARIO 1 - without Lnb
+ assertFalse(ids.isEmpty());
+ FrontendInfo info = mTuner.getFrontendInfoById(ids.get(0));
+ FrontendSettings feSettings = createFrontendSettings(info);
+ int res = mTuner.tune(feSettings);
+ assertEquals(Tuner.RESULT_SUCCESS, res);
+ assertNotNull(mTuner.getFrontendInfo());
+
+ // now close frontend
+ mTuner.closeFrontend();
+
+ // confirm frontend is closed
+ int[] statusCapabilities = info.getStatusCapabilities();
+ boolean frontendClosed = false;
+ try {
+ mTuner.getFrontendStatus(statusCapabilities);
+
+ } catch (IllegalStateException e) {
+ frontendClosed = true;
+ }
+ assertTrue(frontendClosed);
+
+ // now tune to a different setting
+ info = mTuner.getFrontendInfoById(ids.get(1));
+ feSettings = createFrontendSettings(info);
+ mTuner.tune(feSettings);
+ assertEquals(Tuner.RESULT_SUCCESS, res);
+ assertNotNull(mTuner.getFrontendInfo());
+ FrontendStatus status = mTuner.getFrontendStatus(statusCapabilities);
+ assertNotNull(status);
+
+ // SCENARIO 2 - with Lnb
+
+ TunerTestLnbCallback lnbCB1 = new TunerTestLnbCallback();
+ Lnb lnb = mTuner.openLnb(getExecutor(), lnbCB1);
+ if (lnb == null) {
+ return;
+ }
+
+ mTuner.closeFrontend();
+ // confirm frontend is closed
+ statusCapabilities = info.getStatusCapabilities();
+ frontendClosed = false;
+ try {
+ mTuner.getFrontendStatus(statusCapabilities);
+
+ } catch (IllegalStateException e) {
+ frontendClosed = true;
+ }
+ assertTrue(frontendClosed);
+
+ info = mTuner.getFrontendInfoById(ids.get(0));
+ feSettings = createFrontendSettings(info);
+ mTuner.tune(feSettings);
+ assertEquals(Tuner.RESULT_SUCCESS, res);
+ assertNotNull(mTuner.getFrontendInfo());
+ status = mTuner.getFrontendStatus(statusCapabilities);
+ assertNotNull(status);
+ }
+
+ @Test
public void testHasUnusedFrontend1() throws Exception {
prepTRMCustomFeResourceMapTest();