aboutsummaryrefslogtreecommitdiff
path: root/tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java
diff options
context:
space:
mode:
Diffstat (limited to 'tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java')
-rw-r--r--tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java274
1 files changed, 274 insertions, 0 deletions
diff --git a/tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java b/tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java
new file mode 100644
index 00000000..73d234e0
--- /dev/null
+++ b/tuner/tests/unittests/javatests/com/android/tv/tuner/FileTunerHal.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.tuner;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import com.android.tv.testing.utils.Utils;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Random;
+
+/** This class simulate the actions happened in TunerHal. */
+public class FileTunerHal extends TunerHal {
+
+ private static final String TAG = "FileTunerHal";
+ private static final int DEVICE_ID = 0;
+ private static final int TS_PACKET_SIZE = 188;
+ // To keep consistent with tunertvinput_jni, which fit Ethernet MTU (1500)
+ private static final int TS_PACKET_COUNT_PER_PAYLOAD = 7;
+ private static final int TS_PAYLOAD_SIZE = TS_PACKET_SIZE * TS_PACKET_COUNT_PER_PAYLOAD;
+ private static final int TS_SYNC_BYTE = 0x47;
+ // Make the read size from file and size in nativeWriteInBuffer same to make read logic simpler.
+ private static final int MIN_READ_UNIT = TS_PACKET_SIZE * TS_PACKET_COUNT_PER_PAYLOAD;
+ private static final long DELAY_IOCTL_SET_FRONTEND = 100;
+ private static final long DELAY_IOCTL_WAIT_FRONTEND_LOCKED = 500;
+
+ // The terrestrial broadcast mode (known as 8-VSB) delivers an MPEG-2 TS at rate
+ // approximately 19.39 Mbps in a 6 MHz channel. (See section #5 of ATSC A/53 Part 2:2011)
+ private static final double TS_READ_BYTES_PER_MS = 19.39 * 1024 * 1024 / (8 * 1000.0);
+ private static final long INITIAL_BUFFERED_TS_BYTES = (long) (TS_READ_BYTES_PER_MS * 150);
+
+ private static boolean sIsDeviceOpen;
+
+ private final SparseBooleanArray mPids = new SparseBooleanArray();
+ private final byte[] mBuffer = new byte[MIN_READ_UNIT];
+ private final File mTestFile;
+ private final Random mGenerator;
+ private RandomAccessFile mAccessFile;
+ private boolean mHasPendingTune;
+ private long mReadStartMs;
+ private long mTotalReadBytes;
+ private long mInitialSkipMs;
+ private boolean mPacketMissing;
+ private boolean mEnableArtificialDelay;
+
+ FileTunerHal(Context context, File testFile) {
+ super(context);
+ mTestFile = testFile;
+ mGenerator = Utils.createTestRandom();
+ }
+
+ /**
+ * Skip Initial parts of the TS file in order to start from specified time position.
+ *
+ * @param initialSkipMs initial position from where TS stream should be provided
+ */
+ void setInitialSkipMs(long initialSkipMs) {
+ mInitialSkipMs = initialSkipMs;
+ }
+
+ @Override
+ protected boolean openFirstAvailable() {
+ sIsDeviceOpen = true;
+ getDeliverySystemTypeFromDevice();
+ return true;
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ protected boolean isDeviceOpen() {
+ return sIsDeviceOpen;
+ }
+
+ @Override
+ protected long getDeviceId() {
+ return DEVICE_ID;
+ }
+
+ @Override
+ protected void nativeFinalize(long deviceId) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mPids.clear();
+ }
+
+ @Override
+ protected boolean nativeTune(
+ long deviceId, int frequency, @ModulationType String modulation, int timeoutMs) {
+ if (deviceId != DEVICE_ID) {
+ return false;
+ }
+ if (mHasPendingTune) {
+ return false;
+ }
+ closeInputStream();
+ openInputStream();
+ if (mAccessFile == null) {
+ return false;
+ }
+
+ // Sleeping to simulate calling FE_GET_INFO and FE_SET_FRONTEND.
+ if (mEnableArtificialDelay) {
+ SystemClock.sleep(DELAY_IOCTL_SET_FRONTEND);
+ }
+ if (mHasPendingTune) {
+ return false;
+ }
+
+ // Sleeping to simulate waiting frontend locked.
+ if (mEnableArtificialDelay) {
+ SystemClock.sleep(DELAY_IOCTL_WAIT_FRONTEND_LOCKED);
+ }
+ if (mHasPendingTune) {
+ return false;
+ }
+ mTotalReadBytes = 0;
+ mReadStartMs = System.currentTimeMillis();
+ return true;
+ }
+
+ @Override
+ protected void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mPids.put(pid, true);
+ }
+
+ @Override
+ protected void nativeCloseAllPidFilters(long deviceId) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mPids.clear();
+ }
+
+ @Override
+ protected void nativeStopTune(long deviceId) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mPids.clear();
+ }
+
+ @Override
+ protected int nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize) {
+ if (deviceId != DEVICE_ID) {
+ return 0;
+ }
+ if (mEnableArtificialDelay) {
+ long estimatedReadBytes =
+ (long) (TS_READ_BYTES_PER_MS * (System.currentTimeMillis() - mReadStartMs))
+ + INITIAL_BUFFERED_TS_BYTES;
+ if (estimatedReadBytes < mTotalReadBytes) {
+ return 0;
+ }
+ }
+ int readSize = readInternal();
+ if (readSize <= 0) {
+ closeInputStream();
+ openInputStream();
+ if (mAccessFile == null) {
+ return -1;
+ }
+ readSize = readInternal();
+ } else {
+ mTotalReadBytes += readSize;
+ }
+
+ if (mBuffer[0] != TS_SYNC_BYTE) {
+ return -1;
+ }
+ int filteredSize = 0;
+ javaBufferSize = (javaBufferSize / TS_PACKET_SIZE) * TS_PACKET_SIZE;
+ javaBufferSize = (javaBufferSize < TS_PAYLOAD_SIZE) ? javaBufferSize : TS_PAYLOAD_SIZE;
+ for (int i = 0, destPos = 0;
+ i < readSize && destPos + TS_PACKET_SIZE <= javaBufferSize;
+ i += TS_PACKET_SIZE) {
+ if (mBuffer[i] == TS_SYNC_BYTE) {
+ int pid = ((mBuffer[i + 1] & 0x1f) << 8) + (mBuffer[i + 2] & 0xff);
+ if (mPids.get(pid)) {
+ System.arraycopy(mBuffer, i, javaBuffer, destPos, TS_PACKET_SIZE);
+ destPos += TS_PACKET_SIZE;
+ filteredSize += TS_PACKET_SIZE;
+ }
+ }
+ }
+ return filteredSize;
+ }
+
+ @Override
+ protected void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune) {
+ if (deviceId != DEVICE_ID) {
+ return;
+ }
+ mHasPendingTune = hasPendingTune;
+ }
+
+ @Override
+ protected int nativeGetDeliverySystemType(long deviceId) {
+ return DELIVERY_SYSTEM_ATSC;
+ }
+
+ private int readInternal() {
+ int readSize;
+ try {
+ if (mPacketMissing) {
+ mAccessFile.skipBytes(
+ mGenerator.nextInt(TS_PACKET_COUNT_PER_PAYLOAD) * TS_PACKET_SIZE);
+ }
+ readSize = mAccessFile.read(mBuffer, 0, mBuffer.length);
+ } catch (IOException e) {
+ return -1;
+ }
+ return readSize;
+ }
+
+ private void closeInputStream() {
+ try {
+ if (mAccessFile != null) {
+ mAccessFile.close();
+ }
+ } catch (IOException e) {
+ }
+ mAccessFile = null;
+ }
+
+ private void openInputStream() {
+ try {
+ mAccessFile = new RandomAccessFile(mTestFile, "r");
+
+ // Since sync frames are located once per 2 seconds, test with various
+ // starting offsets according to mInitialSkipMs.
+ long skipBytes = (long) (mInitialSkipMs * TS_READ_BYTES_PER_MS);
+ skipBytes = skipBytes / TS_PACKET_SIZE * TS_PACKET_SIZE;
+ mAccessFile.seek(skipBytes);
+ } catch (IOException e) {
+ Log.i(TAG, "open input stream failed:" + e);
+ }
+ }
+
+ /** Gets the number of built-in tuner devices. Always 1 in this case. */
+ public static int getNumberOfDevices(Context context) {
+ return 1;
+ }
+
+ public void setEnablePacketMissing(boolean packetMissing) {
+ mPacketMissing = packetMissing;
+ }
+
+ public void setEnableArtificialDelay(boolean enableArtificialDelay) {
+ mEnableArtificialDelay = enableArtificialDelay;
+ }
+}