diff options
Diffstat (limited to 'tuner/src/com/android/tv/tuner')
65 files changed, 871 insertions, 3267 deletions
diff --git a/tuner/src/com/android/tv/tuner/dvb/DvbDeviceAccessor.java b/tuner/src/com/android/tv/tuner/DvbDeviceAccessor.java index 8be27c91..217433d2 100644 --- a/tuner/src/com/android/tv/tuner/dvb/DvbDeviceAccessor.java +++ b/tuner/src/com/android/tv/tuner/DvbDeviceAccessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.tv.tuner.dvb; +package com.android.tv.tuner; import android.content.Context; import android.media.tv.TvInputManager; diff --git a/tuner/src/com/android/tv/tuner/dvb/DvbTunerHal.java b/tuner/src/com/android/tv/tuner/DvbTunerHal.java index 7f68e379..c802ebbb 100644 --- a/tuner/src/com/android/tv/tuner/dvb/DvbTunerHal.java +++ b/tuner/src/com/android/tv/tuner/DvbTunerHal.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package com.android.tv.tuner.dvb; +package com.android.tv.tuner; import android.content.Context; import android.os.ParcelFileDescriptor; import android.util.Log; import com.android.tv.common.compat.TvInputConstantCompat; -import com.android.tv.tuner.TunerHal; -import com.android.tv.tuner.dvb.DvbDeviceAccessor.DvbDeviceInfoWrapper; +import com.android.tv.tuner.DvbDeviceAccessor.DvbDeviceInfoWrapper; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; diff --git a/tuner/src/com/android/tv/tuner/TunerHal.java b/tuner/src/com/android/tv/tuner/TunerHal.java index 3f469d60..dce4f4c4 100644 --- a/tuner/src/com/android/tv/tuner/TunerHal.java +++ b/tuner/src/com/android/tv/tuner/TunerHal.java @@ -36,7 +36,6 @@ public abstract class TunerHal implements Tuner { private static final int DEFAULT_QAM_TUNE_TIMEOUT_MS = 4000; // Some device takes time for @DeliverySystemType private int mDeliverySystemType; - @DeliverySystemType private int[] mDeliverySystemTypes; private boolean mIsStreaming; private int mFrequency; private String mModulation; @@ -58,16 +57,9 @@ public abstract class TunerHal implements Tuner { } protected void getDeliverySystemTypeFromDevice() { - getDeliverySystemTypesFromDevice(); - } - - protected void getDeliverySystemTypesFromDevice() { if (mDeliverySystemType == DELIVERY_SYSTEM_UNDEFINED) { mDeliverySystemType = nativeGetDeliverySystemType(getDeviceId()); } - if (mDeliverySystemTypes == null) { - mDeliverySystemTypes = nativeGetDeliverySystemTypes(getDeviceId()); - } } /** @@ -87,34 +79,18 @@ public abstract class TunerHal implements Tuner { protected native void nativeFinalize(long deviceId); - @Override - public synchronized boolean tune( - int frequency, @ModulationType String modulation, - String channelNumber) { - return tuneInternal(mDeliverySystemType, frequency, modulation, channelNumber); - } - - @Override - public synchronized boolean tune( - int deliverySystemType, int frequency, @ModulationType String modulation, - String channelNumber) { - return tuneInternal(deliverySystemType, frequency, modulation, channelNumber); - } - /** * Sets the tuner channel. This should be called after acquiring a tuner device. * - * @param deliverySystemType a system delivery type of the channel to tune to * @param frequency a frequency of the channel to tune to * @param modulation a modulation method of the channel to tune to * @param channelNumber channel number when channel number is already known. Some tuner HAL may * use channelNumber instead of frequency for tune. * @return {@code true} if the operation was successful, {@code false} otherwise */ - protected boolean tuneInternal( - int deliverySystemType, int frequency, @ModulationType String modulation, - String channelNumber) { - + @Override + public synchronized boolean tune( + int frequency, @ModulationType String modulation, String channelNumber) { if (!isDeviceOpen()) { Log.e(TAG, "There's no available device"); return false; @@ -123,76 +99,40 @@ public abstract class TunerHal implements Tuner { nativeCloseAllPidFilters(getDeviceId()); mIsStreaming = false; } - if (mDeliverySystemTypes != null) { - int i; - for (i = 0; i < mDeliverySystemTypes.length; i++) { - if (deliverySystemType == mDeliverySystemTypes[i]) { - break; - } - } - - if (i == mDeliverySystemTypes.length) { - Log.e(TAG, "Unsupported delivery system type for device"); - return false; - } - } // When tuning to a new channel in the same frequency, there's no need to stop current tuner // device completely and the only thing necessary for tuning is reopening pid filters. if (mFrequency == frequency && Objects.equals(mModulation, modulation)) { addPidFilter(PID_PAT, FILTER_TYPE_OTHER); addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER); - if (Tuner.isDvbDeliverySystem(deliverySystemType)) { + if (Tuner.isDvbDeliverySystem(mDeliverySystemType)) { addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER); addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER); } mIsStreaming = true; return true; } - int timeout_ms = modulation.equals(MODULATION_8VSB) ? DEFAULT_VSB_TUNE_TIMEOUT_MS : DEFAULT_QAM_TUNE_TIMEOUT_MS; - - boolean tuneStatus; - switch(deliverySystemType) { - case DELIVERY_SYSTEM_UNDEFINED: - case DELIVERY_SYSTEM_ATSC: - tuneStatus = nativeTune(getDeviceId(), frequency, modulation, timeout_ms); - break; - case DELIVERY_SYSTEM_DVBT: - case DELIVERY_SYSTEM_DVBT2: - tuneStatus = nativeTune(getDeviceId(), deliverySystemType, frequency, modulation, - timeout_ms); - break; - default: - Log.e(TAG, "Unsupported delivery system type for device"); - return false; - } - - if (tuneStatus == true) { + if (nativeTune(getDeviceId(), frequency, modulation, timeout_ms)) { addPidFilter(PID_PAT, FILTER_TYPE_OTHER); addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER); - if (Tuner.isDvbDeliverySystem(deliverySystemType)) { + if (Tuner.isDvbDeliverySystem(mDeliverySystemType)) { addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER); addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER); } mFrequency = frequency; mModulation = modulation; mIsStreaming = true; + return true; } - - return tuneStatus; + return false; } protected native boolean nativeTune( - long deviceId, int frequency, - @ModulationType String modulation, int timeout_ms); - - protected native boolean nativeTune( - long deviceId, int deliverySystemType, int frequency, - @ModulationType String modulation, int timeout_ms); + long deviceId, int frequency, @ModulationType String modulation, int timeout_ms); /** * Sets a pid filter. This should be set after setting a channel. @@ -222,8 +162,6 @@ public abstract class TunerHal implements Tuner { protected native int nativeGetDeliverySystemType(long deviceId); - protected native int[] nativeGetDeliverySystemTypes(long deviceId); - protected native int nativeGetSignalStrength(long deviceId); /** @@ -253,11 +191,6 @@ public abstract class TunerHal implements Tuner { return mDeliverySystemType; } - @Override - public int[] getDeliverySystemTypes() { - return mDeliverySystemTypes; - } - protected native void nativeStopTune(long deviceId); /** diff --git a/tuner/src/com/android/tv/tuner/api/ScanChannel.java b/tuner/src/com/android/tv/tuner/api/ScanChannel.java index 1c7a6e79..56e5493c 100644 --- a/tuner/src/com/android/tv/tuner/api/ScanChannel.java +++ b/tuner/src/com/android/tv/tuner/api/ScanChannel.java @@ -15,15 +15,11 @@ */ package com.android.tv.tuner.api; -import android.util.Log; -import com.android.tv.tuner.data.Channel; - +import com.android.tv.tuner.data.nano.Channel; /** Channel information gathered from a <em>scan</em> */ public final class ScanChannel { - private static final String TAG = "ScanChannel"; public final int type; - public final Channel.DeliverySystemType deliverySystemType; public final int frequency; public final String modulation; public final String filename; @@ -35,60 +31,25 @@ public final class ScanChannel { public final Integer radioFrequencyNumber; public static ScanChannel forTuner( - String deliverySystemType, int frequency, String modulation, - Integer radioFrequencyNumber) { + int frequency, String modulation, Integer radioFrequencyNumber) { return new ScanChannel( - Channel.TunerType.TYPE_TUNER_VALUE, lookupDeliveryStringToInt(deliverySystemType), - frequency, modulation, null, radioFrequencyNumber); + Channel.TunerType.TYPE_TUNER, frequency, modulation, null, radioFrequencyNumber); } public static ScanChannel forFile(int frequency, String filename) { - return new ScanChannel(Channel.TunerType.TYPE_FILE_VALUE, - Channel.DeliverySystemType.DELIVERY_SYSTEM_UNDEFINED, frequency, "file:", - filename, null); + return new ScanChannel(Channel.TunerType.TYPE_FILE, frequency, "file:", filename, null); } private ScanChannel( int type, - Channel.DeliverySystemType deliverySystemType, int frequency, String modulation, String filename, Integer radioFrequencyNumber) { this.type = type; - this.deliverySystemType = deliverySystemType; this.frequency = frequency; this.modulation = modulation; this.filename = filename; this.radioFrequencyNumber = radioFrequencyNumber; } - - private static Channel.DeliverySystemType lookupDeliveryStringToInt(String deliverySystemType) { - Channel.DeliverySystemType ret; - switch (deliverySystemType) { - case "A": - ret = Channel.DeliverySystemType.DELIVERY_SYSTEM_ATSC; - break; - case "C": - ret = Channel.DeliverySystemType.DELIVERY_SYSTEM_DVBC; - break; - case "S": - ret = Channel.DeliverySystemType.DELIVERY_SYSTEM_DVBS; - break; - case "S2": - ret = Channel.DeliverySystemType.DELIVERY_SYSTEM_DVBS2; - break; - case "T": - ret = Channel.DeliverySystemType.DELIVERY_SYSTEM_DVBT; - break; - case "T2": - ret = Channel.DeliverySystemType.DELIVERY_SYSTEM_DVBT2; - break; - default: - Log.e(TAG, "Unknown delivery system type"); - ret = Channel.DeliverySystemType.DELIVERY_SYSTEM_UNDEFINED; - break; - } - return ret; - } } diff --git a/tuner/src/com/android/tv/tuner/api/Tuner.java b/tuner/src/com/android/tv/tuner/api/Tuner.java index 02df3ca1..6f7e9d94 100644 --- a/tuner/src/com/android/tv/tuner/api/Tuner.java +++ b/tuner/src/com/android/tv/tuner/api/Tuner.java @@ -28,8 +28,6 @@ public interface Tuner extends AutoCloseable { int FILTER_TYPE_VIDEO = 2; int FILTER_TYPE_PCR = 3; String MODULATION_8VSB = "8VSB"; - String MODULATION_QAM16 = "QAM16"; - String MODULATION_QAM64 = "QAM64"; String MODULATION_QAM256 = "QAM256"; int DELIVERY_SYSTEM_UNDEFINED = 0; int DELIVERY_SYSTEM_ATSC = 1; @@ -42,7 +40,6 @@ public interface Tuner extends AutoCloseable { int TUNER_TYPE_USB = 2; int TUNER_TYPE_NETWORK = 3; int BUILT_IN_TUNER_TYPE_LINUX_DVB = 1; - int BUILT_IN_TUNER_TYPE_ARCHER = 100; /** Check a delivery system is for DVB or not. */ static boolean isDvbDeliverySystem(@DeliverySystemType int deliverySystemType) { @@ -69,11 +66,6 @@ public interface Tuner extends AutoCloseable { boolean tune(int frequency, @ModulationType String modulation, String channelNumber); - default boolean tune(@DeliverySystemType int deliverySystemType, int frequency, - @ModulationType String modulation, String channelNumber) { - return tune(frequency, modulation, channelNumber); - } - boolean addPidFilter(int pid, @FilterType int filterType); void stopTune(); @@ -81,10 +73,6 @@ public interface Tuner extends AutoCloseable { void setHasPendingTune(boolean hasPendingTune); int getDeliverySystemType(); - default int[] getDeliverySystemTypes() { - int[] deliverySystemTypes = {DELIVERY_SYSTEM_UNDEFINED}; - return deliverySystemTypes; - }; int readTsStream(byte[] javaBuffer, int javaBufferSize); @@ -96,7 +84,7 @@ public interface Tuner extends AutoCloseable { public @interface FilterType {} /** Modulation Type */ - @StringDef({MODULATION_8VSB, MODULATION_QAM256, MODULATION_QAM16, MODULATION_QAM64}) + @StringDef({MODULATION_8VSB, MODULATION_QAM256}) @Retention(RetentionPolicy.SOURCE) public @interface ModulationType {} @@ -120,7 +108,6 @@ public interface Tuner extends AutoCloseable { /** Built in tuner type */ @IntDef({ - BUILT_IN_TUNER_TYPE_ARCHER, BUILT_IN_TUNER_TYPE_LINUX_DVB }) @Retention(RetentionPolicy.SOURCE) diff --git a/tuner/src/com/android/tv/tuner/dvb/DvbTunerHalFactory.java b/tuner/src/com/android/tv/tuner/builtin/BuiltInTunerHalFactory.java index 24d7e1fc..9a0be740 100644 --- a/tuner/src/com/android/tv/tuner/dvb/DvbTunerHalFactory.java +++ b/tuner/src/com/android/tv/tuner/builtin/BuiltInTunerHalFactory.java @@ -14,29 +14,39 @@ * limitations under the License. */ -package com.android.tv.tuner.dvb; +package com.android.tv.tuner.builtin; import android.content.Context; import android.support.annotation.WorkerThread; import android.util.Log; import android.util.Pair; - +import com.android.tv.common.customization.CustomizationManager; +import com.android.tv.common.feature.Model; +import com.android.tv.tuner.DvbTunerHal; import com.android.tv.tuner.api.Tuner; import com.android.tv.tuner.api.TunerFactory; + /** TunerHal factory that creates all built in tuner types. */ -public final class DvbTunerHalFactory implements TunerFactory { - private static final String TAG = "DvbTunerHalFactory"; +public final class BuiltInTunerHalFactory implements TunerFactory { + private static final String TAG = "BuiltInTunerHalFactory"; private static final boolean DEBUG = false; - private final int mBuiltInTunerType = Tuner.BUILT_IN_TUNER_TYPE_LINUX_DVB; + private Integer mBuiltInTunerType; - public static final TunerFactory INSTANCE = new DvbTunerHalFactory(); + public static final TunerFactory INSTANCE = new BuiltInTunerHalFactory(); - private DvbTunerHalFactory() {} + private BuiltInTunerHalFactory() {} @Tuner.BuiltInTunerType private int getBuiltInTunerType(Context context) { + if (mBuiltInTunerType == null) { + mBuiltInTunerType = 0; + if (CustomizationManager.hasLinuxDvbBuiltInTuner(context) + && DvbTunerHal.getNumberOfDevices(context) > 0) { + mBuiltInTunerType = Tuner.BUILT_IN_TUNER_TYPE_LINUX_DVB; + } + } return mBuiltInTunerType; } @@ -70,6 +80,17 @@ public final class DvbTunerHalFactory implements TunerFactory { @Override @WorkerThread public Pair<Integer, Integer> getTunerTypeAndCount(Context context) { - return Pair.create(Tuner.TUNER_TYPE_BUILT_IN, DvbTunerHal.getNumberOfDevices(context)); + if (useBuiltInTuner(context)) { + if (getBuiltInTunerType(context) == Tuner.BUILT_IN_TUNER_TYPE_LINUX_DVB) { + return new Pair<>( + Tuner.TUNER_TYPE_BUILT_IN, DvbTunerHal.getNumberOfDevices(context)); + } + } else { + int usbTunerCount = DvbTunerHal.getNumberOfDevices(context); + if (usbTunerCount > 0) { + return new Pair<>(Tuner.TUNER_TYPE_USB, usbTunerCount); + } + } + return new Pair<>(null, 0); } } diff --git a/tuner/src/com/android/tv/tuner/cc/CaptionLayout.java b/tuner/src/com/android/tv/tuner/cc/CaptionLayout.java index 62a4e157..eb9ad463 100644 --- a/tuner/src/com/android/tv/tuner/cc/CaptionLayout.java +++ b/tuner/src/com/android/tv/tuner/cc/CaptionLayout.java @@ -18,7 +18,7 @@ package com.android.tv.tuner.cc; import android.content.Context; import android.util.AttributeSet; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import com.android.tv.tuner.layout.ScaledLayout; /** diff --git a/tuner/src/com/android/tv/tuner/cc/CaptionTrackRenderer.java b/tuner/src/com/android/tv/tuner/cc/CaptionTrackRenderer.java index 75776d6a..4a1c7c1b 100644 --- a/tuner/src/com/android/tv/tuner/cc/CaptionTrackRenderer.java +++ b/tuner/src/com/android/tv/tuner/cc/CaptionTrackRenderer.java @@ -27,7 +27,7 @@ import com.android.tv.tuner.data.Cea708Data.CaptionPenLocation; import com.android.tv.tuner.data.Cea708Data.CaptionWindow; import com.android.tv.tuner.data.Cea708Data.CaptionWindowAttr; import com.android.tv.tuner.data.Cea708Parser; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import java.util.ArrayList; /** Decodes and renders CEA-708. */ @@ -89,7 +89,7 @@ public class CaptionTrackRenderer implements Handler.Callback { return; } if (DEBUG) { - Log.d(TAG, "Start captionTrack " + captionTrack.getLanguage()); + Log.d(TAG, "Start captionTrack " + captionTrack.language); } reset(); mCaptionLayout.setCaptionTrack(captionTrack); diff --git a/tuner/src/com/android/tv/tuner/cc/CaptionWindowLayout.java b/tuner/src/com/android/tv/tuner/cc/CaptionWindowLayout.java index 8c699d55..13c6ff47 100644 --- a/tuner/src/com/android/tv/tuner/cc/CaptionWindowLayout.java +++ b/tuner/src/com/android/tv/tuner/cc/CaptionWindowLayout.java @@ -459,14 +459,14 @@ public class CaptionWindowLayout extends RelativeLayout implements View.OnLayout private boolean isKoreanLanguageTrack() { return mCaptionLayout != null && mCaptionLayout.getCaptionTrack() != null - && mCaptionLayout.getCaptionTrack().hasLanguage() - && "KOR".equalsIgnoreCase(mCaptionLayout.getCaptionTrack().getLanguage()); + && mCaptionLayout.getCaptionTrack().language != null + && "KOR".compareToIgnoreCase(mCaptionLayout.getCaptionTrack().language) == 0; } private boolean isWideAspectRatio() { return mCaptionLayout != null && mCaptionLayout.getCaptionTrack() != null - && mCaptionLayout.getCaptionTrack().getWideAspectRatio(); + && mCaptionLayout.getCaptionTrack().wideAspectRatio; } private void updateWidestChar() { diff --git a/tuner/src/com/android/tv/tuner/data/Cea708Parser.java b/tuner/src/com/android/tv/tuner/data/Cea708Parser.java index 7a5538c8..92834b27 100644 --- a/tuner/src/com/android/tv/tuner/data/Cea708Parser.java +++ b/tuner/src/com/android/tv/tuner/data/Cea708Parser.java @@ -138,7 +138,6 @@ public class Cea708Parser { private long mLastDiscoveryLaunchedMs = SystemClock.elapsedRealtime(); private int mCommand = 0; private int mListenServiceNumber = 0; - private int mDtvCcPacketCalculatedSize = 0; private boolean mDtvCcPacking = false; private boolean mFirstServiceNumberDiscovered; @@ -230,7 +229,6 @@ public class Cea708Parser { mBuffer.setLength(0); mDiscoveredNumBytes.clear(); mCommand = 0; - mDtvCcPacketCalculatedSize = 0; mDtvCcPacking = false; } @@ -286,33 +284,29 @@ public class Cea708Parser { for (int i = 0; i < ccPacket.ccCount; ++i) { boolean ccValid = (bytes[pos] & 0x04) != 0; int ccType = bytes[pos] & 0x03; + + // The dtvcc should be considered complete: + // - if either ccValid is set and ccType is 3 + // - or ccValid is clear and ccType is 2 or 3. if (ccValid) { - // The dtvcc should be considered complete: - // if ccType is 3 or if the packet size is reached. if (ccType == CC_TYPE_DTVCC_PACKET_START) { if (mDtvCcPacking) { parseDtvCcPacket(mDtvCcPacket.buffer(), mDtvCcPacket.length()); mDtvCcPacket.clear(); - mDtvCcPacketCalculatedSize = 0; } mDtvCcPacking = true; - int packetSize = bytes[pos + 1] & 0x3F; // last 6 bits - if (packetSize == 0) { - packetSize = DTVCC_MAX_PACKET_SIZE; - } - mDtvCcPacketCalculatedSize = packetSize * DTVCC_PACKET_SIZE_SCALE_FACTOR; mDtvCcPacket.append(bytes[pos + 1]); mDtvCcPacket.append(bytes[pos + 2]); } else if (mDtvCcPacking && ccType == CC_TYPE_DTVCC_PACKET_DATA) { mDtvCcPacket.append(bytes[pos + 1]); mDtvCcPacket.append(bytes[pos + 2]); } + } else { if ((ccType == CC_TYPE_DTVCC_PACKET_START || ccType == CC_TYPE_DTVCC_PACKET_DATA) - && mDtvCcPacking && mDtvCcPacket.length() == mDtvCcPacketCalculatedSize) { + && mDtvCcPacking) { mDtvCcPacking = false; parseDtvCcPacket(mDtvCcPacket.buffer(), mDtvCcPacket.length()); mDtvCcPacket.clear(); - mDtvCcPacketCalculatedSize = 0; } } pos += 3; diff --git a/tuner/src/com/android/tv/tuner/data/PsiData.java b/tuner/src/com/android/tv/tuner/data/PsiData.java index 74f16035..9b7c2e2c 100644 --- a/tuner/src/com/android/tv/tuner/data/PsiData.java +++ b/tuner/src/com/android/tv/tuner/data/PsiData.java @@ -16,8 +16,8 @@ package com.android.tv.tuner.data; -import com.android.tv.tuner.data.Track.AtscAudioTrack; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; +import com.android.tv.tuner.data.nano.Track.AtscAudioTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import java.util.List; /** Collection of MPEG PSI table items. */ diff --git a/tuner/src/com/android/tv/tuner/data/PsipData.java b/tuner/src/com/android/tv/tuner/data/PsipData.java index 108ce3f2..d4af0934 100644 --- a/tuner/src/com/android/tv/tuner/data/PsipData.java +++ b/tuner/src/com/android/tv/tuner/data/PsipData.java @@ -20,8 +20,8 @@ import android.support.annotation.NonNull; import android.text.TextUtils; import android.text.format.DateUtils; import com.android.tv.common.util.StringUtils; -import com.android.tv.tuner.data.Track.AtscAudioTrack; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; +import com.android.tv.tuner.data.nano.Track.AtscAudioTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import com.android.tv.tuner.util.ConvertUtils; import java.util.ArrayList; import java.util.HashMap; @@ -495,10 +495,10 @@ public class PsipData { public String toString() { return String.format( Locale.US, - "AC3 audio stream sampleRateCode: %d, bsid: %d, bitRateCode: %d, surroundMode:" - + " %d, bsmod: %d, numChannels: %d, fullSvc: %s, langCod: %d, langCod2:" - + " %d, mainId: %d, priority: %d, avcflags: %d, text: %s, language: %s," - + " language2: %s", + "AC3 audio stream sampleRateCode: %d, bsid: %d, bitRateCode: %d, " + + "surroundMode: %d, bsmod: %d, numChannels: %d, fullSvc: %s, langCod: %d, " + + "langCod2: %d, mainId: %d, priority: %d, avcflags: %d, text: %s, language: %s" + + ", language2: %s", mSampleRateCode, mBsid, mBitRateCode, @@ -832,7 +832,7 @@ public class PsipData { } ArrayList<String> languages = new ArrayList<>(); for (AtscAudioTrack audioTrack : mAudioTracks) { - languages.add(audioTrack.getLanguage()); + languages.add(audioTrack.language); } return TextUtils.join(",", languages); } diff --git a/tuner/src/com/android/tv/tuner/data/SectionParser.java b/tuner/src/com/android/tv/tuner/data/SectionParser.java index 3c16749e..d3dba6ba 100644 --- a/tuner/src/com/android/tv/tuner/data/SectionParser.java +++ b/tuner/src/com/android/tv/tuner/data/SectionParser.java @@ -24,8 +24,7 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; -import com.android.tv.common.feature.Model; -import com.android.tv.tuner.data.Channel.AtscServiceType; + import com.android.tv.tuner.data.PsiData.PatItem; import com.android.tv.tuner.data.PsiData.PmtItem; import com.android.tv.tuner.data.PsipData.Ac3AudioDescriptor; @@ -46,8 +45,9 @@ import com.android.tv.tuner.data.PsipData.ServiceDescriptor; import com.android.tv.tuner.data.PsipData.ShortEventDescriptor; import com.android.tv.tuner.data.PsipData.TsDescriptor; import com.android.tv.tuner.data.PsipData.VctItem; -import com.android.tv.tuner.data.Track.AtscAudioTrack; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; +import com.android.tv.tuner.data.nano.Channel; +import com.android.tv.tuner.data.nano.Track.AtscAudioTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import com.android.tv.tuner.util.ByteArrayBuffer; import com.android.tv.tuner.util.ConvertUtils; import java.io.UnsupportedEncodingException; @@ -105,7 +105,7 @@ public class SectionParser { private static final int RATING_REGION_US_TV = 1; private static final int RATING_REGION_KR_TV = 4; - // The following values are defined in the TV app. + // The following values are defined in the live channels app. // See https://developer.android.com/reference/android/media/tv/TvContentRating.html. private static final String RATING_DOMAIN = "com.android.tv"; private static final String RATING_REGION_RATING_SYSTEM_US_TV = "US_TV"; @@ -916,8 +916,8 @@ public class SectionParser { Log.d( TAG, String.format( - "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d" - + " channel: %d-%d encrypted: %b hidden: %b, descriptors: %d", + "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d " + + "channel: %d-%d encrypted: %b hidden: %b, descriptors: %d", shortName, longName, serviceType, @@ -929,14 +929,14 @@ public class SectionParser { hidden, descriptors.size())); } - if ((serviceType == AtscServiceType.SERVICE_TYPE_ATSC_AUDIO_VALUE + if (!accessControlled + && !hidden + && (serviceType == Channel.AtscServiceType.SERVICE_TYPE_ATSC_AUDIO || serviceType - == AtscServiceType.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION_VALUE + == Channel.AtscServiceType.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION || serviceType - == AtscServiceType - .SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE_VALUE) - && !accessControlled - && !hidden) { + == Channel.AtscServiceType + .SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE)) { // Hide hidden, encrypted, or unsupported ATSC service type channels results.add( new VctItem( @@ -1212,20 +1212,16 @@ public class SectionParser { for (TsDescriptor descriptor : descriptors) { if (descriptor instanceof Ac3AudioDescriptor) { Ac3AudioDescriptor audioDescriptor = (Ac3AudioDescriptor) descriptor; - String language = null; + AtscAudioTrack audioTrack = new AtscAudioTrack(); if (audioDescriptor.getLanguage() != null) { - language = audioDescriptor.getLanguage(); + audioTrack.language = audioDescriptor.getLanguage(); } - if (language == null) { - language = ""; + if (audioTrack.language == null) { + audioTrack.language = ""; } - AtscAudioTrack audioTrack = - AtscAudioTrack.newBuilder() - .setLanguage(language) - .setAudioType(AtscAudioTrack.AudioType.AUDIOTYPE_UNDEFINED) - .setChannelCount(audioDescriptor.getNumChannels()) - .setSampleRate(audioDescriptor.getSampleRate()) - .build(); + audioTrack.audioType = AtscAudioTrack.AudioType.AUDIOTYPE_UNDEFINED; + audioTrack.channelCount = audioDescriptor.getNumChannels(); + audioTrack.sampleRate = audioDescriptor.getSampleRate(); ac3Tracks.add(audioTrack); } } @@ -1258,27 +1254,26 @@ public class SectionParser { } int size = Math.max(ac3Tracks.size(), iso639LanguageTracks.size()); for (int i = 0; i < size; ++i) { - AtscAudioTrack.Builder audioTrack = null; + AtscAudioTrack audioTrack = null; if (i < ac3Tracks.size()) { - audioTrack = ac3Tracks.get(i).toBuilder(); + audioTrack = ac3Tracks.get(i); } if (i < iso639LanguageTracks.size()) { if (audioTrack == null) { - audioTrack = iso639LanguageTracks.get(i).toBuilder(); + audioTrack = iso639LanguageTracks.get(i); } else { AtscAudioTrack iso639LanguageTrack = iso639LanguageTracks.get(i); - if (!audioTrack.hasLanguage() - || TextUtils.equals(audioTrack.getLanguage(), "")) { - audioTrack.setLanguage(iso639LanguageTrack.getLanguage()); + if (audioTrack.language == null || TextUtils.equals(audioTrack.language, "")) { + audioTrack.language = iso639LanguageTrack.language; } - audioTrack.setAudioType(iso639LanguageTrack.getAudioType()); + audioTrack.audioType = iso639LanguageTrack.audioType; } } - String language = ISO_LANGUAGE_CODE_MAP.get(audioTrack.getLanguage()); + String language = ISO_LANGUAGE_CODE_MAP.get(audioTrack.language); if (language != null) { - audioTrack = audioTrack.setLanguage(language); + audioTrack.language = language; } - tracks.add(audioTrack.build()); + tracks.add(audioTrack); } return tracks; } @@ -1597,16 +1592,10 @@ public class SectionParser { return null; } String language = new String(data, pos, 3); - int audioTypeInt = data[pos + 3] & 0xff; - AtscAudioTrack.AudioType audioType = AtscAudioTrack.AudioType.forNumber(audioTypeInt); - if (audioType == null) { - audioType = AtscAudioTrack.AudioType.AUDIOTYPE_UNDEFINED; - } - AtscAudioTrack audioTrack = - AtscAudioTrack.newBuilder() - .setLanguage(language) - .setAudioType(audioType) - .build(); + int audioType = data[pos + 3] & 0xff; + AtscAudioTrack audioTrack = new AtscAudioTrack(); + audioTrack.language = language; + audioTrack.audioType = audioType; audioTracks.add(audioTrack); pos += 4; } @@ -1645,13 +1634,11 @@ public class SectionParser { reserved[0] |= (byte) ((data[pos + 1] & 0xc0) >>> 6); reserved[1] = (byte) ((data[pos + 1] & 0x3f) << 2); pos += 2; - AtscCaptionTrack captionTrack = - AtscCaptionTrack.newBuilder() - .setLanguage(language) - .setServiceNumber(captionServiceNumber) - .setEasyReader(easyReader) - .setWideAspectRatio(wideAspectRatio) - .build(); + AtscCaptionTrack captionTrack = new AtscCaptionTrack(); + captionTrack.language = language; + captionTrack.serviceNumber = captionServiceNumber; + captionTrack.easyReader = easyReader; + captionTrack.wideAspectRatio = wideAspectRatio; services.add(captionTrack); } return new CaptionServiceDescriptor(services); @@ -2088,11 +2075,6 @@ public class SectionParser { } private static boolean checkSanity(byte[] data) { - // Skipping CRC checking on Archer since TS data here was modified without updating CRC - // value. For details, see b/28616908. - if (Model.ARCHER.isEnabled()) { - return true; - } if (data.length <= 1) { return false; } diff --git a/tuner/src/com/android/tv/tuner/data/TunerChannel.java b/tuner/src/com/android/tv/tuner/data/TunerChannel.java index 5872cd55..d20c343b 100644 --- a/tuner/src/com/android/tv/tuner/data/TunerChannel.java +++ b/tuner/src/com/android/tv/tuner/data/TunerChannel.java @@ -20,10 +20,12 @@ import android.database.Cursor; import android.support.annotation.NonNull; import android.util.Log; import com.android.tv.common.util.StringUtils; -import com.android.tv.tuner.data.Channel.DeliverySystemType; -import com.android.tv.tuner.data.Channel.TunerChannelProto; -import com.android.tv.tuner.data.Track.AtscAudioTrack; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; +import com.android.tv.tuner.data.nano.Channel; +import com.android.tv.tuner.data.nano.Channel.TunerChannelProto; +import com.android.tv.tuner.data.nano.Track.AtscAudioTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; +import com.android.tv.tuner.util.Ints; +import com.google.protobuf.nano.MessageNano; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -53,7 +55,7 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks "Extended Parameterized Service" }; private static final String ATSC_SERVICE_TYPE_NAME_RESERVED = - ATSC_SERVICE_TYPE_NAMES[Channel.AtscServiceType.SERVICE_TYPE_ATSC_RESERVED_VALUE]; + ATSC_SERVICE_TYPE_NAMES[Channel.AtscServiceType.SERVICE_TYPE_ATSC_RESERVED]; public static final int INVALID_FREQUENCY = -1; @@ -64,128 +66,93 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks public static final int INVALID_STREAMTYPE = -1; // @GuardedBy(this) Writing operations and toByteArray will be guarded. b/34197766 - private TunerChannelProto mProto; + private final TunerChannelProto mProto; private TunerChannel( - PsipData.VctItem channel, - int programNumber, - List<PsiData.PmtItem> pmtItems, - Channel.TunerType type) { - String shortName = ""; - String longName = ""; - String description = ""; - int tsid = 0; - int virtualMajor = 0; - int virtualMinor = 0; - Channel.AtscServiceType serviceType = - Channel.AtscServiceType.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION; - if (channel != null) { - shortName = channel.getShortName(); - tsid = channel.getChannelTsid(); - programNumber = channel.getProgramNumber(); - virtualMajor = channel.getMajorChannelNumber(); - virtualMinor = channel.getMinorChannelNumber(); - Channel.AtscServiceType chanServiceType = - Channel.AtscServiceType.forNumber(channel.getServiceType()); - if (chanServiceType != null) { - serviceType = chanServiceType; + PsipData.VctItem channel, int programNumber, List<PsiData.PmtItem> pmtItems, int type) { + mProto = new TunerChannelProto(); + if (channel == null) { + mProto.shortName = ""; + mProto.tsid = 0; + mProto.programNumber = programNumber; + mProto.virtualMajor = 0; + mProto.virtualMinor = 0; + } else { + mProto.shortName = channel.getShortName(); + if (channel.getLongName() != null) { + mProto.longName = channel.getLongName(); } - longName = (channel.getLongName() != null ? channel.getLongName() : longName); - description = - (channel.getDescription() != null ? channel.getDescription() : description); + mProto.tsid = channel.getChannelTsid(); + mProto.programNumber = channel.getProgramNumber(); + mProto.virtualMajor = channel.getMajorChannelNumber(); + mProto.virtualMinor = channel.getMinorChannelNumber(); + if (channel.getDescription() != null) { + mProto.description = channel.getDescription(); + } + mProto.serviceType = channel.getServiceType(); } - TunerChannelProto tunerChannelProto = - TunerChannelProto.newBuilder() - .setShortName(shortName) - .setTsid(tsid) - .setProgramNumber(programNumber) - .setVirtualMajor(virtualMajor) - .setVirtualMinor(virtualMinor) - .setServiceType(serviceType) - .setLongName(longName) - .setDescription(description) - .build(); - initProto(pmtItems, type, tunerChannelProto); - } - - private void initProto( - List<PsiData.PmtItem> pmtItems, - Channel.TunerType type, - TunerChannelProto tunerChannelProto) { - int videoPid = INVALID_PID; - int pcrPid = 0; - Channel.VideoStreamType videoStreamType = Channel.VideoStreamType.UNSET; + initProto(pmtItems, type); + } + + private void initProto(List<PsiData.PmtItem> pmtItems, int type) { + mProto.type = type; + mProto.channelId = -1L; + mProto.frequency = INVALID_FREQUENCY; + mProto.videoPid = INVALID_PID; + mProto.videoStreamType = INVALID_STREAMTYPE; List<Integer> audioPids = new ArrayList<>(); - List<Channel.AudioStreamType> audioStreamTypes = new ArrayList<>(); + List<Integer> audioStreamTypes = new ArrayList<>(); for (PsiData.PmtItem pmt : pmtItems) { switch (pmt.getStreamType()) { // MPEG ES stream video types - case Channel.VideoStreamType.MPEG1_VALUE: - case Channel.VideoStreamType.MPEG2_VALUE: - case Channel.VideoStreamType.H263_VALUE: - case Channel.VideoStreamType.H264_VALUE: - case Channel.VideoStreamType.H265_VALUE: - videoPid = pmt.getEsPid(); - videoStreamType = Channel.VideoStreamType.forNumber(pmt.getStreamType()); + case Channel.VideoStreamType.MPEG1: + case Channel.VideoStreamType.MPEG2: + case Channel.VideoStreamType.H263: + case Channel.VideoStreamType.H264: + case Channel.VideoStreamType.H265: + mProto.videoPid = pmt.getEsPid(); + mProto.videoStreamType = pmt.getStreamType(); break; // MPEG ES stream audio types - case Channel.AudioStreamType.MPEG1AUDIO_VALUE: - case Channel.AudioStreamType.MPEG2AUDIO_VALUE: - case Channel.AudioStreamType.MPEG2AACAUDIO_VALUE: - case Channel.AudioStreamType.MPEG4LATMAACAUDIO_VALUE: - case Channel.AudioStreamType.A52AC3AUDIO_VALUE: - case Channel.AudioStreamType.EAC3AUDIO_VALUE: + case Channel.AudioStreamType.MPEG1AUDIO: + case Channel.AudioStreamType.MPEG2AUDIO: + case Channel.AudioStreamType.MPEG2AACAUDIO: + case Channel.AudioStreamType.MPEG4LATMAACAUDIO: + case Channel.AudioStreamType.A52AC3AUDIO: + case Channel.AudioStreamType.EAC3AUDIO: audioPids.add(pmt.getEsPid()); - audioStreamTypes.add(Channel.AudioStreamType.forNumber(pmt.getStreamType())); + audioStreamTypes.add(pmt.getStreamType()); break; // Non MPEG ES stream types case 0x100: // PmtItem.ES_PID_PCR: - pcrPid = pmt.getEsPid(); + mProto.pcrPid = pmt.getEsPid(); break; default: // fall out } } - mProto = - TunerChannelProto.newBuilder(tunerChannelProto) - .setType(type) - .setChannelId(-1L) - .setFrequency(INVALID_FREQUENCY) - .setVideoPid(videoPid) - .setVideoStreamType(videoStreamType) - .addAllAudioPids(audioPids) - .setAudioTrackIndex(audioPids.isEmpty() ? -1 : 0) - .addAllAudioStreamTypes(audioStreamTypes) - .setPcrPid(pcrPid) - .build(); + mProto.audioPids = Ints.toArray(audioPids); + mProto.audioStreamTypes = Ints.toArray(audioStreamTypes); + mProto.audioTrackIndex = (audioPids.size() > 0) ? 0 : -1; } private TunerChannel( - int programNumber, - Channel.TunerType type, - PsipData.SdtItem channel, - List<PsiData.PmtItem> pmtItems) { - String shortName = ""; - Channel.AtscServiceType serviceType = - Channel.AtscServiceType.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION; - if (channel != null) { - shortName = channel.getServiceName(); - programNumber = channel.getServiceId(); - Channel.AtscServiceType chanServiceType = - Channel.AtscServiceType.forNumber(channel.getServiceType()); - if (chanServiceType != null) { - serviceType = chanServiceType; - } + int programNumber, int type, PsipData.SdtItem channel, List<PsiData.PmtItem> pmtItems) { + mProto = new TunerChannelProto(); + mProto.tsid = 0; + mProto.virtualMajor = 0; + mProto.virtualMinor = 0; + if (channel == null) { + mProto.shortName = ""; + mProto.programNumber = programNumber; + } else { + mProto.shortName = channel.getServiceName(); + mProto.programNumber = channel.getServiceId(); + mProto.serviceType = channel.getServiceType(); } - TunerChannelProto tunerChannelProto = - TunerChannelProto.newBuilder() - .setShortName(shortName) - .setProgramNumber(programNumber) - .setServiceType(serviceType) - .build(); - initProto(pmtItems, type, tunerChannelProto); + initProto(pmtItems, type); } /** Initialize tuner channel with VCT items and PMT items. */ @@ -266,23 +233,23 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks } public String getName() { - return !mProto.getShortName().isEmpty() ? mProto.getShortName() : mProto.getLongName(); + return (!mProto.shortName.isEmpty()) ? mProto.shortName : mProto.longName; } public String getShortName() { - return mProto.getShortName(); + return mProto.shortName; } public int getProgramNumber() { - return mProto.getProgramNumber(); + return mProto.programNumber; } public int getServiceType() { - return mProto.getServiceType().getNumber(); + return mProto.serviceType; } public String getServiceTypeName() { - int serviceType = getServiceType(); + int serviceType = mProto.serviceType; if (serviceType >= 0 && serviceType < ATSC_SERVICE_TYPE_NAMES.length) { return ATSC_SERVICE_TYPE_NAMES[serviceType]; } @@ -290,129 +257,105 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks } public int getVirtualMajor() { - return mProto.getVirtualMajor(); + return mProto.virtualMajor; } public int getVirtualMinor() { - return mProto.getVirtualMinor(); - } - - public DeliverySystemType getDeliverySystemType() { - return mProto.getDeliverySystemType(); + return mProto.virtualMinor; } public int getFrequency() { - return mProto.getFrequency(); + return mProto.frequency; } public String getModulation() { - return mProto.getModulation(); + return mProto.modulation; } public int getTsid() { - return mProto.getTsid(); + return mProto.tsid; } public int getVideoPid() { - return mProto.getVideoPid(); + return mProto.videoPid; } public synchronized void setVideoPid(int videoPid) { - mProto = mProto.toBuilder().setVideoPid(videoPid).build(); + mProto.videoPid = videoPid; } public int getVideoStreamType() { - return mProto.getVideoStreamType().getNumber(); + return mProto.videoStreamType; } public int getAudioPid() { - if (!mProto.hasAudioTrackIndex() || mProto.getAudioTrackIndex() == -1) { + if (mProto.audioTrackIndex == -1) { return INVALID_PID; } - return mProto.getAudioPids(mProto.getAudioTrackIndex()); + return mProto.audioPids[mProto.audioTrackIndex]; } public int getAudioStreamType() { - if (!mProto.hasAudioTrackIndex() || mProto.getAudioTrackIndex() == -1) { + if (mProto.audioTrackIndex == -1) { return INVALID_STREAMTYPE; } - return mProto.getAudioStreamTypes(mProto.getAudioTrackIndex()).getNumber(); + return mProto.audioStreamTypes[mProto.audioTrackIndex]; } public List<Integer> getAudioPids() { - return mProto.getAudioPidsList(); + return Ints.asList(mProto.audioPids); } public synchronized void setAudioPids(List<Integer> audioPids) { - mProto = mProto.toBuilder().clearAudioPids().addAllAudioPids(audioPids).build(); + mProto.audioPids = Ints.toArray(audioPids); } public List<Integer> getAudioStreamTypes() { - List<Channel.AudioStreamType> audioStreamTypes = mProto.getAudioStreamTypesList(); - List<Integer> audioStreamTypesValues = new ArrayList<>(audioStreamTypes.size()); - - for (Channel.AudioStreamType audioStreamType : audioStreamTypes) { - audioStreamTypesValues.add(audioStreamType.getNumber()); - } - return audioStreamTypesValues; + return Ints.asList(mProto.audioStreamTypes); } - public synchronized void setAudioStreamTypes(List<Integer> audioStreamTypesValues) { - List<Channel.AudioStreamType> audioStreamTypes = - new ArrayList<>(audioStreamTypesValues.size()); - - for (Integer audioStreamTypesValue : audioStreamTypesValues) { - audioStreamTypes.add(Channel.AudioStreamType.forNumber(audioStreamTypesValue)); - } - mProto = - mProto.toBuilder() - .clearAudioStreamTypes() - .addAllAudioStreamTypes(audioStreamTypes) - .build(); + public synchronized void setAudioStreamTypes(List<Integer> audioStreamTypes) { + mProto.audioStreamTypes = Ints.toArray(audioStreamTypes); } public int getPcrPid() { - return mProto.getPcrPid(); + return mProto.pcrPid; } - public Channel.TunerType getType() { - return mProto.getType(); + public int getType() { + return mProto.type; } public synchronized void setFilepath(String filepath) { - mProto = mProto.toBuilder().setFilepath(filepath == null ? "" : filepath).build(); + mProto.filepath = filepath == null ? "" : filepath; } public String getFilepath() { - return mProto.getFilepath(); + return mProto.filepath; } public synchronized void setVirtualMajor(int virtualMajor) { - mProto = mProto.toBuilder().setVirtualMajor(virtualMajor).build(); + mProto.virtualMajor = virtualMajor; } public synchronized void setVirtualMinor(int virtualMinor) { - mProto = mProto.toBuilder().setVirtualMinor(virtualMinor).build(); + mProto.virtualMinor = virtualMinor; } public synchronized void setShortName(String shortName) { - mProto = mProto.toBuilder().setShortName(shortName == null ? "" : shortName).build(); - } - - public synchronized void setDeliverySystemType(DeliverySystemType deliverySystemType) { - mProto = mProto.toBuilder().setDeliverySystemType(deliverySystemType).build(); + mProto.shortName = shortName == null ? "" : shortName; } public synchronized void setFrequency(int frequency) { - mProto = mProto.toBuilder().setFrequency(frequency).build(); + mProto.frequency = frequency; } public synchronized void setModulation(String modulation) { - mProto = mProto.toBuilder().setModulation(modulation == null ? "" : modulation).build(); + mProto.modulation = modulation == null ? "" : modulation; } public boolean hasVideo() { - return mProto.hasVideoPid() && mProto.getVideoPid() != INVALID_PID; + return mProto.videoPid != INVALID_PID; } public boolean hasAudio() { @@ -420,11 +363,11 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks } public long getChannelId() { - return mProto.getChannelId(); + return mProto.channelId; } public synchronized void setChannelId(long channelId) { - mProto = mProto.toBuilder().setChannelId(channelId).build(); + mProto.channelId = channelId; } /** @@ -436,11 +379,11 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks * href="https://developer.android.com/reference/android/media/tv/TvContract.Channels.html#COLUMN_LOCKED">link</a> */ public boolean isLocked() { - return mProto.getLocked(); + return mProto.locked; } public synchronized void setLocked(boolean locked) { - mProto = mProto.toBuilder().setLocked(locked).build(); + mProto.locked = locked; } public String getDisplayNumber() { @@ -448,91 +391,92 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks } public String getDisplayNumber(boolean ignoreZeroMinorNumber) { - if (getVirtualMajor() != 0 && (getVirtualMinor() != 0 || !ignoreZeroMinorNumber)) { + if (mProto.virtualMajor != 0 && (mProto.virtualMinor != 0 || !ignoreZeroMinorNumber)) { return String.format( - "%d%c%d", getVirtualMajor(), CHANNEL_NUMBER_SEPARATOR, getVirtualMinor()); - } else if (getVirtualMajor() != 0) { - return Integer.toString(getVirtualMajor()); + "%d%c%d", mProto.virtualMajor, CHANNEL_NUMBER_SEPARATOR, mProto.virtualMinor); + } else if (mProto.virtualMajor != 0) { + return Integer.toString(mProto.virtualMajor); } else { - return Integer.toString(getProgramNumber()); + return Integer.toString(mProto.programNumber); } } public String getDescription() { - return mProto.getDescription(); + return mProto.description; } @Override public synchronized void setHasCaptionTrack() { - mProto = mProto.toBuilder().setHasCaptionTrack(true).build(); + mProto.hasCaptionTrack = true; } @Override public boolean hasCaptionTrack() { - return mProto.getHasCaptionTrack(); + return mProto.hasCaptionTrack; } @Override public List<AtscAudioTrack> getAudioTracks() { - return mProto.getAudioTracksList(); + return Collections.unmodifiableList(Arrays.asList(mProto.audioTracks)); } public synchronized void setAudioTracks(List<AtscAudioTrack> audioTracks) { - mProto = mProto.toBuilder().clearAudioTracks().addAllAudioTracks(audioTracks).build(); + mProto.audioTracks = audioTracks.toArray(new AtscAudioTrack[audioTracks.size()]); } @Override public List<AtscCaptionTrack> getCaptionTracks() { - return mProto.getCaptionTracksList(); + return Collections.unmodifiableList(Arrays.asList(mProto.captionTracks)); } public synchronized void setCaptionTracks(List<AtscCaptionTrack> captionTracks) { - mProto = mProto.toBuilder().clearCaptionTracks().addAllCaptionTracks(captionTracks).build(); + mProto.captionTracks = captionTracks.toArray(new AtscCaptionTrack[captionTracks.size()]); } public synchronized void selectAudioTrack(int index) { - if (index < 0 || index >= mProto.getAudioPidsCount()) { - index = -1; + if (0 <= index && index < mProto.audioPids.length) { + mProto.audioTrackIndex = index; + } else { + mProto.audioTrackIndex = -1; } - mProto = mProto.toBuilder().setAudioTrackIndex(index).build(); } public synchronized void setRecordingProhibited(boolean recordingProhibited) { - mProto = mProto.toBuilder().setRecordingProhibited(recordingProhibited).build(); + mProto.recordingProhibited = recordingProhibited; } public boolean isRecordingProhibited() { - return mProto.getRecordingProhibited(); + return mProto.recordingProhibited; } public synchronized void setVideoFormat(String videoFormat) { - mProto = mProto.toBuilder().setVideoFormat(videoFormat == null ? "" : videoFormat).build(); + mProto.videoFormat = videoFormat == null ? "" : videoFormat; } public String getVideoFormat() { - return mProto.getVideoFormat(); + return mProto.videoFormat; } @Override public String toString() { - switch (getType()) { - case TYPE_FILE: + switch (mProto.type) { + case Channel.TunerType.TYPE_FILE: return String.format( "{%d-%d %s} Filepath: %s, ProgramNumber %d", - getVirtualMajor(), - getVirtualMinor(), - getShortName(), - getFilepath(), - getProgramNumber()); + mProto.virtualMajor, + mProto.virtualMinor, + mProto.shortName, + mProto.filepath, + mProto.programNumber); // case Channel.TunerType.TYPE_TUNER: default: return String.format( "{%d-%d %s} Frequency: %d, ProgramNumber %d", - getVirtualMajor(), - getVirtualMinor(), - getShortName(), - getFrequency(), - getProgramNumber()); + mProto.virtualMajor, + mProto.virtualMinor, + mProto.shortName, + mProto.frequency, + mProto.programNumber); } } @@ -551,9 +495,6 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks if (ret != 0) { return ret; } - if (getDeliverySystemType() != channel.getDeliverySystemType()) { - return 1; - } // For FileTsStreamer, file paths should be compared. return StringUtils.compare(getFilepath(), channel.getFilepath()); } @@ -568,21 +509,20 @@ public class TunerChannel implements Comparable<TunerChannel>, PsipData.TvTracks @Override public int hashCode() { - return Objects.hash(getDeliverySystemType(), getFrequency(), getProgramNumber(), getName(), - getFilepath()); + return Objects.hash(getFrequency(), getProgramNumber(), getName(), getFilepath()); } // Serialization public synchronized byte[] toByteArray() { try { - return mProto.toByteArray(); + return MessageNano.toByteArray(mProto); } catch (Exception e) { // Retry toByteArray. b/34197766 Log.w( TAG, "TunerChannel or its variables are modified in multiple thread without lock", e); - return mProto.toByteArray(); + return MessageNano.toByteArray(mProto); } } diff --git a/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java index 2a22db17..e48cb03c 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/ExoPlayerSampleExtractor.java @@ -26,32 +26,32 @@ import android.os.SystemClock; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.util.Pair; - import com.android.tv.tuner.exoplayer.audio.MpegTsDefaultAudioTrackRenderer; import com.android.tv.tuner.exoplayer.buffer.BufferManager; import com.android.tv.tuner.exoplayer.buffer.PlaybackBufferListener; import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer; import com.android.tv.tuner.exoplayer.buffer.SimpleSampleBuffer; - import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.SampleHolder; +import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; +import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.source.ExtractorMediaSource.EventListener; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.trackselection.FixedTrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DefaultAllocator; -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; - +import com.google.android.exoplayer2.upstream.TransferListener; +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -72,6 +72,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor { private final long mId; private final Handler.Callback mSourceReaderWorker; + private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; private BufferManager.SampleBuffer mSampleBuffer; private Handler mSourceReaderHandler; @@ -88,29 +89,13 @@ public class ExoPlayerSampleExtractor implements SampleExtractor { private Handler mOnCompletionListenerHandler; private IOException mError; - /** - * Factory for {@link ExoPlayerSampleExtractor}. - * - * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory} - * generated class. - */ - public interface Factory { - public ExoPlayerSampleExtractor create( - Uri uri, - DataSource source, - @Nullable BufferManager bufferManager, - PlaybackBufferListener bufferListener, - boolean isRecording); - } - - @AutoFactory(implementing = Factory.class) public ExoPlayerSampleExtractor( Uri uri, - DataSource source, - @Nullable BufferManager bufferManager, + final DataSource source, + BufferManager bufferManager, PlaybackBufferListener bufferListener, boolean isRecording, - @Provided RecordingSampleBuffer.Factory recordingSampleBufferFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlagsoncurrentDvrPlaybackFlags) { this( uri, source, @@ -119,7 +104,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor { isRecording, Looper.myLooper(), new HandlerThread("SourceReaderThread"), - recordingSampleBufferFactory); + concurrentDvrPlaybackFlagsoncurrentDvrPlaybackFlags); } @VisibleForTesting @@ -132,35 +117,98 @@ public class ExoPlayerSampleExtractor implements SampleExtractor { boolean isRecording, Looper workerLooper, HandlerThread sourceReaderThread, - RecordingSampleBuffer.Factory recordingSampleBufferFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) { // It'll be used as a timeshift file chunk name's prefix. mId = System.currentTimeMillis(); + mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags; + + EventListener eventListener = + new EventListener() { + @Override + public void onLoadError(IOException error) { + mError = error; + } + }; mSourceReaderThread = sourceReaderThread; mSourceReaderWorker = new SourceReaderWorker( new ExtractorMediaSource( uri, - /* dataSourceFactory= */ () -> source, + new com.google.android.exoplayer2.upstream.DataSource.Factory() { + @Override + public com.google.android.exoplayer2.upstream.DataSource + createDataSource() { + // Returns an adapter implementation for ExoPlayer V2 + // DataSource interface. + return new com.google.android.exoplayer2.upstream + .DataSource() { + + private @Nullable Uri uri; + + // TODO: uncomment once this is part of the public API. + // @Override + public void addTransferListener( + TransferListener transferListener) { + // Do nothing. Unsupported in V1. + } + + @Override + public long open(DataSpec dataSpec) throws IOException { + this.uri = dataSpec.uri; + return source.open( + new com.google.android.exoplayer.upstream + .DataSpec( + dataSpec.uri, + dataSpec.postBody, + dataSpec.absoluteStreamPosition, + dataSpec.position, + dataSpec.length, + dataSpec.key, + dataSpec.flags)); + } + + @Override + public int read( + byte[] buffer, int offset, int readLength) + throws IOException { + return source.read(buffer, offset, readLength); + } + + @Override + public @Nullable Uri getUri() { + return uri; + } + + @Override + public void close() throws IOException { + source.close(); + uri = null; + } + }; + } + }, new ExoPlayerExtractorsFactory(), new Handler(workerLooper), - /* eventListener= */ error -> mError = error)); + eventListener)); if (isRecording) { mSampleBuffer = - recordingSampleBufferFactory.create( + new RecordingSampleBuffer( bufferManager, bufferListener, false, + mConcurrentDvrPlaybackFlags, RecordingSampleBuffer.BUFFER_REASON_RECORDING); } else { if (bufferManager == null) { mSampleBuffer = new SimpleSampleBuffer(bufferListener); } else { mSampleBuffer = - recordingSampleBufferFactory.create( + new RecordingSampleBuffer( bufferManager, bufferListener, true, + mConcurrentDvrPlaybackFlags, RecordingSampleBuffer.BUFFER_REASON_LIVE_PLAYBACK); } } @@ -192,11 +240,15 @@ public class ExoPlayerSampleExtractor implements SampleExtractor { public SourceReaderWorker(MediaSource sampleSource) { mSampleSource = sampleSource; mSampleSourceListener = - (source, timeline, manifest) -> { - // Dynamic stream change is not supported yet. b/28169263 - // For now, this will cause EOS and playback reset. + new MediaSource.SourceInfoRefreshListener() { + @Override + public void onSourceInfoRefreshed( + MediaSource source, Timeline timeline, Object manifest) { + // Dynamic stream change is not supported yet. b/28169263 + // For now, this will cause EOS and playback reset. + } }; - mSampleSource.prepareSource(mSampleSourceListener, null); + mSampleSource.prepareSource(null, false, mSampleSourceListener, null); mDecoderInputBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); mSampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_NORMAL); @@ -313,8 +365,9 @@ public class ExoPlayerSampleExtractor implements SampleExtractor { mMediaPeriod = mSampleSource.createPeriod( new MediaSource.MediaPeriodId(0), - new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE), - 0); + new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE) +// AOSP_Comment_Out , 0 + ); mMediaPeriod.prepare(this, 0); try { mMediaPeriod.maybeThrowPrepareError(); @@ -433,7 +486,7 @@ public class ExoPlayerSampleExtractor implements SampleExtractor { sample.data.position(0); sample.data.put(mDecoderInputBuffer.data); sample.data.flip(); - mPendingSamples.add(Pair.create(index, sample)); + mPendingSamples.add(new Pair<>(index, sample)); return; } mVideoTrackMet = true; diff --git a/tuner/src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java index aaca043b..9749e4ba 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/FileSampleExtractor.java @@ -17,18 +17,14 @@ package com.android.tv.tuner.exoplayer; import android.os.Handler; - import com.android.tv.tuner.exoplayer.buffer.BufferManager; import com.android.tv.tuner.exoplayer.buffer.PlaybackBufferListener; import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer; - import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.MediaFormatUtil; import com.google.android.exoplayer.SampleHolder; -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; - +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -48,28 +44,16 @@ public class FileSampleExtractor implements SampleExtractor { private final BufferManager mBufferManager; private final PlaybackBufferListener mBufferListener; private BufferManager.SampleBuffer mSampleBuffer; - private final RecordingSampleBuffer.Factory mRecordingSampleBufferFactory; - - /** - * Factory for {@link FileSampleExtractor}}. - * - * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory} - * generated class. - */ - public interface Factory { - public FileSampleExtractor create( - BufferManager bufferManager, PlaybackBufferListener bufferListener); - } + private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; - @AutoFactory(implementing = Factory.class) public FileSampleExtractor( BufferManager bufferManager, PlaybackBufferListener bufferListener, - @Provided RecordingSampleBuffer.Factory recordingSampleBufferFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) { mBufferManager = bufferManager; mBufferListener = bufferListener; + mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags; mTrackCount = -1; - mRecordingSampleBufferFactory = recordingSampleBufferFactory; } @Override @@ -92,10 +76,11 @@ public class FileSampleExtractor implements SampleExtractor { mTrackFormats.add(MediaFormatUtil.createMediaFormat(trackFormat.format)); } mSampleBuffer = - mRecordingSampleBufferFactory.create( + new RecordingSampleBuffer( mBufferManager, mBufferListener, true, + mConcurrentDvrPlaybackFlags, RecordingSampleBuffer.BUFFER_REASON_RECORDED_PLAYBACK); mSampleBuffer.init(ids, mTrackFormats); return true; diff --git a/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java index 67cf992c..6781c616 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsPlayer.java @@ -43,8 +43,7 @@ import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.audio.AudioCapabilities; import com.google.android.exoplayer.audio.AudioTrack; -import com.google.android.exoplayer2.upstream.DataSource; - +import com.google.android.exoplayer.upstream.DataSource; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -148,7 +147,7 @@ public class MpegTsPlayer * * @param rendererBuilder the builder of track renderers * @param handler the handler for the playback events in track renderers - * @param sourceManager the manager for {@link TsDataSource} + * @param sourceManager the manager for {@link DataSource} * @param capabilities the {@link AudioCapabilities} of the current device * @param listener the listener for playback state changes */ @@ -215,7 +214,7 @@ public class MpegTsPlayer } /** - * Creates renderers and {@link TsDataSource} and initializes player. + * Creates renderers and {@link DataSource} and initializes player. * * @param context a {@link Context} instance * @param channel to play diff --git a/tuner/src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java index f860631c..e043907f 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsRendererBuilder.java @@ -17,51 +17,33 @@ package com.android.tv.tuner.exoplayer; import android.content.Context; -import android.support.annotation.Nullable; - import com.android.tv.tuner.exoplayer.MpegTsPlayer.RendererBuilder; import com.android.tv.tuner.exoplayer.MpegTsPlayer.RendererBuilderCallback; import com.android.tv.tuner.exoplayer.audio.MpegTsDefaultAudioTrackRenderer; import com.android.tv.tuner.exoplayer.buffer.BufferManager; import com.android.tv.tuner.exoplayer.buffer.PlaybackBufferListener; - import com.google.android.exoplayer.MediaCodecSelector; import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.TrackRenderer; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; +import com.google.android.exoplayer.upstream.DataSource; +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; /** Builder for renderer objects for {@link MpegTsPlayer}. */ public class MpegTsRendererBuilder implements RendererBuilder { private final Context mContext; private final BufferManager mBufferManager; private final PlaybackBufferListener mBufferListener; - private final MpegTsSampleExtractor.Factory mMpegTsSampleExtractorFactory; - - /** - * Factory for {@link MpegTsRendererBuilder}. - * - * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory} - * generated class. - */ - public interface Factory { - public MpegTsRendererBuilder create( - Context context, - @Nullable BufferManager bufferManager, - PlaybackBufferListener bufferListener); - } + private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; - @AutoFactory(implementing = Factory.class) public MpegTsRendererBuilder( Context context, - @Nullable BufferManager bufferManager, + BufferManager bufferManager, PlaybackBufferListener bufferListener, - @Provided MpegTsSampleExtractor.Factory mpegTsSampleExtractorFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) { mContext = context; mBufferManager = bufferManager; mBufferListener = bufferListener; - mMpegTsSampleExtractorFactory = mpegTsSampleExtractorFactory; + mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags; } @Override @@ -70,9 +52,13 @@ public class MpegTsRendererBuilder implements RendererBuilder { // Build the video and audio renderers. SampleExtractor extractor = dataSource == null - ? mMpegTsSampleExtractorFactory.create(mBufferManager, mBufferListener) - : mMpegTsSampleExtractorFactory.create( - dataSource, mBufferManager, mBufferListener); + ? new MpegTsSampleExtractor( + mBufferManager, mBufferListener, mConcurrentDvrPlaybackFlags) + : new MpegTsSampleExtractor( + dataSource, + mBufferManager, + mBufferListener, + mConcurrentDvrPlaybackFlags); SampleSource sampleSource = new MpegTsSampleSource(extractor); MpegTsVideoTrackRenderer videoRenderer = new MpegTsVideoTrackRenderer( diff --git a/tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java index 8d3668ef..582f18c5 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsSampleExtractor.java @@ -18,21 +18,16 @@ package com.android.tv.tuner.exoplayer; import android.net.Uri; import android.os.Handler; -import android.support.annotation.Nullable; - import com.android.tv.tuner.exoplayer.buffer.BufferManager; import com.android.tv.tuner.exoplayer.buffer.PlaybackBufferListener; import com.android.tv.tuner.exoplayer.buffer.SamplePool; - import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormatHolder; import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleSource; +import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.util.MimeTypes; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; - +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -64,37 +59,27 @@ public final class MpegTsSampleExtractor implements SampleExtractor { } /** - * Factory for {@link MpegTsSampleExtractor}. - * - * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory} - * generated class. - */ - public interface Factory { - public MpegTsSampleExtractor create( - BufferManager bufferManager, PlaybackBufferListener bufferListener); - - public MpegTsSampleExtractor create( - DataSource source, - @Nullable BufferManager bufferManager, - PlaybackBufferListener bufferListener); - } - - /** - * Creates MpegTsSampleExtractor for a {@link DataSource}. + * Creates MpegTsSampleExtractor for {@link DataSource}. * * @param source the {@link DataSource} to extract from * @param bufferManager the manager for reading & writing samples backed by physical storage * @param bufferListener the {@link PlaybackBufferListener} to notify buffer storage status + * @param concurrentDvrPlaybackFlags */ - @AutoFactory(implementing = Factory.class) public MpegTsSampleExtractor( DataSource source, - @Nullable BufferManager bufferManager, + BufferManager bufferManager, PlaybackBufferListener bufferListener, - @Provided ExoPlayerSampleExtractor.Factory exoPlayerSampleExtractorFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) { + mSampleExtractor = - exoPlayerSampleExtractorFactory.create( - Uri.EMPTY, source, bufferManager, bufferListener, false); + new ExoPlayerSampleExtractor( + Uri.EMPTY, + source, + bufferManager, + bufferListener, + false, + concurrentDvrPlaybackFlags); init(); } @@ -105,12 +90,12 @@ public final class MpegTsSampleExtractor implements SampleExtractor { * @param bufferListener the {@link PlaybackBufferListener} to notify buffer storage status * change */ - @AutoFactory(implementing = Factory.class) public MpegTsSampleExtractor( BufferManager bufferManager, PlaybackBufferListener bufferListener, - @Provided FileSampleExtractor.Factory fileSampleExtractorFactory) { - mSampleExtractor = fileSampleExtractorFactory.create(bufferManager, bufferListener); + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) { + mSampleExtractor = + new FileSampleExtractor(bufferManager, bufferListener, concurrentDvrPlaybackFlags); init(); } diff --git a/tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java b/tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java index fb88e5b7..bab74c9d 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/audio/MpegTsDefaultAudioTrackRenderer.java @@ -246,7 +246,6 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me mSource.seekToUs(positionUs); AUDIO_TRACK.reset(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - // b/21824483 workaround // resetSessionId() will create a new framework AudioTrack instead of reusing old one. AUDIO_TRACK.resetSessionId(); } @@ -285,7 +284,6 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me // Ensure playback stops, after EoS was notified. // Sometimes MediaCodecTrackRenderer does not fetch EoS timely // after EoS was notified here long before. - // see b/21909113 long diff = SystemClock.elapsedRealtime() - mEndOfStreamMs; if (diff >= KEEP_ALIVE_AFTER_EOS_DURATION_MS && !mIsStopped) { throw new ExoPlaybackException("Much time has elapsed after EoS"); @@ -594,7 +592,6 @@ public class MpegTsDefaultAudioTrackRenderer extends TrackRenderer implements Me } mCurrentPositionUs = Math.max(mPresentationTimeUs, mCurrentPositionUs); } else { - // TODO: Remove this workaround when b/22023809 is resolved. if (mPreviousPositionUs > audioTrackCurrentPositionUs + BACKWARD_AUDIO_TRACK_MOVE_THRESHOLD_US) { Log.e( diff --git a/tuner/src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java index b8d85230..c32540c1 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/BufferManager.java @@ -23,13 +23,10 @@ import android.support.annotation.VisibleForTesting; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; - import com.android.tv.common.SoftPreconditions; import com.android.tv.common.util.CommonUtils; import com.android.tv.tuner.exoplayer.SampleExtractor; - import com.google.android.exoplayer.SampleHolder; - import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -403,13 +400,13 @@ public class BufferManager { SampleChunk sampleChunk = mSampleChunkCreator.createSampleChunk( samplePool, file, positionUs, mChunkCallback); - map.put(positionUs, Pair.create(sampleChunk, 0)); + map.put(positionUs, new Pair(sampleChunk, 0)); if (updateIndexFile) { mStorageManager.updateIndexFile(id, map.size(), positionUs, sampleChunk, 0); } return sampleChunk; } else { - map.put(positionUs, Pair.create(currentChunk, currentOffset)); + map.put(positionUs, new Pair(currentChunk, currentOffset)); if (updateIndexFile) { mStorageManager.updateIndexFile( id, map.size(), positionUs, currentChunk, currentOffset); @@ -450,7 +447,7 @@ public class BufferManager { chunk); basePositionUs = position.basePositionUs; } - map.put(position.positionUs, Pair.create(chunk, position.offset)); + map.put(position.positionUs, new Pair(chunk, position.offset)); } } diff --git a/tuner/src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java index 0e1cbe9f..f19756ec 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/DvrStorageManager.java @@ -19,7 +19,8 @@ package com.android.tv.tuner.exoplayer.buffer; import android.media.MediaFormat; import android.util.Log; import android.util.Pair; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; +import com.google.protobuf.nano.MessageNano; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; @@ -368,7 +369,7 @@ public class DvrStorageManager implements BufferManager.StorageManager { META_FILE_TYPE_CAPTION + ((i == 0) ? META_FILE_SUFFIX : (i + META_FILE_SUFFIX)); File file = new File(getBufferDir(), fileName); try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) { - track.writeTo(out); + out.write(MessageNano.toByteArray(track)); } catch (Exception e) { Log.e(TAG, "Fail to write caption info to files", e); } diff --git a/tuner/src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java index df2cd2e6..d95642c2 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/RecordingSampleBuffer.java @@ -20,18 +20,14 @@ import android.os.ConditionVariable; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.util.Log; - import com.android.tv.tuner.exoplayer.MpegTsPlayer; import com.android.tv.tuner.exoplayer.SampleExtractor; - import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.SampleSource; import com.google.android.exoplayer.util.Assertions; -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; - +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -73,7 +69,7 @@ public class RecordingSampleBuffer private final BufferManager mBufferManager; private final PlaybackBufferListener mBufferListener; private final @BufferReason int mBufferReason; - private final SampleChunkIoHelper.Factory mSampleChunkIoHelperFactory; + private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; private int mTrackCount; private boolean[] mTrackSelected; @@ -102,42 +98,28 @@ public class RecordingSampleBuffer }; /** - * Factory for {@link RecordingSampleBuffer}. - * - * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory} - * generated class. - */ - public interface Factory { - public RecordingSampleBuffer create( - BufferManager bufferManager, - PlaybackBufferListener bufferListener, - boolean enableTrickplay, - @BufferReason int bufferReason); - } - - /** * Creates {@link BufferManager.SampleBuffer} with cached I/O backed by physical storage (e.g. * trickplay,recording,recorded-playback). * * @param bufferManager the manager of {@link SampleChunk} * @param bufferListener the listener for buffer I/O event * @param enableTrickplay {@code true} when trickplay should be enabled + * @param concurrentDvrPlaybackFlags * @param bufferReason the reason for caching samples {@link BufferReason} */ - @AutoFactory(implementing = Factory.class) public RecordingSampleBuffer( BufferManager bufferManager, PlaybackBufferListener bufferListener, boolean enableTrickplay, - @BufferReason int bufferReason, - @Provided SampleChunkIoHelper.Factory sampleChunkIoHelperFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, + @BufferReason int bufferReason) { mBufferManager = bufferManager; mBufferListener = bufferListener; + mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags; if (bufferListener != null) { bufferListener.onBufferStateChanged(enableTrickplay); } mBufferReason = bufferReason; - mSampleChunkIoHelperFactory = sampleChunkIoHelperFactory; } @Override @@ -150,8 +132,14 @@ public class RecordingSampleBuffer mTrackSelected = new boolean[mTrackCount]; mReadSampleQueues = new ArrayList<>(); mSampleChunkIoHelper = - mSampleChunkIoHelperFactory.create( - ids, mediaFormats, mBufferReason, mBufferManager, mSamplePool, mIoCallback); + new SampleChunkIoHelper( + ids, + mediaFormats, + mBufferReason, + mBufferManager, + mSamplePool, + mIoCallback, + mConcurrentDvrPlaybackFlags); for (int i = 0; i < mTrackCount; ++i) { mReadSampleQueues.add(i, new SampleQueue(mSamplePool)); } diff --git a/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java index 82bf0df8..f4d3bf8e 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java +++ b/tuner/src/com/android/tv/tuner/exoplayer/buffer/SampleChunkIoHelper.java @@ -24,17 +24,12 @@ import android.os.Message; import android.util.ArraySet; import android.util.Log; import android.util.Pair; - import com.android.tv.common.SoftPreconditions; -import com.android.tv.common.flags.DvrFlags; import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer.BufferReason; - import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.SampleHolder; import com.google.android.exoplayer.util.MimeTypes; -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; - +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; @@ -69,7 +64,7 @@ public class SampleChunkIoHelper implements Handler.Callback { private final BufferManager mBufferManager; private final SamplePool mSamplePool; private final IoCallback mIoCallback; - private final DvrFlags mDvrFlags; + private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; private Handler mIoHandler; private final ConcurrentLinkedQueue<SampleHolder> mReadSampleBuffers[]; @@ -118,22 +113,6 @@ public class SampleChunkIoHelper implements Handler.Callback { } /** - * Factory for {@link SampleChunkIoHelper}. - * - * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory} - * generated class. - */ - public interface Factory { - public SampleChunkIoHelper create( - List<String> ids, - List<MediaFormat> mediaFormats, - @BufferReason int bufferReason, - BufferManager bufferManager, - SamplePool samplePool, - IoCallback ioCallback); - } - - /** * Creates {@link SampleChunk} I/O handler. * * @param ids track names @@ -142,8 +121,8 @@ public class SampleChunkIoHelper implements Handler.Callback { * @param bufferManager manager of {@link SampleChunk} collections * @param samplePool allocator for a sample * @param ioCallback listeners for I/O events + * @param concurrentDvrPlaybackFlags */ - @AutoFactory(implementing = Factory.class) public SampleChunkIoHelper( List<String> ids, List<MediaFormat> mediaFormats, @@ -151,7 +130,7 @@ public class SampleChunkIoHelper implements Handler.Callback { BufferManager bufferManager, SamplePool samplePool, IoCallback ioCallback, - @Provided DvrFlags dvrFlags) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags) { mTrackCount = ids.size(); mIds = ids; mMediaFormats = mediaFormats; @@ -159,7 +138,7 @@ public class SampleChunkIoHelper implements Handler.Callback { mBufferManager = bufferManager; mSamplePool = samplePool; mIoCallback = ioCallback; - mDvrFlags = dvrFlags; + mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags; mReadSampleBuffers = new ConcurrentLinkedQueue[mTrackCount]; mHandlerReadSampleBuffers = new ConcurrentLinkedQueue[mTrackCount]; @@ -205,7 +184,9 @@ public class SampleChunkIoHelper implements Handler.Callback { } try { - if (mBufferReason == RecordingSampleBuffer.BUFFER_REASON_RECORDING && mTrackCount > 0) { + if (mConcurrentDvrPlaybackFlags.enabled() + && mBufferReason == RecordingSampleBuffer.BUFFER_REASON_RECORDING + && mTrackCount > 0) { // Saves meta information for recording. List<BufferManager.TrackFormat> audios = new ArrayList<>(mTrackCount); List<BufferManager.TrackFormat> videos = new ArrayList<>(mTrackCount); @@ -213,14 +194,6 @@ public class SampleChunkIoHelper implements Handler.Callback { android.media.MediaFormat format = mMediaFormats.get(i).getFrameworkMediaFormatV16(); format.setLong(android.media.MediaFormat.KEY_DURATION, mBufferDurationUs); - if (mDvrFlags.storeVideoAspectRatio() && - mMediaFormats.get(i).pixelWidthHeightRatio > 0) { - // MediaFormats doesn't store aspect ratio so updating the width - // to maintain aspect ratio. - format.setInteger(android.media.MediaFormat.KEY_WIDTH, - (int) (mMediaFormats.get(i).width * - mMediaFormats.get(i).pixelWidthHeightRatio)); - } if (MimeTypes.isAudio(mMediaFormats.get(i).mimeType)) { audios.add(new BufferManager.TrackFormat(mIds.get(i), format)); } else if (MimeTypes.isVideo(mMediaFormats.get(i).mimeType)) { @@ -329,14 +302,6 @@ public class SampleChunkIoHelper implements Handler.Callback { android.media.MediaFormat format = mMediaFormats.get(i).getFrameworkMediaFormatV16(); format.setLong(android.media.MediaFormat.KEY_DURATION, mBufferDurationUs); - if (mDvrFlags.storeVideoAspectRatio() && - mMediaFormats.get(i).pixelWidthHeightRatio > 0) { - // MediaFormats doesn't store aspect ratio so updating the width - // to maintain aspect ratio. - format.setInteger(android.media.MediaFormat.KEY_WIDTH, - (int) (mMediaFormats.get(i).width * - mMediaFormats.get(i).pixelWidthHeightRatio)); - } if (MimeTypes.isAudio(mMediaFormats.get(i).mimeType)) { audios.add(new BufferManager.TrackFormat(mIds.get(i), format)); } else if (MimeTypes.isVideo(mMediaFormats.get(i).mimeType)) { @@ -419,7 +384,8 @@ public class SampleChunkIoHelper implements Handler.Callback { private void doOpenWrite(int index) throws IOException { boolean updateIndexFile = - (mBufferReason == RecordingSampleBuffer.BUFFER_REASON_RECORDING) + mConcurrentDvrPlaybackFlags.enabled() + && (mBufferReason == RecordingSampleBuffer.BUFFER_REASON_RECORDING) && (MimeTypes.isVideo(mMediaFormats.get(index).mimeType) || MimeTypes.isAudio(mMediaFormats.get(index).mimeType)); @@ -460,10 +426,13 @@ public class SampleChunkIoHelper implements Handler.Callback { SampleHolder sample = mReadIoStates[index].read(); if (sample != null) { mHandlerReadSampleBuffers[index].offer(sample); - mReadChunkOffset[index] = mReadIoStates[index].getOffset(); - mReadChunkPositionUs[index] = sample.timeUs; + if (mConcurrentDvrPlaybackFlags.enabled()) { + mReadChunkOffset[index] = mReadIoStates[index].getOffset(); + mReadChunkPositionUs[index] = sample.timeUs; + } } else { - if (mBufferReason == RecordingSampleBuffer.BUFFER_REASON_RECORDED_PLAYBACK) { + if (mConcurrentDvrPlaybackFlags.enabled() + && mBufferReason == RecordingSampleBuffer.BUFFER_REASON_RECORDED_PLAYBACK) { // Update Index, to load new Samples updateIndex(index, mReadChunkOffset[index]); } @@ -516,7 +485,9 @@ public class SampleChunkIoHelper implements Handler.Callback { : mWriteIoStates[params.index].getChunk(); int currentOffset = (int) mWriteIoStates[params.index].getOffset(); boolean updateIndexFile = - (mBufferReason == RecordingSampleBuffer.BUFFER_REASON_RECORDING) + mConcurrentDvrPlaybackFlags.enabled() + && (mBufferReason + == RecordingSampleBuffer.BUFFER_REASON_RECORDING) && (MimeTypes.isVideo(mMediaFormats.get(index).mimeType) || MimeTypes.isAudio( mMediaFormats.get(index).mimeType)); diff --git a/tuner/src/com/android/tv/tuner/exoplayer2/VideoRendererExoV2.java b/tuner/src/com/android/tv/tuner/exoplayer2/VideoRendererExoV2.java index a71352f3..12039002 100644 --- a/tuner/src/com/android/tv/tuner/exoplayer2/VideoRendererExoV2.java +++ b/tuner/src/com/android/tv/tuner/exoplayer2/VideoRendererExoV2.java @@ -24,6 +24,7 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException; +import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.video.MediaCodecVideoRenderer; import com.google.android.exoplayer2.video.VideoRendererEventListener; import java.lang.reflect.Field; @@ -47,8 +48,8 @@ public class VideoRendererExoV2 extends MediaCodecVideoRenderer { private static final String SOFTWARE_DECODER_NAME_PREFIX = "OMX.google."; private static final long ALLOWED_JOINING_TIME_MS = 5000; private static final int DROPPED_FRAMES_NOTIFICATION_THRESHOLD = 10; - // private static final int MIN_HD_HEIGHT = 720; - private static Field sRenderedFirstFrameField; + private static final int MIN_HD_HEIGHT = 720; + private static Field sRenderedFirstFrameField; private final boolean mIsSwCodecEnabled; private boolean mCodecIsSwPreferred; @@ -107,18 +108,16 @@ public class VideoRendererExoV2 extends MediaCodecVideoRenderer { return decoderInfos; } - // TODO: Uncomment once ExoPlayer v2.10.0 is released [Internal ref: b/130625979]. - // @Override - // protected void onInputFormatChanged(FormatHolder formatHolder) throws ExoPlaybackException { - // Format format = formatHolder.format; - // mCodecIsSwPreferred = - // MimeTypes.VIDEO_MPEG2.equals(format.sampleMimeType) - // && format.height < MIN_HD_HEIGHT; - // super.onInputFormatChanged(format); - // } + @Override + protected void onInputFormatChanged(Format format) throws ExoPlaybackException { + mCodecIsSwPreferred = + MimeTypes.VIDEO_MPEG2.equals(format.sampleMimeType) + && format.height < MIN_HD_HEIGHT; + super.onInputFormatChanged(format); + } - @Override - protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { + @Override + protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { super.onPositionReset(positionUs, joining); // Disabling pre-rendering of the first frame in order to avoid a frozen picture when // starting the playback. We do this only once, when the renderer is enabled at first, since diff --git a/tuner/src/com/android/tv/tuner/features/TunerFeatures.java b/tuner/src/com/android/tv/tuner/features/TunerFeatures.java index 6ee5aa8c..6033a3a6 100644 --- a/tuner/src/com/android/tv/tuner/features/TunerFeatures.java +++ b/tuner/src/com/android/tv/tuner/features/TunerFeatures.java @@ -19,9 +19,9 @@ package com.android.tv.tuner.features; import static com.android.tv.common.feature.FeatureUtils.OFF; import com.android.tv.common.feature.CommonFeatures; -import com.android.tv.common.feature.DeveloperPreferenceFeature; import com.android.tv.common.feature.Feature; import com.android.tv.common.feature.Model; +import com.android.tv.common.feature.PropertyFeature; import com.android.tv.common.feature.Sdk; /** @@ -39,11 +39,10 @@ public class TunerFeatures extends CommonFeatures { * <p>Prefer software based codec for SD channels. */ public static final Feature USE_SW_CODEC_FOR_SD = - DeveloperPreferenceFeature.create( + PropertyFeature.create( "use_sw_codec_for_sd", - // On Nexus Player, SW codec is better than HW codec in terms of picture - // quality. - Model.NEXUS_PLAYER.isEnabled()); + false + ); /** * Does the TvProvider on the installed device allow systems inserts to the programs table. diff --git a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunChannelScan.java b/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunChannelScan.java deleted file mode 100644 index 38610dd1..00000000 --- a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunChannelScan.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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.hdhomerun; - -import android.content.Context; -import android.media.tv.TvContract; -import android.os.ConditionVariable; -import android.util.Log; -import android.util.Xml; -import com.android.tv.tuner.api.ChannelScanListener; -import com.android.tv.tuner.data.TunerChannel; -import com.android.tv.tuner.ts.EventDetector.EventListener; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.regex.Pattern; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** A helper class to perform channel scan on HDHomeRun tuner. */ -public class HdHomeRunChannelScan { - private static final String TAG = "HdHomeRunChannelScan"; - private static final boolean DEBUG = false; - - private static final String LINEUP_FILENAME = "lineup.xml"; - private static final String NAME_LINEUP = "Lineup"; - private static final String NAME_PROGRAM = "Program"; - private static final String NAME_GUIDE_NUMBER = "GuideNumber"; - private static final String NAME_GUIDE_NAME = "GuideName"; - private static final String NAME_HD = "HD"; - private static final String NAME_TAGS = "Tags"; - private static final String NAME_DRM = "DRM"; - - private final Context mContext; - private final ChannelScanListener mEventListener; - private final HdHomeRunTunerHal mTunerHal; - private int mProgramCount; - - public HdHomeRunChannelScan( - Context context, EventListener eventListener, HdHomeRunTunerHal hal) { - mContext = context; - mEventListener = eventListener; - mTunerHal = hal; - } - - public void scan(ConditionVariable conditionStopped) { - String urlString = "http://" + mTunerHal.getIpAddress() + "/" + LINEUP_FILENAME; - if (DEBUG) Log.d(TAG, "Reading " + urlString); - URL url; - HttpURLConnection connection = null; - InputStream inputStream; - try { - url = new URL(urlString); - connection = (HttpURLConnection) url.openConnection(); - connection.setReadTimeout(HdHomeRunTunerHal.READ_TIMEOUT_MS_FOR_URLCONNECTION); - connection.setConnectTimeout(HdHomeRunTunerHal.CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION); - connection.setRequestMethod("GET"); - connection.setDoInput(true); - connection.connect(); - inputStream = connection.getInputStream(); - } catch (IOException e) { - Log.e(TAG, "Connection failed: " + urlString, e); - if (connection != null) { - connection.disconnect(); - } - return; - } - if (conditionStopped.block(-1)) { - try { - inputStream.close(); - } catch (IOException e) { - // Does nothing. - } - connection.disconnect(); - return; - } - - XmlPullParser parser = Xml.newPullParser(); - try { - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(inputStream, null); - parser.nextTag(); - parser.require(XmlPullParser.START_TAG, null, NAME_LINEUP); - while (parser.next() != XmlPullParser.END_TAG) { - if (conditionStopped.block(-1)) { - break; - } - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - String name = parser.getName(); - // Starts by looking for the program tag - if (name.equals(NAME_PROGRAM)) { - readProgram(parser); - } else { - skip(parser); - } - } - inputStream.close(); - } catch (IOException | XmlPullParserException e) { - Log.e(TAG, "Parse error", e); - } - connection.disconnect(); - mTunerHal.markAsScannedDevice(mContext); - } - - private void readProgram(XmlPullParser parser) throws XmlPullParserException, IOException { - parser.require(XmlPullParser.START_TAG, null, NAME_PROGRAM); - String guideNumber = ""; - String guideName = ""; - String videoFormat = null; - String tags = ""; - boolean recordingProhibited = false; - while (parser.next() != XmlPullParser.END_TAG) { - if (parser.getEventType() != XmlPullParser.START_TAG) { - continue; - } - String name = parser.getName(); - if (name.equals(NAME_GUIDE_NUMBER)) { - guideNumber = readText(parser, NAME_GUIDE_NUMBER); - } else if (name.equals(NAME_GUIDE_NAME)) { - guideName = readText(parser, NAME_GUIDE_NAME); - } else if (name.equals(NAME_HD)) { - videoFormat = TvContract.Channels.VIDEO_FORMAT_720P; - skip(parser); - } else if (name.equals(NAME_TAGS)) { - tags = readText(parser, NAME_TAGS); - } else if (name.equals(NAME_DRM)) { - String drm = readText(parser, NAME_DRM); - try { - recordingProhibited = (Integer.parseInt(drm)) != 0; - } catch (NumberFormatException e) { - Log.e(TAG, "Load DRM property failed: illegal number: " + drm); - // If DRM property is present, we treat it as copy-once or copy-never. - recordingProhibited = true; - } - } else { - skip(parser); - } - } - if (!tags.isEmpty()) { - // Skip encrypted channels since we don't know how to decrypt them. - return; - } - int major; - int minor = 0; - final String separator = Character.toString(HdHomeRunTunerHal.VCHANNEL_SEPARATOR); - if (guideNumber.contains(separator)) { - String[] parts = guideNumber.split(Pattern.quote(separator)); - major = Integer.parseInt(parts[0]); - minor = Integer.parseInt(parts[1]); - } else { - major = Integer.parseInt(guideNumber); - } - // Need to assign a unique program number (i.e. mProgramCount) to avoid being duplicated. - mEventListener.onChannelDetected( - TunerChannel.forNetwork( - major, minor, mProgramCount++, guideName, recordingProhibited, videoFormat), - true); - } - - private String readText(XmlPullParser parser, String name) - throws IOException, XmlPullParserException { - String result = ""; - parser.require(XmlPullParser.START_TAG, null, name); - if (parser.next() == XmlPullParser.TEXT) { - result = parser.getText(); - parser.nextTag(); - } - parser.require(XmlPullParser.END_TAG, null, name); - if (DEBUG) Log.d(TAG, "<" + name + ">=" + result); - return result; - } - - private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { - if (parser.getEventType() != XmlPullParser.START_TAG) { - throw new IllegalStateException(); - } - int depth = 1; - while (depth != 0) { - switch (parser.next()) { - case XmlPullParser.END_TAG: - depth--; - break; - case XmlPullParser.START_TAG: - depth++; - break; - } - } - } -} diff --git a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunControlSocket.java b/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunControlSocket.java deleted file mode 100644 index ce7c5180..00000000 --- a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunControlSocket.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2017 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.hdhomerun; - -import android.support.annotation.Nullable; -import android.util.Log; -import android.util.Pair; -import com.android.tv.tuner.hdhomerun.HdHomeRunDiscover.HdHomeRunDiscoverDevice; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.List; - -/** - * A class to send/receive control commands and results to/from HDHomeRun devices via TCP sockets. - * {@link #close()} method should be called after usage to close the TCP socket. - */ -class HdHomeRunControlSocket implements AutoCloseable { - private static final String TAG = "HdHomeRunControlSocket"; - private static final boolean DEBUG = false; - - private int mDesiredDeviceId; - private int mDesiredDeviceIp; - private int mActualDeviceId; - private int mActualDeviceIp; - private Socket mSocket; - - HdHomeRunControlSocket(int deviceId, int deviceIp) { - mDesiredDeviceId = deviceId; - mDesiredDeviceIp = deviceIp; - mActualDeviceId = 0; - mActualDeviceIp = 0; - } - - /** - * Gets control settings from HDHomeRun devices. - * - * @param name the name of the field whose value we want to get. - */ - @Nullable - String get(String name) { - byte[] data = new byte[name.length() + 3]; - ByteBuffer buffer = ByteBuffer.wrap(data); - buffer.put(HdHomeRunUtils.HDHOMERUN_TAG_GETSET_NAME); - buffer.put((byte) (name.length() + 1)); - buffer.put(name.getBytes()); - - // Send & Receive. - byte[] result = - sendAndReceive( - data, - HdHomeRunUtils.HDHOMERUN_TYPE_GETSET_REQUEST, - HdHomeRunUtils.HDHOMERUN_CONTROL_RECEIVE_TIMEOUT_MS); - if (result == null) { - if (DEBUG) Log.d(TAG, "Cannot get result for " + name); - return null; - } - - // Response. - buffer = ByteBuffer.wrap(result); - while (true) { - Pair<Byte, byte[]> tagAndValue = HdHomeRunUtils.readTaggedValue(buffer); - if (tagAndValue == null) { - break; - } - switch (tagAndValue.first) { - case HdHomeRunUtils.HDHOMERUN_TAG_GETSET_VALUE: - // Removes the 0 tail. - return new String( - Arrays.copyOfRange( - tagAndValue.second, 0, tagAndValue.second.length - 1)); - case HdHomeRunUtils.HDHOMERUN_TAG_ERROR_MESSAGE: - return null; - } - } - return null; - } - - /** Gets ID of HDHomeRun devices. */ - int getDeviceId() { - if (!connectAndUpdateDeviceInfo()) { - return 0; - } - return mActualDeviceId; - } - - private boolean connectAndUpdateDeviceInfo() { - if (mSocket != null) { - return true; - } - if ((mDesiredDeviceId == 0) && (mDesiredDeviceIp == 0)) { - if (DEBUG) Log.d(TAG, "Desired ID and IP cannot be both zero."); - return false; - } - if (HdHomeRunUtils.isIpMulticast(mDesiredDeviceIp)) { - if (DEBUG) Log.d(TAG, "IP cannot be multicast IP."); - return false; - } - - // Find device. - List<HdHomeRunDiscoverDevice> result = - HdHomeRunUtils.findHdHomeRunDevices( - mDesiredDeviceIp, - HdHomeRunUtils.HDHOMERUN_DEVICE_TYPE_WILDCARD, - mDesiredDeviceId, - 1); - if (result.isEmpty()) { - if (DEBUG) Log.d(TAG, "Cannot find device on: " + mDesiredDeviceIp); - return false; - } - mActualDeviceIp = result.get(0).mIpAddress; - mActualDeviceId = result.get(0).mDeviceId; - - // Create socket and initiate connection. - mSocket = new Socket(); - try { - mSocket.connect( - new InetSocketAddress( - HdHomeRunUtils.intToAddress(mActualDeviceIp), - HdHomeRunUtils.HDHOMERUN_CONTROL_TCP_PORT), - HdHomeRunUtils.HDHOMERUN_CONTROL_CONNECT_TIMEOUT_MS); - } catch (IOException e) { - if (DEBUG) Log.d(TAG, "Cannot connect to socket: " + mSocket); - mSocket = null; - return false; - } - - // Success. - Log.i(TAG, "Connected to socket: " + mSocket); - return true; - } - - private byte[] sendAndReceive(byte[] data, short type, int timeout) { - byte[] sealedData = HdHomeRunUtils.sealFrame(data, type); - for (int i = 0; i < 2; i++) { - if (mSocket == null && !connectAndUpdateDeviceInfo()) { - return null; - } - if (!send(sealedData)) { - continue; - } - Pair<Short, byte[]> receivedData = receive(timeout); - if (receivedData == null || receivedData.first == null) { - continue; - } - if (receivedData.first != type + 1) { - if (DEBUG) Log.d(TAG, "Returned type incorrect: " + receivedData.first); - close(); - continue; - } - return receivedData.second; - } - return null; - } - - private boolean send(byte[] data) { - try { - OutputStream out = mSocket.getOutputStream(); - mSocket.setSoTimeout(HdHomeRunUtils.HDHOMERUN_CONTROL_SEND_TIMEOUT_MS); - out.write(data); - } catch (IOException e) { - if (DEBUG) Log.d(TAG, "Cannot send packet to socket: " + mSocket); - close(); - return false; - } - return true; - } - - private Pair<Short, byte[]> receive(int timeout) { - byte[] receivedData = new byte[3074]; - try { - InputStream input = mSocket.getInputStream(); - mSocket.setSoTimeout(timeout); - int index = 0; - long startTime = System.currentTimeMillis(); - while (System.currentTimeMillis() - startTime < timeout) { - int length = receivedData.length - index; - index += input.read(receivedData, index, length); - Pair<Short, byte[]> result = HdHomeRunUtils.openFrame(receivedData, index); - if (result != null) { - if (result.first == HdHomeRunUtils.HDHOMERUN_TYPE_INVALID) { - if (DEBUG) Log.d(TAG, "Returned type is invalid."); - close(); - return null; - } - return result; - } - if (DEBUG) Log.d(TAG, "Received result is null!"); - } - } catch (IOException e) { - if (DEBUG) Log.d(TAG, "Cannot receive from socket: " + mSocket); - close(); - } - return null; - } - - @Override - public void close() { - if (mSocket != null) { - try { - mSocket.close(); - } catch (IOException e) { - // Do nothing - } - mSocket = null; - } - } -} diff --git a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunDevice.java b/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunDevice.java deleted file mode 100644 index dcf87cad..00000000 --- a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunDevice.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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.hdhomerun; - -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; - -/** - * An HDHomeRun device detected on the network. This abstraction only contains network data - * necessary to establish a connection with the device and does not represent a communication - * channel with the device itself. Currently, we only support devices with HTTP streaming - * functionality. - */ -public class HdHomeRunDevice implements Parcelable { - private int mIpAddress; - private int mDeviceType; - private int mDeviceId; - private int mTunerIndex; - private String mDeviceModel; - - /** - * Creates {@code HdHomeRunDevice} object from a parcel. - * - * @param parcel The parcel to create {@code HdHomeRunDevice} object from. - */ - public HdHomeRunDevice(Parcel parcel) { - mIpAddress = parcel.readInt(); - mDeviceType = parcel.readInt(); - mDeviceId = parcel.readInt(); - mTunerIndex = parcel.readInt(); - mDeviceModel = parcel.readString(); - } - - /** - * Creates {@code HdHomeRunDevice} object from IP address, device type, device ID and tuner - * index. - * - * @param ipAddress The IP address to create {@code HdHomeRunDevice} object from. - * @param deviceType The device type to create {@code HdHomeRunDevice} object from. - * @param deviceId The device ID to create {@code HdHomeRunDevice} object from. - * @param tunerIndex The tuner index to {@code HdHomeRunDevice} object from. - */ - public HdHomeRunDevice( - int ipAddress, int deviceType, int deviceId, int tunerIndex, String deviceModel) { - mIpAddress = ipAddress; - mDeviceType = deviceType; - mDeviceId = deviceId; - mTunerIndex = tunerIndex; - mDeviceModel = deviceModel; - } - - /** - * Returns the IP address. - * - * @return the IP address of this homerun device. - */ - public int getIpAddress() { - return mIpAddress; - } - - /** - * Returns the device type. - * - * @return the type of device for this homerun device. - */ - public int getDeviceType() { - return mDeviceType; - } - - /** - * Returns the device ID. - * - * @return the device ID of this homerun device. - */ - public int getDeviceId() { - return mDeviceId; - } - - /** - * Returns the tuner index. - * - * @return the tuner index of this homerun device. - */ - public int getTunerIndex() { - return mTunerIndex; - } - - /** - * Returns the device model. - * - * @return the device model of this homerun device. - */ - public String getDeviceModel() { - return mDeviceModel; - } - - @Override - public String toString() { - String ipAddress = - "" - + ((mIpAddress >>> 24) & 0xff) - + "." - + ((mIpAddress >>> 16) & 0xff) - + "." - + ((mIpAddress >>> 8) & 0xff) - + "." - + (mIpAddress & 0xff); - return String.format("[%x-%d:%s]", mDeviceId, mTunerIndex, ipAddress); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeInt(mIpAddress); - out.writeInt(mDeviceType); - out.writeInt(mDeviceId); - out.writeInt(mTunerIndex); - out.writeString(mDeviceModel); - } - - @Override - public int hashCode() { - int hash = 17; - hash = hash * 31 + getIpAddress(); - hash = hash * 31 + getDeviceType(); - hash = hash * 31 + getDeviceId(); - hash = hash * 31 + getTunerIndex(); - return hash; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof HdHomeRunDevice)) { - return false; - } - HdHomeRunDevice rhs = (HdHomeRunDevice) o; - return rhs != null - && getIpAddress() == rhs.getIpAddress() - && getDeviceType() == rhs.getDeviceType() - && getDeviceId() == rhs.getDeviceId() - && getTunerIndex() == rhs.getTunerIndex() - && TextUtils.equals(getDeviceModel(), rhs.getDeviceModel()); - } - - public static final Parcelable.Creator<HdHomeRunDevice> CREATOR = - new Parcelable.Creator<HdHomeRunDevice>() { - - @Override - public HdHomeRunDevice createFromParcel(Parcel in) { - return new HdHomeRunDevice(in); - } - - @Override - public HdHomeRunDevice[] newArray(int size) { - return new HdHomeRunDevice[size]; - } - }; -} diff --git a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunDiscover.java b/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunDiscover.java deleted file mode 100644 index 85b3450e..00000000 --- a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunDiscover.java +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (C) 2017 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.hdhomerun; - -import android.support.annotation.NonNull; -import android.util.Log; -import android.util.Pair; -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.net.InterfaceAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; - -/** A class to discover HDHomeRun devices on the network with UDP broadcasting. */ -class HdHomeRunDiscover { - private static final String TAG = "HdHomeRunDiscover"; - private static final boolean DEBUG = false; - - private static final int HDHOMERUN_DISCOVER_MAX_SOCK_COUNT = 16; - private static final int HDHOMERUN_DISCOVER_RETRY_LIMIT = 2; - private static final int HDHOMERUN_DISCOVER_TIMEOUT_MS = 500; - private static final int HDHOMERUN_DISCOVER_RECEIVE_WAITE_TIME_MS = 10; - - private List<HdHomeRunDiscoverSocket> mSockets = new ArrayList<>(); - - /** Creates a discover object. If cannot add a default socket, return {@code null}. */ - static HdHomeRunDiscover create() { - HdHomeRunDiscover hdHomeRunDiscover = new HdHomeRunDiscover(); - // Create a routable socket (always first entry). - if (!hdHomeRunDiscover.addSocket(0, 0)) { - return null; - } - return hdHomeRunDiscover; - } - - /** Closes and releases all sockets required by this discover object. */ - void close() { - for (HdHomeRunDiscoverSocket discoverSocket : mSockets) { - discoverSocket.close(); - } - } - - /** Finds HDHomeRun devices. */ - @NonNull - List<HdHomeRunDiscoverDevice> findDevices( - int targetIp, int deviceType, int deviceId, int maxCount) { - List<HdHomeRunDiscoverDevice> resultList = new ArrayList<>(); - resetLocalIpSockets(); - for (int retry = 0; - retry < HDHOMERUN_DISCOVER_RETRY_LIMIT && resultList.isEmpty(); - retry++) { - int localIpSent = send(targetIp, deviceType, deviceId); - if (localIpSent == 0) { - if (DEBUG) { - Log.d(TAG, "Cannot send to target ip: " + HdHomeRunUtils.getIpString(targetIp)); - } - continue; - } - long timeout = System.currentTimeMillis() + HDHOMERUN_DISCOVER_TIMEOUT_MS * localIpSent; - while (System.currentTimeMillis() < timeout) { - HdHomeRunDiscoverDevice result = new HdHomeRunDiscoverDevice(); - if (!receive(result)) { - continue; - } - // Filter. - if (deviceType != HdHomeRunUtils.HDHOMERUN_DEVICE_TYPE_WILDCARD - && deviceType != result.mDeviceType) { - continue; - } - if (deviceId != HdHomeRunUtils.HDHOMERUN_DEVICE_ID_WILDCARD - && deviceId != result.mDeviceId) { - continue; - } - if (isObsoleteDevice(deviceId)) { - continue; - } - // Ensure not already in list. - if (resultList.contains(result)) { - continue; - } - // Add to list. - resultList.add(result); - if (resultList.size() >= maxCount) { - break; - } - } - } - return resultList; - } - - private boolean addSocket(int localIp, int subnetMask) { - for (int i = 1; i < mSockets.size(); i++) { - HdHomeRunDiscoverSocket discoverSocket = mSockets.get(i); - if ((discoverSocket.mLocalIp == localIp) - && (discoverSocket.mSubnetMask == subnetMask)) { - discoverSocket.mDetected = true; - return true; - } - } - if (mSockets.size() >= HDHOMERUN_DISCOVER_MAX_SOCK_COUNT) { - return false; - } - DatagramSocket socket; - try { - socket = new DatagramSocket(0, HdHomeRunUtils.intToAddress(localIp)); - socket.setBroadcast(true); - } catch (IOException e) { - if (DEBUG) Log.d(TAG, "Cannot create socket: " + HdHomeRunUtils.getIpString(localIp)); - return false; - } - // Write socket entry. - mSockets.add(new HdHomeRunDiscoverSocket(socket, true, localIp, subnetMask)); - return true; - } - - private void resetLocalIpSockets() { - for (int i = 1; i < mSockets.size(); i++) { - mSockets.get(i).mDetected = false; - mSockets.get(i).mDiscoverPacketSent = false; - } - List<LocalIpInfo> ipInfoList = getLocalIpInfo(HDHOMERUN_DISCOVER_MAX_SOCK_COUNT); - for (LocalIpInfo ipInfo : ipInfoList) { - if (DEBUG) { - Log.d( - TAG, - "Add local IP: " - + HdHomeRunUtils.getIpString(ipInfo.mIpAddress) - + ", " - + HdHomeRunUtils.getIpString(ipInfo.mSubnetMask)); - } - addSocket(ipInfo.mIpAddress, ipInfo.mSubnetMask); - } - Iterator<HdHomeRunDiscoverSocket> iterator = mSockets.iterator(); - while (iterator.hasNext()) { - HdHomeRunDiscoverSocket discoverSocket = iterator.next(); - if (!discoverSocket.mDetected) { - discoverSocket.close(); - iterator.remove(); - } - } - } - - private List<LocalIpInfo> getLocalIpInfo(int maxCount) { - Enumeration<NetworkInterface> interfaces; - try { - interfaces = NetworkInterface.getNetworkInterfaces(); - } catch (SocketException e) { - return Collections.emptyList(); - } - List<LocalIpInfo> result = new ArrayList<>(); - while (interfaces.hasMoreElements()) { - NetworkInterface networkInterface = interfaces.nextElement(); - for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) { - InetAddress inetAddress = interfaceAddress.getAddress(); - if (!inetAddress.isAnyLocalAddress() - && !inetAddress.isLinkLocalAddress() - && !inetAddress.isLoopbackAddress() - && !inetAddress.isMulticastAddress()) { - LocalIpInfo localIpInfo = new LocalIpInfo(); - localIpInfo.mIpAddress = HdHomeRunUtils.addressToInt(inetAddress.getAddress()); - localIpInfo.mSubnetMask = - (0x7fffffff >> (31 - interfaceAddress.getNetworkPrefixLength())); - result.add(localIpInfo); - if (result.size() >= maxCount) { - return result; - } - } - } - } - return result; - } - - private int send(int targetIp, int deviceType, int deviceId) { - return targetIp == 0 - ? sendWildcardIp(deviceType, deviceId) - : sendTargetIp(targetIp, deviceType, deviceId); - } - - private int sendWildcardIp(int deviceType, int deviceId) { - int localIpSent = 0; - - // Send subnet broadcast using each local ip socket. - // This will work with multiple separate 169.254.x.x interfaces. - for (int i = 1; i < mSockets.size(); i++) { - HdHomeRunDiscoverSocket discoverSocket = mSockets.get(i); - int targetIp = discoverSocket.mLocalIp | ~discoverSocket.mSubnetMask; - if (DEBUG) Log.d(TAG, "Send: " + HdHomeRunUtils.getIpString(targetIp)); - localIpSent += discoverSocket.send(targetIp, deviceType, deviceId) ? 1 : 0; - } - // If no local ip sockets then fall back to sending a global broadcast letting - // the OS choose the interface. - if (localIpSent == 0) { - if (DEBUG) Log.d(TAG, "Send: " + HdHomeRunUtils.getIpString(0xFFFFFFFF)); - localIpSent = mSockets.get(0).send(0xFFFFFFFF, deviceType, deviceId) ? 1 : 0; - } - return localIpSent; - } - - private int sendTargetIp(int targetIp, int deviceType, int deviceId) { - int localIpSent = 0; - - // Send targeted packet from any local ip that is in the same subnet. - // This will work with multiple separate 169.254.x.x interfaces. - for (int i = 1; i < mSockets.size(); i++) { - HdHomeRunDiscoverSocket discoverSocket = mSockets.get(i); - if (discoverSocket.mSubnetMask == 0) { - continue; - } - if ((targetIp & discoverSocket.mSubnetMask) - != (discoverSocket.mLocalIp & discoverSocket.mSubnetMask)) { - continue; - } - localIpSent += discoverSocket.send(targetIp, deviceType, deviceId) ? 1 : 0; - } - // If target IP does not match a local subnet then fall back to letting the OS choose - // the gateway interface. - if (localIpSent == 0) { - localIpSent = mSockets.get(0).send(targetIp, deviceType, deviceId) ? 1 : 0; - } - return localIpSent; - } - - private boolean receive(HdHomeRunDiscoverDevice result) { - for (HdHomeRunDiscoverSocket discoverSocket : mSockets) { - if (discoverSocket.mDiscoverPacketSent && discoverSocket.receive(result)) { - return true; - } - } - return false; - } - - private boolean isObsoleteDevice(int deviceId) { - switch (deviceId >> 20) { - case 0x100: /* TECH-US/TECH3-US */ - return (deviceId < 0x10040000); - case 0x120: /* TECH3-EU */ - return (deviceId < 0x12030000); - case 0x101: /* HDHR-US */ - case 0x102: /* HDHR-T1-US */ - case 0x103: /* HDHR3-US */ - case 0x111: /* HDHR3-DT */ - case 0x121: /* HDHR-EU */ - case 0x122: /* HDHR3-EU */ - return true; - default: - return false; - } - } - - static class HdHomeRunDiscoverDevice { - int mIpAddress; - int mDeviceType; - int mDeviceId; - int mTunerCount; - String mBaseUrl; - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } else if (other instanceof HdHomeRunDiscoverDevice) { - HdHomeRunDiscoverDevice o = (HdHomeRunDiscoverDevice) other; - return mIpAddress == o.mIpAddress - && mDeviceType == o.mDeviceType - && mDeviceId == o.mDeviceId; - } - return false; - } - - @Override - public int hashCode() { - int result = mIpAddress; - result = 31 * result + mDeviceType; - result = 31 * result + mDeviceId; - return result; - } - } - - private static class HdHomeRunDiscoverSocket { - DatagramSocket mSocket; - boolean mDetected; - boolean mDiscoverPacketSent; - int mLocalIp; - int mSubnetMask; - - private HdHomeRunDiscoverSocket( - DatagramSocket socket, boolean detected, int localIp, int subnetMask) { - mSocket = socket; - mDetected = detected; - mLocalIp = localIp; - mSubnetMask = subnetMask; - } - - private boolean send(int targetIp, int deviceType, int deviceId) { - byte[] data = new byte[12]; - ByteBuffer buffer = ByteBuffer.wrap(data); - buffer.put(HdHomeRunUtils.HDHOMERUN_TAG_DEVICE_TYPE); - buffer.put((byte) 4); - buffer.putInt(deviceType); - buffer.put(HdHomeRunUtils.HDHOMERUN_TAG_DEVICE_ID); - buffer.put((byte) 4); - buffer.putInt(deviceId); - data = HdHomeRunUtils.sealFrame(data, HdHomeRunUtils.HDHOMERUN_TYPE_DISCOVER_REQUEST); - try { - DatagramPacket packet = - new DatagramPacket( - data, - data.length, - HdHomeRunUtils.intToAddress(targetIp), - HdHomeRunUtils.HDHOMERUN_DISCOVER_UDP_PORT); - mSocket.send(packet); - if (DEBUG) { - Log.d(TAG, "Discover packet sent to: " + HdHomeRunUtils.getIpString(targetIp)); - } - mDiscoverPacketSent = true; - } catch (IOException e) { - if (DEBUG) { - Log.d( - TAG, - "Cannot send discover packet to socket(" - + HdHomeRunUtils.getIpString(mLocalIp) - + ")"); - } - mDiscoverPacketSent = false; - } - return mDiscoverPacketSent; - } - - private boolean receive(HdHomeRunDiscoverDevice result) { - DatagramPacket packet = new DatagramPacket(new byte[3074], 3074); - try { - mSocket.setSoTimeout(HDHOMERUN_DISCOVER_RECEIVE_WAITE_TIME_MS); - mSocket.receive(packet); - if (DEBUG) Log.d(TAG, "Received packet, size: " + packet.getLength()); - } catch (IOException e) { - if (DEBUG) { - Log.d( - TAG, - "Cannot receive from socket(" - + HdHomeRunUtils.getIpString(mLocalIp) - + ")"); - } - return false; - } - - Pair<Short, byte[]> data = - HdHomeRunUtils.openFrame(packet.getData(), packet.getLength()); - if (data == null - || data.first == null - || data.first != HdHomeRunUtils.HDHOMERUN_TYPE_DISCOVER_REPLY) { - if (DEBUG) Log.d(TAG, "Ill-formed packet: " + Arrays.toString(packet.getData())); - return false; - } - result.mIpAddress = HdHomeRunUtils.addressToInt(packet.getAddress().getAddress()); - if (DEBUG) { - Log.d(TAG, "Get Device IP: " + HdHomeRunUtils.getIpString(result.mIpAddress)); - } - ByteBuffer buffer = ByteBuffer.wrap(data.second); - while (true) { - Pair<Byte, byte[]> tagAndValue = HdHomeRunUtils.readTaggedValue(buffer); - if (tagAndValue == null) { - break; - } - switch (tagAndValue.first) { - case HdHomeRunUtils.HDHOMERUN_TAG_DEVICE_TYPE: - if (tagAndValue.second.length != 4) { - break; - } - result.mDeviceType = ByteBuffer.wrap(tagAndValue.second).getInt(); - if (DEBUG) Log.d(TAG, "Get Device Type: " + result.mDeviceType); - break; - case HdHomeRunUtils.HDHOMERUN_TAG_DEVICE_ID: - if (tagAndValue.second.length != 4) { - break; - } - result.mDeviceId = ByteBuffer.wrap(tagAndValue.second).getInt(); - if (DEBUG) Log.d(TAG, "Get Device ID: " + result.mDeviceId); - break; - case HdHomeRunUtils.HDHOMERUN_TAG_TUNER_COUNT: - if (tagAndValue.second.length != 1) { - break; - } - result.mTunerCount = tagAndValue.second[0]; - if (DEBUG) Log.d(TAG, "Get Tuner Count: " + result.mTunerCount); - break; - case HdHomeRunUtils.HDHOMERUN_TAG_BASE_URL: - result.mBaseUrl = new String(tagAndValue.second); - if (DEBUG) Log.d(TAG, "Get Base URL: " + result.mBaseUrl); - break; - default: - break; - } - } - // Fixup for old firmware. - if (result.mTunerCount == 0) { - switch (result.mDeviceId >> 20) { - case 0x102: - result.mTunerCount = 1; - break; - case 0x100: - case 0x101: - case 0x121: - result.mTunerCount = 2; - break; - default: - break; - } - } - return true; - } - - private void close() { - if (mSocket != null) { - mSocket.close(); - } - } - } - - private static class LocalIpInfo { - int mIpAddress; - int mSubnetMask; - } -} diff --git a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunInterface.java b/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunInterface.java deleted file mode 100644 index 2928aba7..00000000 --- a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunInterface.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.hdhomerun; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.WorkerThread; -import android.util.Log; -import com.android.tv.tuner.hdhomerun.HdHomeRunDiscover.HdHomeRunDiscoverDevice; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** An interface class provides methods to access physical HDHomeRun devices. */ -@WorkerThread -public class HdHomeRunInterface { - private static final String TAG = "HdHomeRunInterface"; - private static final boolean DEBUG = false; - - private static final int FETCH_DEVICE_NAME_TRY_NUM = 2; - private static final int MAX_DEVICES = 1; - private static final boolean DISABLE_CABLE = false; - - /** - * Scans for HDHomeRun devices on the network. - * - * @param deviceId The target device ID we want to find, scans for all available devices if - * {@code null} or the given ID cannot be found. - * @return A set of HDHomeRun devices - */ - @NonNull - public static Set<HdHomeRunDevice> scanDevices(Integer deviceId) { - List<HdHomeRunDiscoverDevice> discoveredDevices = null; - if (deviceId != null) { - discoveredDevices = - HdHomeRunUtils.findHdHomeRunDevices( - 0, HdHomeRunUtils.HDHOMERUN_DEVICE_TYPE_TUNER, deviceId, 1); - if (discoveredDevices.isEmpty()) { - Log.i(TAG, "Can't find device with ID: " + deviceId); - } - } - if (discoveredDevices == null || discoveredDevices.isEmpty()) { - discoveredDevices = - HdHomeRunUtils.findHdHomeRunDevices( - 0, - HdHomeRunUtils.HDHOMERUN_DEVICE_TYPE_TUNER, - HdHomeRunUtils.HDHOMERUN_DEVICE_ID_WILDCARD, - MAX_DEVICES); - if (DEBUG) Log.d(TAG, "Found " + discoveredDevices.size() + " devices"); - } - Set<HdHomeRunDevice> result = new HashSet<>(); - for (HdHomeRunDiscoverDevice discoveredDevice : discoveredDevices) { - String model = - fetchDeviceModel(discoveredDevice.mDeviceId, discoveredDevice.mIpAddress); - if (model == null) { - Log.e(TAG, "Fetching device model failed: " + discoveredDevice.mDeviceId); - continue; - } else if (DEBUG) { - Log.d(TAG, "Fetch Device Model: " + model); - } - if (DISABLE_CABLE) { - if (model != null && model.contains("cablecard")) { - // filter out CableCARD devices - continue; - } - } - for (int i = 0; i < discoveredDevice.mTunerCount; i++) { - result.add( - new HdHomeRunDevice( - discoveredDevice.mIpAddress, - discoveredDevice.mDeviceType, - discoveredDevice.mDeviceId, - i, - model)); - } - } - return result; - } - - /** - * Returns {@code true} if the given device IP, ID and tuner index is available for use. - * - * @param deviceId The target device ID, 0 denotes a wildcard match. - * @param deviceIp The target device IP. - * @param tunerIndex The target tuner index of the target device. This parameter is only - * meaningful when the target device has multiple tuners. - */ - public static boolean isDeviceAvailable(int deviceId, int deviceIp, int tunerIndex) { - // TODO: check the lock state for the given tuner. - if ((deviceId == 0) && (deviceIp == 0)) { - return false; - } - if (HdHomeRunUtils.isIpMulticast(deviceIp)) { - return false; - } - if ((deviceId == 0) || (deviceId == HdHomeRunUtils.HDHOMERUN_DEVICE_ID_WILDCARD)) { - try (HdHomeRunControlSocket controlSock = - new HdHomeRunControlSocket(deviceId, deviceIp)) { - deviceId = controlSock.getDeviceId(); - } - } - return deviceId != 0; - } - - @Nullable - private static String fetchDeviceModel(int deviceId, int deviceIp) { - for (int i = 0; i < FETCH_DEVICE_NAME_TRY_NUM; i++) { - try (HdHomeRunControlSocket controlSock = - new HdHomeRunControlSocket(deviceId, deviceIp)) { - String model = controlSock.get("/sys/model"); - if (model != null) { - return model; - } - } - } - return null; - } - - private HdHomeRunInterface() {} -} diff --git a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunTunerHal.java b/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunTunerHal.java deleted file mode 100644 index 81682991..00000000 --- a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunTunerHal.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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.hdhomerun; - -import android.content.Context; -import android.text.TextUtils; -import android.util.Log; -import com.android.tv.common.SoftPreconditions; -import com.android.tv.common.compat.TvInputConstantCompat; -import com.android.tv.tuner.api.Tuner; -import com.android.tv.tuner.data.TunerChannel; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; - -/** Tuner implementation for HdHomeRun */ -public class HdHomeRunTunerHal implements Tuner { - private static final String TAG = "HdHomeRunTunerHal"; - private static final boolean DEBUG = false; - - private static final String CABLECARD_MODEL = "cablecard"; - private static final String ATSC_MODEL = "atsc"; - private static final String DVBC_MODEL = "dvbc"; - private static final String DVBT_MODEL = "dvbt"; - - private final HdHomeRunTunerManager mTunerManager; - private HdHomeRunDevice mDevice; - private BufferedInputStream mInputStream; - private HttpURLConnection mConnection; - private String mHttpConnectionAddress; - private final Context mContext; - - @DeliverySystemType private int mDeliverySystemType = DELIVERY_SYSTEM_UNDEFINED; - - public static final char VCHANNEL_SEPARATOR = '.'; - public static final int CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION = 3000; // 3 sec - public static final int READ_TIMEOUT_MS_FOR_URLCONNECTION = 10000; // 10 sec - - public HdHomeRunTunerHal(Context context) { - mTunerManager = HdHomeRunTunerManager.getInstance(); - mContext = context; - } - - @Override - public boolean openFirstAvailable() { - SoftPreconditions.checkState(mDevice == null); - try { - mDevice = mTunerManager.acquireDevice(mContext); - if (mDevice != null) { - if (mDeliverySystemType == DELIVERY_SYSTEM_UNDEFINED) { - mDeliverySystemType = nativeGetDeliverySystemType(getDeviceId()); - } - } - return mDevice != null; - } catch (Exception e) { - Log.w(TAG, "Failed to open first available device", e); - return false; - } - } - - @Override - public boolean isDeviceOpen() { - return mDevice != null; - } - - @Override - public boolean isReusable() { - return false; - } - - @Override - public long getDeviceId() { - return mDevice == null ? 0 : mDevice.getDeviceId(); - } - - @Override - public void close() throws Exception { - closeInputStreamAndDisconnect(); - if (mDevice != null) { - mTunerManager.releaseDevice(mDevice); - mDevice = null; - } - } - - @Override - public synchronized boolean tune( - int frequency, @ModulationType String modulation, String channelNumber) { - if (DEBUG) { - Log.d( - TAG, - "tune(frequency=" - + frequency - + ", modulation=" - + modulation - + ", channelNumber=" - + channelNumber - + ")"); - } - closeInputStreamAndDisconnect(); - if (TextUtils.isEmpty(channelNumber)) { - return false; - } - channelNumber = - channelNumber.replace(TunerChannel.CHANNEL_NUMBER_SEPARATOR, VCHANNEL_SEPARATOR); - mHttpConnectionAddress = "http://" + getIpAddress() + ":5004/auto/v" + channelNumber; - return connectAndOpenInputStream(); - } - - private boolean connectAndOpenInputStream() { - URL url; - try { - url = new URL(mHttpConnectionAddress); - } catch (MalformedURLException e) { - Log.e(TAG, "Invalid address: " + mHttpConnectionAddress, e); - return false; - } - URLConnection connection; - try { - connection = url.openConnection(); - connection.setConnectTimeout(CONNECTION_TIMEOUT_MS_FOR_URLCONNECTION); - connection.setReadTimeout(READ_TIMEOUT_MS_FOR_URLCONNECTION); - if (connection instanceof HttpURLConnection) { - mConnection = (HttpURLConnection) connection; - } - } catch (IOException e) { - Log.e(TAG, "Connection failed: " + mHttpConnectionAddress, e); - return false; - } - try { - mInputStream = new BufferedInputStream(connection.getInputStream()); - } catch (IOException e) { - closeInputStreamAndDisconnect(); - Log.e(TAG, "Failed to get input stream from " + mHttpConnectionAddress, e); - return false; - } - if (DEBUG) Log.d(TAG, "tuning to " + mHttpConnectionAddress); - return true; - } - - @Override - public synchronized boolean addPidFilter(int pid, @FilterType int filterType) { - // no-op - return true; - } - - @Override - public synchronized void stopTune() { - closeInputStreamAndDisconnect(); - } - - @Override - public synchronized int readTsStream(byte[] javaBuffer, int javaBufferSize) { - if (mInputStream != null) { - try { - // Note: this call sometimes take more than 500ms, because the data is - // streamed through network unlike connected tuner devices. - return mInputStream.read(javaBuffer, 0, javaBufferSize); - } catch (IOException e) { - Log.e(TAG, "Failed to read stream", e); - closeInputStreamAndDisconnect(); - } - } - if (connectAndOpenInputStream()) { - Log.w(TAG, "Tuned by http connection again"); - } else { - Log.e(TAG, "Tuned by http connection again failed"); - } - return 0; - } - - @Override - public void setHasPendingTune(boolean hasPendingTune) { - // no-op - } - - protected int nativeGetDeliverySystemType(long deviceId) { - String deviceModel = mDevice.getDeviceModel(); - if (SoftPreconditions.checkState(!TextUtils.isEmpty(deviceModel))) { - if (deviceModel.contains(CABLECARD_MODEL) || deviceModel.contains(ATSC_MODEL)) { - return DELIVERY_SYSTEM_ATSC; - } else if (deviceModel.contains(DVBC_MODEL)) { - return DELIVERY_SYSTEM_DVBC; - } else if (deviceModel.contains(DVBT_MODEL)) { - return DELIVERY_SYSTEM_DVBT; - } - } - return DELIVERY_SYSTEM_UNDEFINED; - } - - private void closeInputStreamAndDisconnect() { - if (mInputStream != null) { - try { - mInputStream.close(); - } catch (IOException e) { - Log.e(TAG, "Failed to close input stream", e); - } - mInputStream = null; - } - if (mConnection != null) { - mConnection.disconnect(); - mConnection = null; - } - } - - /** Gets the number of tuners in a given HDHomeRun devices. */ - public static int getNumberOfDevices() { - return HdHomeRunTunerManager.getInstance().getTunerCount(); - } - - /** Returns the IP address. */ - public String getIpAddress() { - return HdHomeRunUtils.getIpString(mDevice.getIpAddress()); - } - - /** - * Marks the device associated to this instance as a scanned device. Scanned device has higher - * priority among multiple HDHomeRun devices. - */ - public void markAsScannedDevice(Context context) { - HdHomeRunTunerManager.markAsScannedDevice(context, mDevice); - } - - @Override - @DeliverySystemType - public int getDeliverySystemType() { - return Tuner.DELIVERY_SYSTEM_UNDEFINED; - } - - @Override - public int getSignalStrength() { - return TvInputConstantCompat.SIGNAL_STRENGTH_NOT_USED; - } -} diff --git a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunTunerHalFactory.java b/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunTunerHalFactory.java deleted file mode 100644 index 6f6b1864..00000000 --- a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunTunerHalFactory.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2019 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.hdhomerun; - -import android.content.Context; -import android.support.annotation.WorkerThread; -import android.util.Pair; - -import com.android.tv.tuner.api.Tuner; -import com.android.tv.tuner.api.TunerFactory; - -/** TunerHal factory that creates all built in tuner types. */ -public final class HdHomeRunTunerHalFactory implements TunerFactory { - public static final TunerFactory INSTANCE = new HdHomeRunTunerHalFactory(); - - private HdHomeRunTunerHalFactory() {} - /** - * Creates a TunerHal instance. - * - * @param context context for creating the TunerHal instance - * @return the TunerHal instance - */ - @Override - @WorkerThread - public synchronized Tuner createInstance(Context context) { - Tuner tunerHal = null; - if (tunerHal == null) { - tunerHal = new HdHomeRunTunerHal(context); - } - return tunerHal.openFirstAvailable() ? tunerHal : null; - } - - /** - * Returns if tuner input service would use built-in tuners instead of USB tuners or network - * tuners. - */ - @Override - public boolean useBuiltInTuner(Context context) { - return false; - } - - /** Gets the number of tuner devices currently present. */ - @Override - @WorkerThread - public Pair<Integer, Integer> getTunerTypeAndCount(Context context) { - return Pair.create(Tuner.TUNER_TYPE_NETWORK, HdHomeRunTunerHal.getNumberOfDevices()); - } -} diff --git a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunTunerManager.java b/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunTunerManager.java deleted file mode 100644 index 9e3ea595..00000000 --- a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunTunerManager.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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.hdhomerun; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.support.annotation.WorkerThread; -import android.util.Log; -import java.util.HashSet; -import java.util.Set; - -/** - * A class to manage tuner resources of HDHomeRun devices. It handles tuner resource acquisition and - * release. - */ -class HdHomeRunTunerManager { - private static final String TAG = "HdHomeRunTunerManager"; - private static final boolean DEBUG = false; - - private static final String PREF_KEY_SCANNED_DEVICE_ID = "scanned_device_id"; - - private static HdHomeRunTunerManager sInstance; - - private final Set<HdHomeRunDevice> mHdHomeRunDevices = new HashSet<>(); - private final Set<HdHomeRunDevice> mUsedDevices = new HashSet<>(); - - private HdHomeRunTunerManager() {} - - /** Returns the instance of this manager. */ - public static synchronized HdHomeRunTunerManager getInstance() { - if (sInstance == null) { - sInstance = new HdHomeRunTunerManager(); - } - return sInstance; - } - - /** Returns number of tuners. */ - @WorkerThread - synchronized int getTunerCount() { - updateDevicesLocked(null); - if (DEBUG) Log.d(TAG, "getTunerCount: " + mHdHomeRunDevices.size()); - return mHdHomeRunDevices.size(); - } - - /** Creates an HDHomeRun device. If there is no available one, returns {@code null}. */ - @WorkerThread - synchronized HdHomeRunDevice acquireDevice(Context context) { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); - int scannedDeviceId = sp.getInt(PREF_KEY_SCANNED_DEVICE_ID, 0); - updateDevicesLocked(scannedDeviceId == 0 ? null : scannedDeviceId); - if (DEBUG) Log.d(TAG, "createDevice: device count = " + mHdHomeRunDevices.size()); - HdHomeRunDevice availableDevice = null; - // Use the device used for scanning first since other devices might have different line-up. - if (scannedDeviceId != 0) { - for (HdHomeRunDevice device : mHdHomeRunDevices) { - if (!mUsedDevices.contains(device) && scannedDeviceId == device.getDeviceId()) { - if (!HdHomeRunInterface.isDeviceAvailable( - device.getDeviceId(), device.getIpAddress(), device.getTunerIndex())) { - if (DEBUG) Log.d(TAG, "Device not available: " + device); - continue; - } - availableDevice = device; - break; - } - } - } - if (availableDevice == null) { - for (HdHomeRunDevice device : mHdHomeRunDevices) { - if (!mUsedDevices.contains(device)) { - if (!HdHomeRunInterface.isDeviceAvailable( - device.getDeviceId(), device.getIpAddress(), device.getTunerIndex())) { - if (DEBUG) Log.d(TAG, "Device not available: " + device); - continue; - } - availableDevice = device; - break; - } - } - } - if (availableDevice != null) { - if (DEBUG) Log.d(TAG, "created device " + availableDevice); - mUsedDevices.add(availableDevice); - return availableDevice; - } - return null; - } - - /** Releases a created device by {@link #acquireDevice(Context)}. */ - synchronized void releaseDevice(HdHomeRunDevice device) { - if (DEBUG) Log.d(TAG, "releaseDevice: " + device); - mUsedDevices.remove(device); - } - - /** - * Marks the device associated to this instance as a scanned device. Scanned device has higher - * priority among multiple HDHomeRun devices. - */ - static void markAsScannedDevice(Context context, HdHomeRunDevice device) { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); - sp.edit().putInt(PREF_KEY_SCANNED_DEVICE_ID, device.getDeviceId()).apply(); - } - - private void updateDevicesLocked(Integer deviceId) { - mHdHomeRunDevices.clear(); - mHdHomeRunDevices.addAll(HdHomeRunInterface.scanDevices(deviceId)); - } -} diff --git a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunUtils.java b/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunUtils.java deleted file mode 100644 index 733fc96f..00000000 --- a/tuner/src/com/android/tv/tuner/hdhomerun/HdHomeRunUtils.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2017 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.hdhomerun; - -import android.support.annotation.NonNull; -import android.util.Log; -import android.util.Pair; - -import com.android.tv.tuner.hdhomerun.HdHomeRunDiscover.HdHomeRunDiscoverDevice; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.zip.CRC32; - -class HdHomeRunUtils { - private static final String TAG = "HdHomeRunUtils"; - private static final boolean DEBUG = false; - - static final int HDHOMERUN_DEVICE_TYPE_WILDCARD = 0xFFFFFFFF; - static final int HDHOMERUN_DEVICE_TYPE_TUNER = 0x00000001; - static final int HDHOMERUN_DEVICE_ID_WILDCARD = 0xFFFFFFFF; - - static final int HDHOMERUN_DISCOVER_UDP_PORT = 65001; - static final int HDHOMERUN_CONTROL_TCP_PORT = 65001; - - static final short HDHOMERUN_TYPE_INVALID = -1; - static final short HDHOMERUN_TYPE_DISCOVER_REQUEST = 0x0002; - static final short HDHOMERUN_TYPE_DISCOVER_REPLY = 0x0003; - static final short HDHOMERUN_TYPE_GETSET_REQUEST = 0x0004; - static final short HDHOMERUN_TYPE_GETSET_REPLY = 0x0005; - - static final byte HDHOMERUN_TAG_DEVICE_TYPE = 0x01; - static final byte HDHOMERUN_TAG_DEVICE_ID = 0x02; - static final byte HDHOMERUN_TAG_GETSET_NAME = 0x03; - static final int HDHOMERUN_TAG_GETSET_VALUE = 0x04; - static final int HDHOMERUN_TAG_ERROR_MESSAGE = 0x05; - static final int HDHOMERUN_TAG_TUNER_COUNT = 0x10; - static final int HDHOMERUN_TAG_BASE_URL = 0x2A; - - static final int HDHOMERUN_CONTROL_CONNECT_TIMEOUT_MS = 2500; - static final int HDHOMERUN_CONTROL_SEND_TIMEOUT_MS = 2500; - static final int HDHOMERUN_CONTROL_RECEIVE_TIMEOUT_MS = 2500; - - /** - * Finds HDHomeRun devices with given IP, type, and ID. - * - * @param targetIp {@code 0} to find target devices with broadcasting. - * @param deviceType The type of target devices. - * @param deviceId The ID of target devices. - * @param maxCount Maximum number of devices should be returned. - */ - @NonNull - static List<HdHomeRunDiscoverDevice> findHdHomeRunDevices( - int targetIp, int deviceType, int deviceId, int maxCount) { - if (isIpMulticast(targetIp)) { - if (DEBUG) Log.d(TAG, "Target IP cannot be multicast IP."); - return Collections.emptyList(); - } - try { - HdHomeRunDiscover ds = HdHomeRunDiscover.create(); - if (ds == null) { - if (DEBUG) Log.d(TAG, "Cannot create discover object."); - return Collections.emptyList(); - } - List<HdHomeRunDiscoverDevice> result = - ds.findDevices(targetIp, deviceType, deviceId, maxCount); - ds.close(); - return result; - } catch (Exception e) { - Log.w(TAG, "Failed to find HdHomeRun Devices", e); - return Collections.emptyList(); - } - } - - /** Returns {@code true} if the given IP is a multi-cast IP. */ - static boolean isIpMulticast(long ip) { - return (ip >= 0xE0000000) && (ip < 0xF0000000); - } - - /** Translates a {@code byte[]} address to its integer representation. */ - static int addressToInt(byte[] address) { - return ByteBuffer.wrap(address).order(ByteOrder.LITTLE_ENDIAN).getInt(); - } - - /** Translates an {@code int} address to a corresponding {@link InetAddress}. */ - static InetAddress intToAddress(int address) throws UnknownHostException { - return InetAddress.getByAddress( - ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(address).array()); - } - - /** Gets {@link String} representation of an {@code int} address. */ - static String getIpString(int ip) { - return String.format( - "%d.%d.%d.%d", (ip & 0xff), (ip >> 8 & 0xff), (ip >> 16 & 0xff), (ip >> 24 & 0xff)); - } - - /** - * Opens the packet returned from HDHomeRun devices to acquire the real content and verify it. - */ - static Pair<Short, byte[]> openFrame(byte[] data, int length) { - if (length < 4) { - return null; - } - ByteBuffer buffer = ByteBuffer.wrap(data); - short resultType = buffer.getShort(); - int dataLength = buffer.getShort() & 0xffff; - - if (dataLength + 8 > length) { - // Not finished yet. - return null; - } - byte[] result = new byte[dataLength]; - buffer.get(result); - byte[] calculatedCrc = getCrcFromBytes(Arrays.copyOfRange(data, 0, dataLength + 4)); - byte[] packetCrc = new byte[4]; - buffer.get(packetCrc); - - if (!Arrays.equals(calculatedCrc, packetCrc)) { - return Pair.create(HDHOMERUN_TYPE_INVALID, null); - } - - return Pair.create(resultType, result); - } - - /** Seals the contents in a packet to send to HDHomeRun devices. */ - static byte[] sealFrame(byte[] data, short frameType) { - byte[] result = new byte[data.length + 8]; - ByteBuffer buffer = ByteBuffer.wrap(result); - buffer.putShort(frameType); - buffer.putShort((short) data.length); - buffer.put(data); - buffer.put(getCrcFromBytes(Arrays.copyOfRange(result, 0, data.length + 4))); - return result; - } - - /** Reads a (tag, value) pair from packets returned from HDHomeRun devices. */ - static Pair<Byte, byte[]> readTaggedValue(ByteBuffer buffer) { - try { - Byte tag = buffer.get(); - byte[] value = readVarLength(buffer); - return Pair.create(tag, value); - } catch (BufferUnderflowException e) { - return null; - } - } - - private static byte[] readVarLength(ByteBuffer buffer) { - short length; - Byte lengthByte1 = buffer.get(); - if ((lengthByte1 & 0x80) != 0) { - length = buffer.get(); - length = (short) ((length << 7) + (lengthByte1 & 0x7F)); - } else { - length = lengthByte1; - } - byte[] result = new byte[length]; - buffer.get(result); - return result; - } - - private static byte[] getCrcFromBytes(byte[] data) { - CRC32 crc32 = new CRC32(); - crc32.update(data); - long crc = crc32.getValue(); - byte[] result = new byte[4]; - for (int offset = 0; offset < 4; offset++) { - result[offset] = (byte) (crc & 0xFF); - crc >>= 8; - } - return result; - } - - private HdHomeRunUtils() {} -} diff --git a/tuner/src/com/android/tv/tuner/modules/TunerModule.java b/tuner/src/com/android/tv/tuner/modules/TunerModule.java index ff86e09f..4843f383 100644 --- a/tuner/src/com/android/tv/tuner/modules/TunerModule.java +++ b/tuner/src/com/android/tv/tuner/modules/TunerModule.java @@ -15,86 +15,9 @@ */ package com.android.tv.tuner.modules; -import com.android.tv.tuner.exoplayer.ExoPlayerSampleExtractor; -import com.android.tv.tuner.exoplayer.ExoPlayerSampleExtractorFactory; -import com.android.tv.tuner.exoplayer.FileSampleExtractor; -import com.android.tv.tuner.exoplayer.FileSampleExtractorFactory; -import com.android.tv.tuner.exoplayer.MpegTsRendererBuilder; -import com.android.tv.tuner.exoplayer.MpegTsRendererBuilderFactory; -import com.android.tv.tuner.exoplayer.MpegTsSampleExtractor; -import com.android.tv.tuner.exoplayer.MpegTsSampleExtractorFactory; -import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBuffer; -import com.android.tv.tuner.exoplayer.buffer.RecordingSampleBufferFactory; -import com.android.tv.tuner.exoplayer.buffer.SampleChunkIoHelper; -import com.android.tv.tuner.exoplayer.buffer.SampleChunkIoHelperFactory; import com.android.tv.tuner.source.TunerSourceModule; -import com.android.tv.tuner.tvinput.TunerRecordingSessionFactoryImpl; -import com.android.tv.tuner.tvinput.TunerRecordingSessionWorker; -import com.android.tv.tuner.tvinput.TunerRecordingSessionWorkerFactory; -import com.android.tv.tuner.tvinput.TunerSessionExoV2Factory; -import com.android.tv.tuner.tvinput.TunerSessionV1Factory; -import com.android.tv.tuner.tvinput.TunerSessionWorker; -import com.android.tv.tuner.tvinput.TunerSessionWorkerExoV2; -import com.android.tv.tuner.tvinput.TunerSessionWorkerExoV2Factory; -import com.android.tv.tuner.tvinput.TunerSessionWorkerFactory; -import com.android.tv.tuner.tvinput.factory.TunerRecordingSessionFactory; -import com.android.tv.tuner.tvinput.factory.TunerSessionFactory; - -import dagger.Binds; import dagger.Module; -import dagger.Provides; - -import com.android.tv.common.flags.TunerFlags; /** Dagger module for TV Tuners. */ @Module(includes = {TunerSingletonsModule.class, TunerSourceModule.class}) -public abstract class TunerModule { - - @Provides - static TunerSessionFactory tunerSessionFactory( - TunerFlags tunerFlags, - TunerSessionV1Factory tunerSessionFactory, - TunerSessionExoV2Factory tunerSessionExoV2Factory) { - return tunerFlags.useExoplayerV2() ? tunerSessionExoV2Factory : tunerSessionFactory; - } - - @Binds - abstract TunerRecordingSessionWorker.Factory tunerRecordingSessionWorkerFactory( - TunerRecordingSessionWorkerFactory tunerRecordingSessionWorkerFactory); - - @Binds - abstract TunerSessionWorker.Factory tunerSessionWorkerFactory( - TunerSessionWorkerFactory tunerSessionWorkerFactory); - - @Binds - abstract TunerSessionWorkerExoV2.Factory tunerSessionWorkerExoV2Factory( - TunerSessionWorkerExoV2Factory tunerSessionWorkerExoV2Factory); - - @Binds - abstract TunerRecordingSessionFactory tunerRecordingSessionFactory( - TunerRecordingSessionFactoryImpl impl); - - @Binds - abstract MpegTsRendererBuilder.Factory mpegTsRendererBuilderFactory( - MpegTsRendererBuilderFactory mpegTsRendererBuilderFactory); - - @Binds - abstract MpegTsSampleExtractor.Factory mpegTsSampleExtractorFactory( - MpegTsSampleExtractorFactory mpegTsSampleExtractorFactory); - - @Binds - abstract FileSampleExtractor.Factory fileSampleExtractorFactory( - FileSampleExtractorFactory fileSampleExtractorFactory); - - @Binds - abstract RecordingSampleBuffer.Factory recordingSampleBufferFactory( - RecordingSampleBufferFactory recordingSampleBufferFactory); - - @Binds - abstract ExoPlayerSampleExtractor.Factory exoPlayerSampleExtractorFactory( - ExoPlayerSampleExtractorFactory exoPlayerSampleExtractorFactory); - - @Binds - abstract SampleChunkIoHelper.Factory sampleChunkIoHelperFactory( - SampleChunkIoHelperFactory sampleChunkIoHelperFactory); -} +public class TunerModule {} diff --git a/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java b/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java index 05026907..44f689bf 100644 --- a/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java +++ b/tuner/src/com/android/tv/tuner/setup/BaseTunerSetupActivity.java @@ -75,14 +75,11 @@ public abstract class BaseTunerSetupActivity extends SetupActivity { R.raw.ut_kr_cable_standard_center_frequencies_qam256, R.raw.ut_kr_all, R.raw.ut_kr_dev_cj_cable_center_frequencies_qam256, + R.raw.ut_euro_dvbt_all, + R.raw.ut_euro_dvbt_all, R.raw.ut_euro_dvbt_all - /* these two resource files are obsolete and removed, so comment them out - R.raw.ut_euro_all, - R.raw.ut_euro_all */ }; - protected final String mInputId; - protected ScanFragment mLastScanFragment; protected Integer mTunerType; protected boolean mNeedToShowPostalCodeFragment; @@ -93,10 +90,6 @@ public abstract class BaseTunerSetupActivity extends SetupActivity { private TunerHalCreator mTunerHalCreator; - protected BaseTunerSetupActivity(String mInputId) { - this.mInputId = mInputId; - } - @Override protected void onCreate(Bundle savedInstanceState) { if (DEBUG) { @@ -229,7 +222,6 @@ public abstract class BaseTunerSetupActivity extends SetupActivity { args1.putInt( ScanFragment.EXTRA_FOR_CHANNEL_SCAN_FILE, CHANNEL_MAP_SCAN_FILE[actionId]); args1.putInt(KEY_TUNER_TYPE, mTunerType); - args1.putString(ScanFragment.EXTRA_FOR_INPUT_ID, mInputId); mLastScanFragment.setArguments(args1); showFragment(mLastScanFragment, true); return true; diff --git a/tuner/src/com/android/tv/tuner/setup/ChannelScanFileParser.java b/tuner/src/com/android/tv/tuner/setup/ChannelScanFileParser.java index 2e782705..43c584ed 100644 --- a/tuner/src/com/android/tv/tuner/setup/ChannelScanFileParser.java +++ b/tuner/src/com/android/tv/tuner/setup/ChannelScanFileParser.java @@ -56,7 +56,6 @@ public final class ChannelScanFileParser { } scanChannelList.add( ScanChannel.forTuner( - tokens[0], Integer.parseInt(tokens[1]), tokens[2], tokens.length == 4 ? Integer.parseInt(tokens[3]) : null)); diff --git a/tuner/src/com/android/tv/tuner/setup/ConnectionTypeFragment.java b/tuner/src/com/android/tv/tuner/setup/ConnectionTypeFragment.java index db297426..ebe4e41e 100644 --- a/tuner/src/com/android/tv/tuner/setup/ConnectionTypeFragment.java +++ b/tuner/src/com/android/tv/tuner/setup/ConnectionTypeFragment.java @@ -18,12 +18,14 @@ package com.android.tv.tuner.setup; import android.os.Bundle; import android.support.annotation.NonNull; -import androidx.leanback.widget.GuidanceStylist.Guidance; -import androidx.leanback.widget.GuidedAction; +import android.support.v17.leanback.widget.GuidanceStylist.Guidance; +import android.support.v17.leanback.widget.GuidedAction; +import com.android.tv.common.BuildConfig; import com.android.tv.common.ui.setup.SetupGuidedStepFragment; import com.android.tv.common.ui.setup.SetupMultiPaneFragment; import com.android.tv.tuner.R; import java.util.List; +import java.util.TimeZone; /** A fragment for connection type selection. */ public class ConnectionTypeFragment extends SetupMultiPaneFragment { @@ -65,6 +67,7 @@ public class ConnectionTypeFragment extends SetupMultiPaneFragment { /** The content fragment of {@link ConnectionTypeFragment}. */ public static class ContentFragment extends SetupGuidedStepFragment { + @NonNull @Override public Guidance onCreateGuidance(Bundle savedInstanceState) { diff --git a/tuner/src/com/android/tv/tuner/setup/LineupFragment.java b/tuner/src/com/android/tv/tuner/setup/LineupFragment.java index 224237d9..41f755df 100644 --- a/tuner/src/com/android/tv/tuner/setup/LineupFragment.java +++ b/tuner/src/com/android/tv/tuner/setup/LineupFragment.java @@ -20,8 +20,8 @@ import android.content.res.Resources; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import androidx.leanback.widget.GuidanceStylist.Guidance; -import androidx.leanback.widget.GuidedAction; +import android.support.v17.leanback.widget.GuidanceStylist.Guidance; +import android.support.v17.leanback.widget.GuidedAction; import android.util.Log; import android.view.View; import com.android.tv.common.ui.setup.SetupGuidedStepFragment; diff --git a/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java b/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java new file mode 100644 index 00000000..741edc78 --- /dev/null +++ b/tuner/src/com/android/tv/tuner/setup/LiveTvTunerSetupActivity.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2015 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.setup; + +import android.app.FragmentManager; +import android.content.pm.PackageManager; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.view.KeyEvent; +import com.android.tv.common.util.PostalCodeUtils; +import dagger.android.ContributesAndroidInjector; + +/** An activity that serves tuner setup process. */ +public class LiveTvTunerSetupActivity extends BaseTunerSetupActivity { + private static final String TAG = "LiveTvTunerSetupActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // TODO(shubang): use LocationFragment + if (checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) + != PackageManager.PERMISSION_GRANTED) { + // No need to check the request result. + requestPermissions( + new String[] {android.Manifest.permission.ACCESS_COARSE_LOCATION}, + PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION); + } + } + + @Override + protected void executeGetTunerTypeAndCountAsyncTask() { + new AsyncTask<Void, Void, Integer>() { + @Override + protected Integer doInBackground(Void... arg0) { + return mTunerFactory.getTunerTypeAndCount(LiveTvTunerSetupActivity.this).first; + } + + @Override + protected void onPostExecute(Integer result) { + if (!LiveTvTunerSetupActivity.this.isDestroyed()) { + mTunerType = result; + if (result == null) { + finish(); + } else if (!mActivityStopped) { + showInitialFragment(); + } else { + mPendingShowInitialFragment = true; + } + } + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + FragmentManager manager = getFragmentManager(); + int count = manager.getBackStackEntryCount(); + if (count > 0) { + String lastTag = manager.getBackStackEntryAt(count - 1).getName(); + if (ScanResultFragment.class.getCanonicalName().equals(lastTag) && count >= 2) { + String secondLastTag = manager.getBackStackEntryAt(count - 2).getName(); + if (ScanFragment.class.getCanonicalName().equals(secondLastTag)) { + // Pops fragment including ScanFragment. + manager.popBackStack( + secondLastTag, FragmentManager.POP_BACK_STACK_INCLUSIVE); + return true; + } + } else if (ScanFragment.class.getCanonicalName().equals(lastTag)) { + mLastScanFragment.finishScan(true); + return true; + } + } + } + return super.onKeyUp(keyCode, event); + } + + @Override + public void onRequestPermissionsResult( + int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + try { + // Updating postal code takes time, therefore we should update postal code + // right after the permission is granted, so that the subsequent operations, + // especially EPG fetcher, could get the newly updated postal code. + PostalCodeUtils.updatePostalCode(this); + } catch (Exception e) { + // Do nothing + } + } + } + } + + /** + * Exports {@link LiveTvTunerSetupActivity} for Dagger codegen to create the appropriate + * injector. + */ + @dagger.Module + public abstract static class Module { + @ContributesAndroidInjector + abstract LiveTvTunerSetupActivity contributeLiveTvTunerSetupActivityInjector(); + } +} diff --git a/tuner/src/com/android/tv/tuner/setup/LocationFragment.java b/tuner/src/com/android/tv/tuner/setup/LocationFragment.java index f950405f..1234ae20 100644 --- a/tuner/src/com/android/tv/tuner/setup/LocationFragment.java +++ b/tuner/src/com/android/tv/tuner/setup/LocationFragment.java @@ -23,14 +23,16 @@ import android.location.Address; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; +import android.support.v17.leanback.widget.GuidanceStylist.Guidance; +import android.support.v17.leanback.widget.GuidedAction; import android.util.Log; -import androidx.leanback.widget.GuidanceStylist.Guidance; -import androidx.leanback.widget.GuidedAction; + import com.android.tv.common.ui.setup.SetupActionHelper; import com.android.tv.common.ui.setup.SetupGuidedStepFragment; import com.android.tv.common.ui.setup.SetupMultiPaneFragment; import com.android.tv.common.util.LocationUtils; import com.android.tv.tuner.R; + import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -38,7 +40,7 @@ import java.util.List; /** A fragment shows the rationale of location permission */ public class LocationFragment extends SetupMultiPaneFragment { private static final String TAG = "com.android.tv.tuner.setup.LocationFragment"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; public static final String ACTION_CATEGORY = "com.android.tv.tuner.setup.LocationFragment"; public static final String KEY_POSTAL_CODE = "key_postal_code"; @@ -77,7 +79,8 @@ public class LocationFragment extends SetupMultiPaneFragment { () -> { synchronized (mPostalCodeLock) { if (DEBUG) { - Log.d(TAG, "get location timeout. mPostalCode=" + mPostalCode); + Log.d(TAG, + "get location timeout. mPostalCode=" + mPostalCode); } if (mPostalCode == null) { // timeout. setup activity will get null postal code @@ -118,7 +121,8 @@ public class LocationFragment extends SetupMultiPaneFragment { .id(ACTION_GETTING_LOCATION) .title(getString(R.string.location_choices_getting_location)) .focusable(false) - .build()); + .build() + ); } @Override @@ -143,8 +147,8 @@ public class LocationFragment extends SetupMultiPaneFragment { } @Override - public void onRequestPermissionsResult( - int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { if (requestCode == PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { diff --git a/tuner/src/com/android/tv/tuner/setup/PostalCodeFragment.java b/tuner/src/com/android/tv/tuner/setup/PostalCodeFragment.java index f9ea1675..52247972 100644 --- a/tuner/src/com/android/tv/tuner/setup/PostalCodeFragment.java +++ b/tuner/src/com/android/tv/tuner/setup/PostalCodeFragment.java @@ -18,9 +18,9 @@ package com.android.tv.tuner.setup; import android.os.Bundle; import android.support.annotation.NonNull; -import androidx.leanback.widget.GuidanceStylist.Guidance; -import androidx.leanback.widget.GuidedAction; -import androidx.leanback.widget.GuidedActionsStylist; +import android.support.v17.leanback.widget.GuidanceStylist.Guidance; +import android.support.v17.leanback.widget.GuidedAction; +import android.support.v17.leanback.widget.GuidedActionsStylist; import android.text.InputFilter; import android.text.InputFilter.AllCaps; import android.view.View; diff --git a/tuner/src/com/android/tv/tuner/setup/ScanFragment.java b/tuner/src/com/android/tv/tuner/setup/ScanFragment.java index 87a79e35..7d59284c 100644 --- a/tuner/src/com/android/tv/tuner/setup/ScanFragment.java +++ b/tuner/src/com/android/tv/tuner/setup/ScanFragment.java @@ -40,9 +40,11 @@ import com.android.tv.common.ui.setup.SetupFragment; import com.android.tv.tuner.R; import com.android.tv.tuner.api.ScanChannel; import com.android.tv.tuner.api.Tuner; -import com.android.tv.tuner.data.Channel.TunerType; import com.android.tv.tuner.data.PsipData; import com.android.tv.tuner.data.TunerChannel; +import com.android.tv.tuner.data.nano.Channel; + + import com.android.tv.tuner.prefs.TunerPreferences; import com.android.tv.tuner.source.FileTsStreamer; import com.android.tv.tuner.source.TsDataSource; @@ -72,13 +74,7 @@ public class ScanFragment extends SetupFragment { public static final int ACTION_FINISH = 2; public static final String EXTRA_FOR_CHANNEL_SCAN_FILE = "scan_file_choice"; - public static final String EXTRA_FOR_INPUT_ID = "input_id"; public static final String KEY_CHANNEL_NUMBERS = "channel_numbers"; - - // Allows adding audio-only channels (CJ music channel) for which VCT is not present. - private static final boolean ADD_CJ_MUSIC_CHANNELS = false; - private static final int CJ_MUSIC_CHANNEL_FREQUENCY = 585000000; - private static final long CHANNEL_SCAN_SHOW_DELAY_MS = 10000; private static final long CHANNEL_SCAN_PERIOD_MS = 4000; private static final long SHOW_PROGRESS_DIALOG_DELAY_MS = 300; @@ -103,6 +99,8 @@ public class ScanFragment extends SetupFragment { if (DEBUG) Log.d(TAG, "onCreateView"); View view = super.onCreateView(inflater, container, savedInstanceState); mChannelNumbers = new ArrayList<>(); + mChannelDataManager = new ChannelDataManager(getActivity().getApplicationContext()); + mChannelDataManager.checkDataVersion(getActivity()); mAdapter = new ChannelAdapter(); mProgressBar = (ProgressBar) view.findViewById(R.id.tune_progress); mScanningMessage = (TextView) view.findViewById(R.id.tune_description); @@ -124,6 +122,8 @@ public class ScanFragment extends SetupFragment { }); Bundle args = getArguments(); int tunerType = (args == null ? 0 : args.getInt(BaseTunerSetupActivity.KEY_TUNER_TYPE, 0)); + // TODO: Handle the case when the fragment is restored. + startScan(args == null ? 0 : args.getInt(EXTRA_FOR_CHANNEL_SCAN_FILE, 0)); TextView scanTitleView = (TextView) view.findViewById(R.id.tune_title); switch (tunerType) { case Tuner.TUNER_TYPE_USB: @@ -139,28 +139,6 @@ public class ScanFragment extends SetupFragment { } @Override - public void onStart() { - super.onStart(); - Bundle args = getArguments(); - String inputId = args == null ? null : args.getString(ScanFragment.EXTRA_FOR_INPUT_ID); - if (inputId == null) { - Log.w(TAG, "No input ID, stopping setup activity."); - getActivity().finish(); - } - - mChannelDataManager = new ChannelDataManager(getContext().getApplicationContext(), inputId); - mChannelDataManager.checkDataVersion(getActivity()); - } - - @Override - public void onStop() { - if (mChannelDataManager != null) { - mChannelDataManager.release(); - } - super.onStop(); - } - - @Override protected int getLayoutResourceId() { return R.layout.ut_channel_scan; } @@ -176,13 +154,6 @@ public class ScanFragment extends SetupFragment { } @Override - public void onResume() { - Bundle args = getArguments(); - startScan(args == null ? 0 : args.getInt(EXTRA_FOR_CHANNEL_SCAN_FILE, 0)); - super.onResume(); - } - - @Override public void onPause() { Log.d(TAG, "onPause"); if (mChannelScanTask != null) { @@ -279,7 +250,6 @@ public class ScanFragment extends SetupFragment { private final Activity mActivity; private final int mChannelMapId; -// AOSP_Comment_Out private final com.android.tv.tuner.hdhomerun.HdHomeRunTunerHal mNetworkTuner; private final TsStreamer mScanTsStreamer; private final TsStreamer mFileTsStreamer; private final ConditionVariable mConditionStopped; @@ -300,13 +270,6 @@ public class ScanFragment extends SetupFragment { if (hal == null) { throw new RuntimeException("Failed to open a DVB device"); } - /* Begin_AOSP_Comment_Out - if (hal instanceof com.android.tv.tuner.hdhomerun.HdHomeRunTunerHal) { - mNetworkTuner = (com.android.tv.tuner.hdhomerun.HdHomeRunTunerHal) hal; - } else { - mNetworkTuner = null; - } - End_AOSP_Comment_Out */ mScanTsStreamer = new TunerTsStreamer(hal, this); } mFileTsStreamer = SCAN_LOCAL_STREAMS ? new FileTsStreamer(this, mActivity) : null; @@ -351,18 +314,6 @@ public class ScanFragment extends SetupFragment { @Override protected Void doInBackground(Void... params) { - /* Begin_AOSP_Comment_Out - if (mNetworkTuner != null) { - mChannelDataManager.notifyScanStarted(); - com.android.tv.tuner.hdhomerun.HdHomeRunChannelScan hdHomeRunChannelScan = - new com.android.tv.tuner.hdhomerun.HdHomeRunChannelScan( - mActivity.getApplicationContext(), this, mNetworkTuner); - hdHomeRunChannelScan.scan(mConditionStopped); - mChannelDataManager.notifyScanCompleted(); - publishProgress(MAX_PROGRESS); - return null; - } - End_AOSP_Comment_Out */ mScanChannelList.clear(); if (SCAN_LOCAL_STREAMS) { FileTsStreamer.addLocalStreamFiles(mScanChannelList); @@ -425,10 +376,6 @@ public class ScanFragment extends SetupFragment { e); } streamer.stopStream(); - - if (ADD_CJ_MUSIC_CHANNELS) { - addCjMusicChannel(frequency, modulation); - } addChannelsWithoutVct(scanChannel); if (System.currentTimeMillis() > startMs + CHANNEL_SCAN_SHOW_DELAY_MS && !mChannelListVisible) { @@ -447,24 +394,6 @@ public class ScanFragment extends SetupFragment { if (DEBUG) Log.i(TAG, "Channel scan ended"); } - private void addCjMusicChannel(int frequency, String modulation) { - if (frequency == CJ_MUSIC_CHANNEL_FREQUENCY - && mChannelMapId == R.raw.ut_kr_dev_cj_cable_center_frequencies_qam256) { - List<TunerChannel> incompleteChannels = - mScanTsStreamer instanceof TunerTsStreamer - ? ((TunerTsStreamer) mScanTsStreamer).getMalFormedChannels() - : new ArrayList<>(); - for (TunerChannel tunerChannel : incompleteChannels) { - if ((tunerChannel.getVideoPid() == TunerChannel.INVALID_PID) - && (tunerChannel.getAudioPid() != TunerChannel.INVALID_PID)) { - tunerChannel.setFrequency(frequency); - tunerChannel.setModulation(modulation); - onChannelDetected(tunerChannel, true); - } - } - } - } - private void addChannelsWithoutVct(ScanChannel scanChannel) { if (scanChannel.radioFrequencyNumber == null || !(mScanTsStreamer instanceof TunerTsStreamer)) { @@ -474,7 +403,6 @@ public class ScanFragment extends SetupFragment { ((TunerTsStreamer) mScanTsStreamer).getMalFormedChannels()) { if ((tunerChannel.getVideoPid() != TunerChannel.INVALID_PID) && (tunerChannel.getAudioPid() != TunerChannel.INVALID_PID)) { - tunerChannel.setDeliverySystemType(scanChannel.deliverySystemType); tunerChannel.setFrequency(scanChannel.frequency); tunerChannel.setModulation(scanChannel.modulation); tunerChannel.setShortName( @@ -492,9 +420,9 @@ public class ScanFragment extends SetupFragment { private TsStreamer getStreamer(int type) { switch (type) { - case TunerType.TYPE_TUNER_VALUE: + case Channel.TunerType.TYPE_TUNER: return mScanTsStreamer; - case TunerType.TYPE_FILE_VALUE: + case Channel.TunerType.TYPE_FILE: return mFileTsStreamer; default: return null; diff --git a/tuner/src/com/android/tv/tuner/setup/ScanResultFragment.java b/tuner/src/com/android/tv/tuner/setup/ScanResultFragment.java index 01bcc9f2..bd3f9ad9 100644 --- a/tuner/src/com/android/tv/tuner/setup/ScanResultFragment.java +++ b/tuner/src/com/android/tv/tuner/setup/ScanResultFragment.java @@ -20,8 +20,8 @@ import android.content.Context; import android.content.res.Resources; import android.os.Bundle; import android.support.annotation.NonNull; -import androidx.leanback.widget.GuidanceStylist.Guidance; -import androidx.leanback.widget.GuidedAction; +import android.support.v17.leanback.widget.GuidanceStylist.Guidance; +import android.support.v17.leanback.widget.GuidedAction; import com.android.tv.common.ui.setup.SetupGuidedStepFragment; import com.android.tv.common.ui.setup.SetupMultiPaneFragment; import com.android.tv.tuner.R; diff --git a/tuner/src/com/android/tv/tuner/setup/WelcomeFragment.java b/tuner/src/com/android/tv/tuner/setup/WelcomeFragment.java index dfa994b6..2a414df7 100644 --- a/tuner/src/com/android/tv/tuner/setup/WelcomeFragment.java +++ b/tuner/src/com/android/tv/tuner/setup/WelcomeFragment.java @@ -19,8 +19,8 @@ package com.android.tv.tuner.setup; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import androidx.leanback.widget.GuidanceStylist.Guidance; -import androidx.leanback.widget.GuidedAction; +import android.support.v17.leanback.widget.GuidanceStylist.Guidance; +import android.support.v17.leanback.widget.GuidedAction; import com.android.tv.common.ui.setup.SetupGuidedStepFragment; import com.android.tv.common.ui.setup.SetupMultiPaneFragment; import com.android.tv.tuner.R; diff --git a/tuner/src/com/android/tv/tuner/singletons/TunerSingletons.java b/tuner/src/com/android/tv/tuner/singletons/TunerSingletons.java index dfe9005b..48b17dcb 100644 --- a/tuner/src/com/android/tv/tuner/singletons/TunerSingletons.java +++ b/tuner/src/com/android/tv/tuner/singletons/TunerSingletons.java @@ -18,17 +18,4 @@ package com.android.tv.tuner.singletons; import com.android.tv.common.singletons.HasTvInputId; /** Singletons used in tuner applications */ -public interface TunerSingletons extends HasTvInputId { - - /* - * Do not add any new methods here. - * - * To move a getter to Injection. - * 1. Make a type injectable @Singleton. - * 2. Mark the getter here as deprecated. - * 3. Lazily inject the object in TvApplication. - * 4. Move easy usages of getters to injection instead. - * 5. Delete the method when all usages are migrated. - */ - -} +public interface TunerSingletons extends HasTvInputId {} diff --git a/tuner/src/com/android/tv/tuner/source/FileSourceEventDetector.java b/tuner/src/com/android/tv/tuner/source/FileSourceEventDetector.java index 5ee897bb..85932c8c 100644 --- a/tuner/src/com/android/tv/tuner/source/FileSourceEventDetector.java +++ b/tuner/src/com/android/tv/tuner/source/FileSourceEventDetector.java @@ -24,9 +24,9 @@ import com.android.tv.tuner.data.PsiData.PmtItem; import com.android.tv.tuner.data.PsipData.EitItem; import com.android.tv.tuner.data.PsipData.SdtItem; import com.android.tv.tuner.data.PsipData.VctItem; -import com.android.tv.tuner.data.Track.AtscAudioTrack; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; import com.android.tv.tuner.data.TunerChannel; +import com.android.tv.tuner.data.nano.Track.AtscAudioTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import com.android.tv.tuner.ts.EventDetector.EventListener; import com.android.tv.tuner.ts.TsParser; import java.util.ArrayList; diff --git a/tuner/src/com/android/tv/tuner/source/FileTsStreamer.java b/tuner/src/com/android/tv/tuner/source/FileTsStreamer.java index 15f3458a..99d37e39 100644 --- a/tuner/src/com/android/tv/tuner/source/FileTsStreamer.java +++ b/tuner/src/com/android/tv/tuner/source/FileTsStreamer.java @@ -17,9 +17,7 @@ package com.android.tv.tuner.source; import android.content.Context; -import android.net.Uri; import android.os.Environment; -import android.support.annotation.Nullable; import android.util.Log; import android.util.SparseBooleanArray; import com.android.tv.common.SoftPreconditions; @@ -28,8 +26,8 @@ import com.android.tv.tuner.data.TunerChannel; import com.android.tv.tuner.features.TunerFeatures; import com.android.tv.tuner.ts.EventDetector.EventListener; import com.android.tv.tuner.ts.TsParser; -import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.upstream.TransferListener; +import com.google.android.exoplayer.C; +import com.google.android.exoplayer.upstream.DataSpec; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -73,7 +71,6 @@ public class FileTsStreamer implements TsStreamer { public static class FileDataSource extends TsDataSource { private final FileTsStreamer mTsStreamer; private final AtomicLong mLastReadPosition = new AtomicLong(0); - private Uri mUri; private long mStartBufferedPosition; private FileDataSource(FileTsStreamer tsStreamer) { @@ -99,10 +96,9 @@ public class FileTsStreamer implements TsStreamer { } @Override - public long open(DataSpec dataSpec) { - mUri = dataSpec.uri; + public long open(DataSpec dataSpec) throws IOException { mLastReadPosition.set(0); - return com.google.android.exoplayer2.C.LENGTH_UNSET; + return C.LENGTH_UNBOUNDED; } @Override @@ -121,19 +117,6 @@ public class FileTsStreamer implements TsStreamer { } return ret; } - - // ExoPlayer V2 DataSource implementation. - - @Override - public void addTransferListener(TransferListener transferListener) { - // TODO: Implement to support metrics collection. - } - - @Nullable - @Override - public Uri getUri() { - return mUri; - } } /** diff --git a/tuner/src/com/android/tv/tuner/source/TsDataSource.java b/tuner/src/com/android/tv/tuner/source/TsDataSource.java index 18f4458b..cf3c25d9 100644 --- a/tuner/src/com/android/tv/tuner/source/TsDataSource.java +++ b/tuner/src/com/android/tv/tuner/source/TsDataSource.java @@ -17,7 +17,7 @@ package com.android.tv.tuner.source; import com.android.tv.common.compat.TvInputConstantCompat; -import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer.upstream.DataSource; /** {@link DataSource} for MPEG-TS stream, which will be used by {@link TsExtractor}. */ public abstract class TsDataSource implements DataSource { diff --git a/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java b/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java index 3c00b5cc..28756a93 100644 --- a/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java +++ b/tuner/src/com/android/tv/tuner/source/TsDataSourceManager.java @@ -19,8 +19,8 @@ package com.android.tv.tuner.source; import android.content.Context; import android.support.annotation.VisibleForTesting; import com.android.tv.tuner.api.Tuner; -import com.android.tv.tuner.data.Channel; import com.android.tv.tuner.data.TunerChannel; +import com.android.tv.tuner.data.nano.Channel; import com.android.tv.tuner.ts.EventDetector.EventListener; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; diff --git a/tuner/src/com/android/tv/tuner/source/TunerTsStreamer.java b/tuner/src/com/android/tv/tuner/source/TunerTsStreamer.java index 19058c8a..9e68c910 100644 --- a/tuner/src/com/android/tv/tuner/source/TunerTsStreamer.java +++ b/tuner/src/com/android/tv/tuner/source/TunerTsStreamer.java @@ -17,11 +17,8 @@ package com.android.tv.tuner.source; import android.content.Context; -import android.net.Uri; -import android.support.annotation.Nullable; import android.util.Log; import android.util.Pair; - import com.android.tv.common.SoftPreconditions; import com.android.tv.tuner.api.ScanChannel; import com.android.tv.tuner.api.Tuner; @@ -29,10 +26,8 @@ import com.android.tv.tuner.data.TunerChannel; import com.android.tv.tuner.prefs.TunerPreferences; import com.android.tv.tuner.ts.EventDetector; import com.android.tv.tuner.ts.EventDetector.EventListener; - -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.upstream.TransferListener; +import com.google.android.exoplayer.C; +import com.google.android.exoplayer.upstream.DataSpec; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -71,7 +66,6 @@ public class TunerTsStreamer implements TsStreamer { private final TunerTsStreamer mTsStreamer; private final AtomicLong mLastReadPosition = new AtomicLong(0); private long mStartBufferedPosition; - private Uri mUri; private TunerDataSource(TunerTsStreamer tsStreamer) { mTsStreamer = tsStreamer; @@ -96,16 +90,13 @@ public class TunerTsStreamer implements TsStreamer { } @Override - public long open(DataSpec dataSpec) { - mUri = dataSpec.uri; + public long open(DataSpec dataSpec) throws IOException { mLastReadPosition.set(0); - return C.LENGTH_UNSET; + return C.LENGTH_UNBOUNDED; } @Override - public void close() { - mUri = null; - } + public void close() {} @Override public int read(byte[] buffer, int offset, int readLength) throws IOException { @@ -135,18 +126,6 @@ public class TunerTsStreamer implements TsStreamer { public int getSignalStrength() { return mTsStreamer.getSignalStrength(); } - - @Override - public void addTransferListener(TransferListener transferListener) { - // TODO: Implement to support metrics collection. - } - - @Nullable - @Override - public Uri getUri() { - return mUri; - } - } /** * Creates {@link TsStreamer} for playing or recording the specified channel. @@ -173,8 +152,7 @@ public class TunerTsStreamer implements TsStreamer { @Override public boolean startStream(TunerChannel channel) { if (mTunerHal.tune( - channel.getDeliverySystemType().getNumber(), channel.getFrequency(), - channel.getModulation(), channel.getDisplayNumber(false))) { + channel.getFrequency(), channel.getModulation(), channel.getDisplayNumber(false))) { if (channel.hasVideo()) { mTunerHal.addPidFilter(channel.getVideoPid(), Tuner.FILTER_TYPE_VIDEO); } @@ -192,7 +170,6 @@ public class TunerTsStreamer implements TsStreamer { mTunerHal.addPidFilter(channel.getPcrPid(), Tuner.FILTER_TYPE_PCR); if (mEventDetector != null) { mEventDetector.startDetecting( - channel.getDeliverySystemType(), channel.getFrequency(), channel.getModulation(), channel.getProgramNumber()); @@ -222,11 +199,9 @@ public class TunerTsStreamer implements TsStreamer { @Override public boolean startStream(ScanChannel channel) { - if (mTunerHal.tune(channel.deliverySystemType.getNumber(), channel.frequency, - channel.modulation, null)) { + if (mTunerHal.tune(channel.frequency, channel.modulation, null)) { mEventDetector.startDetecting( - channel.deliverySystemType, channel.frequency, channel.modulation, - EventDetector.ALL_PROGRAM_NUMBERS); + channel.frequency, channel.modulation, EventDetector.ALL_PROGRAM_NUMBERS); synchronized (mCircularBufferMonitor) { if (mStreaming) { Log.w(TAG, "Streaming should be stopped before start streaming"); @@ -320,7 +295,7 @@ public class TunerTsStreamer implements TsStreamer { public void registerListener(EventListener listener) { if (mEventDetector != null && listener != null) { synchronized (mEventListenerActions) { - mEventListenerActions.add(Pair.create(listener, true)); + mEventListenerActions.add(new Pair<>(listener, true)); } } } diff --git a/tuner/src/com/android/tv/tuner/ts/EventDetector.java b/tuner/src/com/android/tv/tuner/ts/EventDetector.java index 3a2d835e..6d1fc277 100644 --- a/tuner/src/com/android/tv/tuner/ts/EventDetector.java +++ b/tuner/src/com/android/tv/tuner/ts/EventDetector.java @@ -20,13 +20,12 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.tv.tuner.api.Tuner; -import com.android.tv.tuner.data.Channel.DeliverySystemType; import com.android.tv.tuner.data.PsiData; import com.android.tv.tuner.data.PsipData; import com.android.tv.tuner.data.PsipData.EitItem; -import com.android.tv.tuner.data.Track.AtscAudioTrack; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; import com.android.tv.tuner.data.TunerChannel; +import com.android.tv.tuner.data.nano.Track.AtscAudioTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -52,7 +51,6 @@ public class EventDetector { private final SparseBooleanArray mVctCaptionTracksFound = new SparseBooleanArray(); private final SparseBooleanArray mEitCaptionTracksFound = new SparseBooleanArray(); private final List<EventListener> mEventListeners = new ArrayList<>(); - private DeliverySystemType mDeliverySystemType; private int mFrequency; private String mModulation; private int mProgramNumber = ALL_PROGRAM_NUMBERS; @@ -172,7 +170,6 @@ public class EventDetector { } tunerChannel.setAudioTracks(audioTracks); tunerChannel.setCaptionTracks(captionTracks); - tunerChannel.setDeliverySystemType(mDeliverySystemType); tunerChannel.setFrequency(mFrequency); tunerChannel.setModulation(mModulation); mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel); @@ -212,7 +209,6 @@ public class EventDetector { int channelProgramNumber = channel.getServiceId(); tunerChannel.setAudioTracks(audioTracks); tunerChannel.setCaptionTracks(captionTracks); - tunerChannel.setDeliverySystemType(mDeliverySystemType); tunerChannel.setFrequency(mFrequency); tunerChannel.setModulation(mModulation); mChannelMap.put(tunerChannel.getProgramNumber(), tunerChannel); @@ -256,18 +252,10 @@ public class EventDetector { private void reset() { // TODO: Use TsParser.reset() - int[] deliverySystemTypes = mTunerHal.getDeliverySystemTypes(); - boolean isDvbSignal = false; - for (int i = 0; i < deliverySystemTypes.length; i++) { - if (Tuner.isDvbDeliverySystem(deliverySystemTypes[i])) { - isDvbSignal = true; - break; - } - } mTsParser = new TsParser( mTsOutputListener, - isDvbSignal); + Tuner.isDvbDeliverySystem(mTunerHal.getDeliverySystemType())); mPidSet.clear(); mVctProgramNumberSet.clear(); mSdtProgramNumberSet.clear(); @@ -284,10 +272,8 @@ public class EventDetector { * @param programNumber The program number if this is for handling tune request. For scanning * purpose, supply {@link #ALL_PROGRAM_NUMBERS}. */ - public void startDetecting(DeliverySystemType deliverySystemType, int frequency, - String modulation, int programNumber) { + public void startDetecting(int frequency, String modulation, int programNumber) { reset(); - mDeliverySystemType = deliverySystemType; mFrequency = frequency; mModulation = modulation; mProgramNumber = programNumber; diff --git a/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java b/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java index e47162ad..d22b6399 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java +++ b/tuner/src/com/android/tv/tuner/tvinput/BaseTunerTvInputService.java @@ -21,26 +21,17 @@ import android.app.job.JobScheduler; import android.content.ComponentName; import android.content.Context; import android.media.tv.TvInputService; -import android.net.Uri; import android.util.Log; - import com.android.tv.common.feature.CommonFeatures; +import com.android.tv.tuner.source.TsDataSourceManager; import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; -import com.android.tv.tuner.tvinput.factory.TunerRecordingSessionFactory; import com.android.tv.tuner.tvinput.factory.TunerSessionFactory; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.cache.RemovalListener; - import dagger.android.AndroidInjection; - +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; import java.util.Collections; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; - import javax.inject.Inject; /** {@link BaseTunerTvInputService} serves TV channels coming from a tuner device. */ @@ -51,19 +42,10 @@ public class BaseTunerTvInputService extends TvInputService { private static final int DVR_STORAGE_CLEANUP_JOB_ID = 100; private final Set<Session> mTunerSessions = Collections.newSetFromMap(new WeakHashMap<>()); - private final Set<RecordingSession> mTunerRecordingSession = - Collections.newSetFromMap(new WeakHashMap<>()); + private ChannelDataManager mChannelDataManager; + @Inject ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; + @Inject TsDataSourceManager.Factory mTsDataSourceManagerFactory; @Inject TunerSessionFactory mTunerSessionFactory; - @Inject TunerRecordingSessionFactory mTunerRecordingSessionFactory; - - LoadingCache<String, ChannelDataManager> mChannelDataManagers; - RemovalListener<String, ChannelDataManager> mChannelDataManagerRemovalListener = - notification -> { - ChannelDataManager cdm = notification.getValue(); - if (cdm != null) { - cdm.release(); - } - }; @Override public void onCreate() { @@ -75,17 +57,7 @@ public class BaseTunerTvInputService extends TvInputService { AndroidInjection.inject(this); super.onCreate(); if (DEBUG) Log.d(TAG, "onCreate"); - mChannelDataManagers = - CacheBuilder.newBuilder() - .weakValues() - .removalListener(mChannelDataManagerRemovalListener) - .build( - new CacheLoader<String, ChannelDataManager>() { - @Override - public ChannelDataManager load(String inputId) { - return createChannelDataManager(inputId); - } - }); + mChannelDataManager = new ChannelDataManager(getApplicationContext()); if (CommonFeatures.DVR.isEnabled(this)) { JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); @@ -105,24 +77,21 @@ public class BaseTunerTvInputService extends TvInputService { } } - private ChannelDataManager createChannelDataManager(String inputId) { - return new ChannelDataManager(getApplicationContext(), inputId); - } - @Override public void onDestroy() { if (DEBUG) Log.d(TAG, "onDestroy"); super.onDestroy(); - mChannelDataManagers.invalidateAll(); + mChannelDataManager.release(); } @Override public RecordingSession onCreateRecordingSession(String inputId) { - RecordingSession session = - mTunerRecordingSessionFactory.create( - inputId, this::onReleased, mChannelDataManagers.getUnchecked(inputId)); - mTunerRecordingSession.add(session); - return session; + return new TunerRecordingSession( + this, + inputId, + mChannelDataManager, + mConcurrentDvrPlaybackFlags, + mTsDataSourceManagerFactory); } @Override @@ -134,12 +103,8 @@ public class BaseTunerTvInputService extends TvInputService { Log.d(TAG, "abort creating an session"); return null; } - final Session session = - mTunerSessionFactory.create( - mChannelDataManagers.getUnchecked(inputId), - this::onReleased, - this::getRecordingUri); + mTunerSessionFactory.create(this, mChannelDataManager, this::onReleased); mTunerSessions.add(session); session.setOverlayViewEnabled(true); return session; @@ -150,22 +115,7 @@ public class BaseTunerTvInputService extends TvInputService { } } - private Uri getRecordingUri(Uri channelUri) { - for (RecordingSession session : mTunerRecordingSession) { - TunerRecordingSession tunerSession = (TunerRecordingSession) session; - if (tunerSession.getChannelUri().equals(channelUri)) { - return tunerSession.getRecordingUri(); - } - } - return null; - } - private void onReleased(Session session) { mTunerSessions.remove(session); - mChannelDataManagers.cleanUp(); - } - - private void onReleased(RecordingSession session) { - mTunerRecordingSession.remove(session); } } diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java index ed61f71b..55616931 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java +++ b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSession.java @@ -22,40 +22,33 @@ import android.support.annotation.MainThread; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; import android.util.Log; - import com.android.tv.common.compat.RecordingSessionCompat; -import com.android.tv.common.dagger.annotations.ApplicationContext; +import com.android.tv.tuner.source.TsDataSourceManager; import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; -import com.android.tv.tuner.tvinput.factory.TunerRecordingSessionFactory; -import com.android.tv.tuner.tvinput.factory.TunerRecordingSessionFactory.RecordingSessionReleasedCallback; - -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; /** Processes DVR recordings, and deletes the previously recorded contents. */ -@AutoFactory( - className = "TunerRecordingSessionFactoryImpl", - implementing = TunerRecordingSessionFactory.class) public class TunerRecordingSession extends RecordingSessionCompat { private static final String TAG = "TunerRecordingSession"; private static final boolean DEBUG = false; private final TunerRecordingSessionWorker mSessionWorker; - private final RecordingSessionReleasedCallback mReleasedCallback; - private Uri mChannelUri; - private Uri mRecordingUri; public TunerRecordingSession( - @Provided @ApplicationContext Context context, + Context context, String inputId, - RecordingSessionReleasedCallback releasedCallback, ChannelDataManager channelDataManager, - @Provided TunerRecordingSessionWorker.Factory tunerRecordingSessionWorkerFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, + TsDataSourceManager.Factory tsDataSourceManagerFactory) { super(context); - mReleasedCallback = releasedCallback; mSessionWorker = - tunerRecordingSessionWorkerFactory.create( - context, inputId, channelDataManager, this); + new TunerRecordingSessionWorker( + context, + inputId, + channelDataManager, + this, + concurrentDvrPlaybackFlags, + tsDataSourceManagerFactory); } // RecordingSession @@ -76,7 +69,6 @@ public class TunerRecordingSession extends RecordingSessionCompat { Log.d(TAG, "Requesting recording session release."); } mSessionWorker.release(); - mReleasedCallback.onReleased(this); } @MainThread @@ -103,7 +95,6 @@ public class TunerRecordingSession extends RecordingSessionCompat { if (DEBUG) { Log.d(TAG, "Notifying recording session tuned."); } - mChannelUri = channelUri; notifyTuned(channelUri); } @@ -121,7 +112,6 @@ public class TunerRecordingSession extends RecordingSessionCompat { if (DEBUG) { Log.d(TAG, "Notifying record successfully finished."); } - mRecordingUri = null; notifyRecordingStopped(recordedProgramUri); } @@ -130,19 +120,4 @@ public class TunerRecordingSession extends RecordingSessionCompat { Log.w(TAG, "Notifying recording error: " + reason); notifyError(reason); } - - public void onRecordingStatePartial(Uri recUri) { - if (DEBUG) { - Log.d(TAG, "Updating recording session state to Partial"); - } - mRecordingUri = recUri; - } - - public Uri getChannelUri() { - return mChannelUri; - } - - public Uri getRecordingUri() { - return mRecordingUri; - } } diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java index 97cd0572..2c0c09a6 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java +++ b/tuner/src/com/android/tv/tuner/tvinput/TunerRecordingSessionWorker.java @@ -37,19 +37,17 @@ import android.support.annotation.MainThread; import android.support.annotation.Nullable; import android.util.Log; import android.util.Pair; - import androidx.tvprovider.media.tv.Program; - import com.android.tv.common.BaseApplication; import com.android.tv.common.data.RecordedProgramState; import com.android.tv.common.recording.RecordingCapability; import com.android.tv.common.recording.RecordingStorageStatusManager; import com.android.tv.common.util.CommonUtils; +import com.android.tv.tuner.DvbDeviceAccessor; import com.android.tv.tuner.data.PsipData; import com.android.tv.tuner.data.PsipData.EitItem; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; import com.android.tv.tuner.data.TunerChannel; -import com.android.tv.tuner.dvb.DvbDeviceAccessor; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import com.android.tv.tuner.exoplayer.ExoPlayerSampleExtractor; import com.android.tv.tuner.exoplayer.SampleExtractor; import com.android.tv.tuner.exoplayer.buffer.BufferManager; @@ -59,11 +57,8 @@ import com.android.tv.tuner.source.TsDataSource; import com.android.tv.tuner.source.TsDataSourceManager; import com.android.tv.tuner.ts.EventDetector.EventListener; import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; - import com.google.android.exoplayer.C; -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; - +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; import java.io.File; import java.io.IOException; import java.lang.annotation.Retention; @@ -147,6 +142,7 @@ public class TunerRecordingSessionWorker private static final long CHANNEL_ID_NONE = -1; private static final int MAX_TUNING_RETRY = 6; + private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; private final Context mContext; private final ChannelDataManager mChannelDataManager; @@ -172,31 +168,15 @@ public class TunerRecordingSessionWorker private PsipData.EitItem mCurrenProgram; private List<AtscCaptionTrack> mCaptionTracks; private DvrStorageManager mDvrStorageManager; - private final ExoPlayerSampleExtractor.Factory mExoPlayerSampleExtractorFactory; - /** - * Factory for {@link TunerRecordingSessionWorker}}. - * - * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory} - * generated class. - */ - public interface Factory { - TunerRecordingSessionWorker create( - Context context, - String inputId, - ChannelDataManager dataManager, - TunerRecordingSession session); - } - - @AutoFactory(implementing = Factory.class) public TunerRecordingSessionWorker( Context context, String inputId, ChannelDataManager dataManager, TunerRecordingSession session, - @Provided ExoPlayerSampleExtractor.Factory exoPlayerSampleExtractorFactory, - @Provided TsDataSourceManager.Factory tsDataSourceManagerFactory) { - mExoPlayerSampleExtractorFactory = exoPlayerSampleExtractorFactory; + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, + TsDataSourceManager.Factory tsDataSourceManagerFactory) { + mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags; mRandom.setSeed(System.nanoTime()); mContext = context; HandlerThread handlerThread = new HandlerThread(TAG); @@ -237,7 +217,7 @@ public class TunerRecordingSessionWorker if (mChannel == null || mChannel.compareTo(channel) != 0) { return; } - mHandler.obtainMessage(MSG_UPDATE_CC_INFO, Pair.create(channel, items)).sendToTarget(); + mHandler.obtainMessage(MSG_UPDATE_CC_INFO, new Pair<>(channel, items)).sendToTarget(); mChannelDataManager.notifyEventDetected(channel, items); } @@ -382,7 +362,7 @@ public class TunerRecordingSessionWorker } case MSG_UPDATE_PARTIAL_STATE: { - updateRecordedProgramStatePartial(); + updateRecordedProgram(RecordedProgramState.PARTIAL, -1, -1); return true; } } @@ -476,30 +456,36 @@ public class TunerRecordingSessionWorker mRecordStartTime = System.currentTimeMillis(); mDvrStorageManager = new DvrStorageManager(mStorageDir, true); mRecorder = - mExoPlayerSampleExtractorFactory.create( - Uri.EMPTY, mTunerSource, new BufferManager(mDvrStorageManager), this, true); + new ExoPlayerSampleExtractor( + Uri.EMPTY, + mTunerSource, + new BufferManager(mDvrStorageManager), + this, + true, + mConcurrentDvrPlaybackFlags); mRecorder.setOnCompletionListener(this, mHandler); mProgramUri = programUri; mSessionState = STATE_RECORDING; mRecorderRunning = true; - mRecordedProgramUri = - insertRecordedProgram( - getRecordedProgram(), - mChannel.getChannelId(), - Uri.fromFile(mStorageDir).toString(), - calculateRecordingSizeInBytes(), - mRecordStartTime, - mRecordStartTime); - if (mRecordedProgramUri == null) { - new DeleteRecordingTask().execute(mStorageDir); - mSession.onError(TvInputManager.RECORDING_ERROR_UNKNOWN); - Log.e(TAG, "Inserting a recording to DB failed"); - return false; + if (mConcurrentDvrPlaybackFlags.enabled()) { + mRecordedProgramUri = + insertRecordedProgram( + getRecordedProgram(), + mChannel.getChannelId(), + Uri.fromFile(mStorageDir).toString(), + calculateRecordingSizeInBytes(), + mRecordStartTime, + mRecordStartTime); + if (mRecordedProgramUri == null) { + new DeleteRecordingTask().execute(mStorageDir); + mSession.onError(TvInputManager.RECORDING_ERROR_UNKNOWN); + Log.e(TAG, "Inserting a recording to DB failed"); + return false; + } + mSession.onRecordingUri(mRecordedProgramUri.toString()); + mHandler.sendEmptyMessageDelayed( + MSG_UPDATE_PARTIAL_STATE, MIN_PARTIAL_RECORDING_DURATION_MS); } - mSession.onRecordingUri(mRecordedProgramUri.toString()); - mHandler.sendEmptyMessageDelayed( - MSG_UPDATE_PARTIAL_STATE, MIN_PARTIAL_RECORDING_DURATION_MS); - mHandler.sendEmptyMessage(MSG_PREPARE_RECODER); mHandler.removeMessages(MSG_MONITOR_STORAGE_STATUS); mHandler.sendEmptyMessageDelayed(MSG_MONITOR_STORAGE_STATUS, STORAGE_MONITOR_INTERVAL_MS); @@ -606,7 +592,7 @@ public class TunerRecordingSessionWorker if (checkRecordedProgramTable(COLUMN_SERIES_ID)) { values.put(COLUMN_SERIES_ID, mSeriesId); } - if (checkRecordedProgramTable(COLUMN_STATE)) { + if (mConcurrentDvrPlaybackFlags.enabled() && checkRecordedProgramTable(COLUMN_STATE)) { values.put(COLUMN_STATE, RecordedProgramState.STARTED.name()); } if (program != null) { @@ -616,24 +602,18 @@ public class TunerRecordingSessionWorker .insert(TvContract.RecordedPrograms.CONTENT_URI, values); } - private void updateRecordedProgramStateFinished(long endTime, long totalBytes) { + private void updateRecordedProgram(RecordedProgramState state, long endTime, long totalBytes) { ContentValues values = new ContentValues(); - values.put(RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, totalBytes); - values.put(RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, endTime - mRecordStartTime); - values.put(RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); if (checkRecordedProgramTable(COLUMN_STATE)) { - values.put(COLUMN_STATE, RecordedProgramState.FINISHED.name()); + values.put(COLUMN_STATE, state.name()); } - mContext.getContentResolver().update(mRecordedProgramUri, values, null, null); - } - - private void updateRecordedProgramStatePartial() { - mSession.onRecordingStatePartial(mRecordedProgramUri); - if (checkRecordedProgramTable(COLUMN_STATE)) { - ContentValues values = new ContentValues(); - values.put(COLUMN_STATE, RecordedProgramState.PARTIAL.name()); - mContext.getContentResolver().update(mRecordedProgramUri, values, null, null); + if (state.equals(RecordedProgramState.FINISHED)) { + values.put(RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, totalBytes); + values.put( + RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, endTime - mRecordStartTime); + values.put(RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, endTime); } + mContext.getContentResolver().update(mRecordedProgramUri, values, null, null); } private void onRecordingResult(boolean success, long lastExtractedPositionUs) { @@ -660,7 +640,25 @@ public class TunerRecordingSessionWorker (lastExtractedPositionUs == C.UNKNOWN_TIME_US) ? System.currentTimeMillis() : mRecordStartTime + lastExtractedPositionUs / 1000; - updateRecordedProgramStateFinished(recordEndTime, calculateRecordingSizeInBytes()); + if (!mConcurrentDvrPlaybackFlags.enabled()) { + mRecordedProgramUri = + insertRecordedProgram( + getRecordedProgram(), + mChannel.getChannelId(), + Uri.fromFile(mStorageDir).toString(), + calculateRecordingSizeInBytes(), + mRecordStartTime, + recordEndTime); + if (mRecordedProgramUri == null) { + new DeleteRecordingTask().execute(mStorageDir); + mSession.onError(TvInputManager.RECORDING_ERROR_UNKNOWN); + Log.e(TAG, "Inserting a recording to DB failed"); + return; + } + } else { + updateRecordedProgram( + RecordedProgramState.FINISHED, recordEndTime, calculateRecordingSizeInBytes()); + } mDvrStorageManager.writeCaptionInfoFiles(mCaptionTracks); mSession.onRecordFinished(mRecordedProgramUri); } diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java index eb3a7d0c..fedb5f6b 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java +++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSession.java @@ -27,24 +27,18 @@ import android.os.SystemClock; import android.util.Log; import android.view.Surface; import android.view.View; - import com.android.tv.common.CommonPreferences.CommonPreferencesChangedListener; import com.android.tv.common.compat.TisSessionCompat; -import com.android.tv.common.dagger.annotations.ApplicationContext; import com.android.tv.tuner.prefs.TunerPreferences; +import com.android.tv.tuner.source.TsDataSourceManager; import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; -import com.android.tv.tuner.tvinput.factory.TunerSessionFactory; -import com.android.tv.tuner.tvinput.factory.TunerSessionFactory.SessionRecordingCallback; import com.android.tv.tuner.tvinput.factory.TunerSessionFactory.SessionReleasedCallback; - -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; /** * Provides a tuner TV input session. Main tuner input functions are implemented in {@link * TunerSessionWorker}. */ -@AutoFactory(className = "TunerSessionV1Factory", implementing = TunerSessionFactory.class) public class TunerSession extends TisSessionCompat implements CommonPreferencesChangedListener { private static final String TAG = "TunerSession"; @@ -53,26 +47,26 @@ public class TunerSession extends TisSessionCompat implements CommonPreferencesC private final TunerSessionOverlay mTunerSessionOverlay; private final TunerSessionWorker mSessionWorker; private final SessionReleasedCallback mReleasedCallback; - private final SessionRecordingCallback mRecordingCallback; private boolean mPlayPaused; private long mTuneStartTimestamp; public TunerSession( - @Provided @ApplicationContext Context context, + Context context, ChannelDataManager channelDataManager, SessionReleasedCallback releasedCallback, - SessionRecordingCallback recordingCallback, - @Provided TunerSessionWorker.Factory tunerSessionWorkerFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, + TsDataSourceManager.Factory tsDataSourceManagerFactory) { super(context); mReleasedCallback = releasedCallback; - mRecordingCallback = recordingCallback; mTunerSessionOverlay = new TunerSessionOverlay(context); mSessionWorker = - tunerSessionWorkerFactory.create( + new TunerSessionWorker( context, channelDataManager, this, - mTunerSessionOverlay); + mTunerSessionOverlay, + concurrentDvrPlaybackFlags, + tsDataSourceManagerFactory); TunerPreferences.setCommonPreferencesChangedListener(this); } @@ -210,8 +204,4 @@ public class TunerSession extends TisSessionCompat implements CommonPreferencesC public void onCommonPreferencesChanged() { mSessionWorker.sendMessage(TunerSessionWorker.MSG_TUNER_PREFERENCES_CHANGED); } - - public Uri getRecordingUri(Uri channelUri) { - return mRecordingCallback.getRecordingUri(channelUri); - } } diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionExoV2.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionExoV2.java index 7ebb2b21..4eca44d6 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionExoV2.java +++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionExoV2.java @@ -27,21 +27,15 @@ import android.os.SystemClock; import android.util.Log; import android.view.Surface; import android.view.View; - import com.android.tv.common.CommonPreferences.CommonPreferencesChangedListener; import com.android.tv.common.compat.TisSessionCompat; -import com.android.tv.common.dagger.annotations.ApplicationContext; import com.android.tv.tuner.prefs.TunerPreferences; +import com.android.tv.tuner.source.TsDataSourceManager; import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; -import com.android.tv.tuner.tvinput.factory.TunerSessionFactory; -import com.android.tv.tuner.tvinput.factory.TunerSessionFactory.SessionRecordingCallback; import com.android.tv.tuner.tvinput.factory.TunerSessionFactory.SessionReleasedCallback; - -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; /** Provides a tuner TV input session. */ -@AutoFactory(implementing = TunerSessionFactory.class) public class TunerSessionExoV2 extends TisSessionCompat implements CommonPreferencesChangedListener { @@ -51,26 +45,26 @@ public class TunerSessionExoV2 extends TisSessionCompat private final TunerSessionOverlay mTunerSessionOverlay; private final TunerSessionWorkerExoV2 mSessionWorker; private final SessionReleasedCallback mReleasedCallback; - private final SessionRecordingCallback mRecordingCallback; private boolean mPlayPaused; private long mTuneStartTimestamp; public TunerSessionExoV2( - @Provided @ApplicationContext Context context, + Context context, ChannelDataManager channelDataManager, SessionReleasedCallback releasedCallback, - SessionRecordingCallback recordingCallback, - @Provided TunerSessionWorkerExoV2.Factory tunerSessionWorkerExoV2Factory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, + TsDataSourceManager.Factory tsDataSourceManagerFactory) { super(context); mReleasedCallback = releasedCallback; - mRecordingCallback = recordingCallback; mTunerSessionOverlay = new TunerSessionOverlay(context); mSessionWorker = - tunerSessionWorkerExoV2Factory.create( + new TunerSessionWorkerExoV2( context, channelDataManager, this, - mTunerSessionOverlay); + mTunerSessionOverlay, + concurrentDvrPlaybackFlags, + tsDataSourceManagerFactory); TunerPreferences.setCommonPreferencesChangedListener(this); } @@ -209,8 +203,4 @@ public class TunerSessionExoV2 extends TisSessionCompat public void onCommonPreferencesChanged() { mSessionWorker.sendMessage(TunerSessionWorkerExoV2.MSG_TUNER_PREFERENCES_CHANGED); } - - public Uri getRecordingUri(Uri channelUri) { - return mRecordingCallback.getRecordingUri(channelUri); - } } diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionOverlay.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionOverlay.java index 53e0bcc2..9f21e16a 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionOverlay.java +++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionOverlay.java @@ -26,18 +26,17 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; - +import com.android.tv.common.util.SystemPropertiesProxy; import com.android.tv.tuner.R; import com.android.tv.tuner.cc.CaptionLayout; import com.android.tv.tuner.cc.CaptionTrackRenderer; import com.android.tv.tuner.data.Cea708Data.CaptionEvent; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import com.android.tv.tuner.util.GlobalSettingsUtils; import com.android.tv.tuner.util.StatusTextUtils; /** Executes {@link Session} overlay changes on the main thread. */ /* package */ final class TunerSessionOverlay implements Handler.Callback { - private static final boolean DEBUG = false; /** Displays the given {@link String} message object in the message view. */ public static final int MSG_UI_SHOW_MESSAGE = 1; @@ -68,6 +67,8 @@ import com.android.tv.tuner.util.StatusTextUtils; /** Displays a toast signalling that a re-scan is required. Does not expect a message object. */ public static final int MSG_UI_TOAST_RESCAN_NEEDED = 11; + private static final String USBTUNER_SHOW_DEBUG = "persist.tv.tuner.show_debug"; + private final Context mContext; private final Handler mHandler; private final View mOverlayView; @@ -87,12 +88,13 @@ import com.android.tv.tuner.util.StatusTextUtils; mHandler = new Handler(this); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + boolean showDebug = SystemPropertiesProxy.getBoolean(USBTUNER_SHOW_DEBUG, false); mOverlayView = inflater.inflate(R.layout.ut_overlay_view, null); mMessageLayout = mOverlayView.findViewById(R.id.message_layout); mMessageLayout.setVisibility(View.INVISIBLE); mMessageView = mOverlayView.findViewById(R.id.message); mStatusView = mOverlayView.findViewById(R.id.tuner_status); - mStatusView.setVisibility(DEBUG ? View.VISIBLE : View.INVISIBLE); + mStatusView.setVisibility(showDebug ? View.VISIBLE : View.INVISIBLE); mAudioStatusView = mOverlayView.findViewById(R.id.audio_status); mAudioStatusView.setVisibility(View.INVISIBLE); CaptionLayout captionLayout = mOverlayView.findViewById(R.id.caption); diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java index 792dfaab..d3f9409b 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java +++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorker.java @@ -44,22 +44,22 @@ import android.util.Pair; import android.util.SparseArray; import android.view.Surface; import android.view.accessibility.CaptioningManager; - import com.android.tv.common.CommonPreferences.TrickplaySetting; import com.android.tv.common.SoftPreconditions; import com.android.tv.common.TvContentRatingCache; import com.android.tv.common.compat.TvInputConstantCompat; import com.android.tv.common.customization.CustomizationManager; import com.android.tv.common.customization.CustomizationManager.TRICKPLAY_MODE; -import com.android.tv.common.dev.DeveloperPreferences; +import com.android.tv.common.experiments.Experiments; import com.android.tv.common.feature.CommonFeatures; +import com.android.tv.common.util.SystemPropertiesProxy; import com.android.tv.tuner.data.Cea708Data; -import com.android.tv.tuner.data.Channel; import com.android.tv.tuner.data.PsipData.EitItem; import com.android.tv.tuner.data.PsipData.TvTracksInterface; -import com.android.tv.tuner.data.Track.AtscAudioTrack; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; import com.android.tv.tuner.data.TunerChannel; +import com.android.tv.tuner.data.nano.Channel; +import com.android.tv.tuner.data.nano.Track.AtscAudioTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import com.android.tv.tuner.exoplayer.MpegTsPlayer; import com.android.tv.tuner.exoplayer.MpegTsRendererBuilder; import com.android.tv.tuner.exoplayer.buffer.BufferManager; @@ -74,15 +74,10 @@ import com.android.tv.tuner.ts.EventDetector.EventListener; import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; import com.android.tv.tuner.tvinput.debug.TunerDebug; import com.android.tv.tuner.util.StatusTextUtils; - import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.audio.AudioCapabilities; -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; import com.google.common.collect.ImmutableList; - -import com.android.tv.common.flags.LegacyFlags; - +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; import java.io.File; import java.util.ArrayList; import java.util.Iterator; @@ -108,6 +103,8 @@ public class TunerSessionWorker private static final boolean DEBUG = false; private static final boolean ENABLE_PROFILER = true; private static final String PLAY_FROM_CHANNEL = "channel"; + private static final String MAX_BUFFER_SIZE_KEY = "tv.tuner.buffersize_mbytes"; + private static final int MAX_BUFFER_SIZE_DEF = 2 * 1024; // 2GB private static final int MIN_BUFFER_SIZE_DEF = 256; // 256MB // Public messages @@ -192,8 +189,6 @@ public class TunerSessionWorker private final int mMaxTrickplayBufferSizeMb; private final File mTrickplayBufferDir; private final @TRICKPLAY_MODE int mTrickplayModeCustomization; - private final LegacyFlags mLegacyFlags; - private final MpegTsRendererBuilder.Factory mMpegTsRendererBuilderFactory; private volatile Surface mSurface; private volatile float mVolume = 1.0f; private volatile boolean mCaptionEnabled; @@ -236,44 +231,25 @@ public class TunerSessionWorker private boolean mIsActiveSession; private boolean mReleaseRequested; // Guarded by mReleaseLock private final Object mReleaseLock = new Object(); - private Uri mChannelUri; - private Uri mRecordingUri; - private boolean mOnTuneUsesRecording = false; + private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; private int mSignalStrength; private long mRecordedProgramStartTimeMs; - /** - * Factory for {@link TunerSessionWorker}. - * - * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory} - * generated class. - */ - public interface Factory { - public TunerSessionWorker create( - Context context, - ChannelDataManager channelDataManager, - TunerSession tunerSession, - TunerSessionOverlay tunerSessionOverlay); - } - - @AutoFactory(implementing = Factory.class) public TunerSessionWorker( Context context, ChannelDataManager channelDataManager, TunerSession tunerSession, TunerSessionOverlay tunerSessionOverlay, - @Provided LegacyFlags legacyFlags, - @Provided MpegTsRendererBuilder.Factory mpegTsRendererBuilderFactory, - @Provided TsDataSourceManager.Factory tsDataSourceManagerFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, + TsDataSourceManager.Factory tsDataSourceManagerFactory) { this( context, channelDataManager, tunerSession, tunerSessionOverlay, null, - legacyFlags, - mpegTsRendererBuilderFactory, + concurrentDvrPlaybackFlags, tsDataSourceManagerFactory); } @@ -284,10 +260,9 @@ public class TunerSessionWorker TunerSession tunerSession, TunerSessionOverlay tunerSessionOverlay, @Nullable Handler handler, - LegacyFlags legacyFlags, - MpegTsRendererBuilder.Factory mpegTsRendererBuilderFactory, + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, TsDataSourceManager.Factory tsDataSourceManagerFactory) { - mLegacyFlags = legacyFlags; + this.mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags; if (DEBUG) Log.d(TAG, "TunerSessionWorker created"); mContext = context; if (handler != null) { @@ -302,8 +277,6 @@ public class TunerSessionWorker mSession = tunerSession; mTunerSessionOverlay = tunerSessionOverlay; mChannelDataManager = channelDataManager; - mMpegTsRendererBuilderFactory = mpegTsRendererBuilderFactory; - mRecordingUri = null; mChannelDataManager.setListener(this); mChannelDataManager.checkDataVersion(mContext); mSourceManager = tsDataSourceManagerFactory.create(false); @@ -320,7 +293,8 @@ public class TunerSessionWorker (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); mCaptionEnabled = captioningManager.isEnabled(); mPlaybackParams.setSpeed(1.0f); - mMaxTrickplayBufferSizeMb = DeveloperPreferences.MAX_BUFFER_SIZE_MBYTES.get(context); + mMaxTrickplayBufferSizeMb = + SystemPropertiesProxy.getInt(MAX_BUFFER_SIZE_KEY, MAX_BUFFER_SIZE_DEF); mTrickplayModeCustomization = CustomizationManager.getTrickplayMode(context); if (mTrickplayModeCustomization == CustomizationManager.TRICKPLAY_MODE_USE_EXTERNAL_STORAGE) { @@ -513,11 +487,6 @@ public class TunerSessionWorker // Final status // notification of STATE_ENDED from MpegTsPlayer will be ignored afterwards. Log.i(TAG, "Player ended: end of stream"); - if (mOnTuneUsesRecording) { - mRecordingUri = null; - mSession.notifyChannelRetuned(mChannelUri); - sendMessage(MSG_TUNE, mChannelUri); - } if (mChannel != null) { sendMessage(MSG_RETRY_PLAYBACK, System.identityHashCode(mPlayer)); } @@ -546,10 +515,10 @@ public class TunerSessionWorker @Override public void onVideoSizeChanged(int width, int height, float pixelWidthHeight) { if (mChannel != null && mChannel.hasVideo()) { - updateVideoTrack(width, height, pixelWidthHeight); + updateVideoTrack(width, height); } if (mRecordingId != null) { - updateVideoTrack(width, height, pixelWidthHeight); + updateVideoTrack(width, height); } } @@ -563,9 +532,6 @@ public class TunerSessionWorker } else { mBufferStartTimeMs = mRecordStartTimeMs = System.currentTimeMillis(); } - if (mOnTuneUsesRecording) { - mBufferStartTimeMs = mRecordStartTimeMs = mRecordedProgramStartTimeMs; - } notifyVideoAvailable(); mReportedDrawnToSurface = true; @@ -621,7 +587,7 @@ public class TunerSessionWorker // ChannelDataManager.ProgramInfoListener @Override public void onProgramsArrived(TunerChannel channel, List<EitItem> programs) { - sendMessage(MSG_SCHEDULE_OF_PROGRAMS, Pair.create(channel, programs)); + sendMessage(MSG_SCHEDULE_OF_PROGRAMS, new Pair<>(channel, programs)); } @Override @@ -636,7 +602,7 @@ public class TunerSessionWorker @Override public void onRequestProgramsResponse(TunerChannel channel, List<EitItem> programs) { - sendMessage(MSG_PROGRAM_DATA_RESULT, Pair.create(channel, programs)); + sendMessage(MSG_PROGRAM_DATA_RESULT, new Pair<>(channel, programs)); } // PlaybackBufferListener @@ -684,7 +650,7 @@ public class TunerSessionWorker } private static class RecordedProgram { - private final long mChannelId; + // private final long mChannelId; private final String mDataUri; private final long mStartTimeMillis; @@ -696,13 +662,14 @@ public class TunerSessionWorker public RecordedProgram(Cursor cursor) { int index = 0; - mChannelId = cursor.getLong(index++); + // mChannelId = cursor.getLong(index++); + index++; mDataUri = cursor.getString(index++); mStartTimeMillis = cursor.getLong(index++); } public RecordedProgram(long channelId, String dataUri) { - mChannelId = channelId; + // mChannelId = channelId; mDataUri = dataUri; mStartTimeMillis = 0; } @@ -722,10 +689,6 @@ public class TunerSessionWorker public long getStartTime() { return mStartTimeMillis; } - - public long getChannelId() { - return mChannelId; - } } private RecordedProgram getRecordedProgram(Uri recordedUri) { @@ -748,13 +711,9 @@ public class TunerSessionWorker } } - private String parseRecording(Uri uri, long channelId) { + private String parseRecording(Uri uri) { RecordedProgram recording = getRecordedProgram(uri); if (recording != null) { - if (channelId != -1 && channelId != recording.getChannelId()) { - // Recorded URI is of some other channel - return null; - } mRecordedProgramStartTimeMs = recording.getStartTime(); return recording.getDataUri(); } @@ -867,20 +826,10 @@ public class TunerSessionWorker mIsActiveSession = true; } String recording = null; - mOnTuneUsesRecording = false; long channelId = parseChannel(channelUri); TunerChannel channel = (channelId == -1) ? null : mChannelDataManager.getChannel(channelId); - mRecordingUri = mSession.getRecordingUri(channelUri); if (channelId == -1) { - recording = parseRecording(channelUri, channelId); - - } else if (mRecordingUri != null) { - mChannelUri = channelUri; - recording = parseRecording(mRecordingUri, channelId); - if (recording != null) { - mOnTuneUsesRecording = true; - channel = null; - } + recording = parseRecording(channelUri); } if (channel == null && recording == null) { Log.w(TAG, "onTune() is failed. Can't find channel for " + channelUri); @@ -1184,15 +1133,8 @@ public class TunerSessionWorker if (mPlayer == null) { return true; } - long seekPosMs = timeMs; - if (mRecordingId != null) { - long systemBufferTime = System.currentTimeMillis() - SEEK_MARGIN_MS; - if (seekPosMs > systemBufferTime) { - seekPosMs = systemBufferTime; - } - } setTrickplayEnabledIfNeeded(); - doTimeShiftSeekTo(seekPosMs); + doTimeShiftSeekTo(timeMs); return true; } @@ -1490,7 +1432,8 @@ public class TunerSessionWorker } MpegTsPlayer player = new MpegTsPlayer( - mMpegTsRendererBuilderFactory.create(mContext, bufferManager, this), + new MpegTsRendererBuilder( + mContext, bufferManager, this, mConcurrentDvrPlaybackFlags), mHandler, mSourceManager, capabilities, @@ -1501,7 +1444,7 @@ public class TunerSessionWorker player.setVideoEventListener(this); player.setCaptionServiceNumber( mCaptionTrack != null - ? mCaptionTrack.getServiceNumber() + ? mCaptionTrack.serviceNumber : Cea708Data.EMPTY_SERVICE_NUMBER); return player; } @@ -1511,7 +1454,7 @@ public class TunerSessionWorker mTunerSessionOverlay.sendUiMessage( TunerSessionOverlay.MSG_UI_START_CAPTION_TRACK, mCaptionTrack); if (mPlayer != null) { - mPlayer.setCaptionServiceNumber(mCaptionTrack.getServiceNumber()); + mPlayer.setCaptionServiceNumber(mCaptionTrack.serviceNumber); } } } @@ -1568,13 +1511,12 @@ public class TunerSessionWorker } } - private void updateVideoTrack(int width, int height, float pixelWidthHeight) { + private void updateVideoTrack(int width, int height) { removeTvTracks(TvTrackInfo.TYPE_VIDEO); mTvTracks.add( new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID) .setVideoWidth(width) .setVideoHeight(height) - .setVideoPixelAspectRatio(pixelWidthHeight) .build()); mSession.notifyTracksChanged(mTvTracks); mSession.notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID); @@ -1588,7 +1530,7 @@ public class TunerSessionWorker if (audioTracks != null) { int index = 0; for (AtscAudioTrack audioTrack : audioTracks) { - audioTrack = audioTrack.toBuilder().setIndex(index).build(); + audioTrack.index = index; mAudioTrackMap.put(index, audioTrack); ++index; } @@ -1618,10 +1560,10 @@ public class TunerSessionWorker String language = !TextUtils.isEmpty(infoFromPlayer.language) ? infoFromPlayer.language - : (infoFromEit != null && infoFromEit.hasLanguage()) - ? infoFromEit.getLanguage() - : (infoFromVct != null && infoFromVct.hasLanguage()) - ? infoFromVct.getLanguage() + : (infoFromEit != null && infoFromEit.language != null) + ? infoFromEit.language + : (infoFromVct != null && infoFromVct.language != null) + ? infoFromVct.language : null; TvTrackInfo.Builder builder = new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, AUDIO_TRACK_PREFIX + i); @@ -1642,20 +1584,20 @@ public class TunerSessionWorker mCaptionTrackMap.clear(); if (captionTracks != null) { for (AtscCaptionTrack captionTrack : captionTracks) { - if (mCaptionTrackMap.indexOfKey(captionTrack.getServiceNumber()) >= 0) { + if (mCaptionTrackMap.indexOfKey(captionTrack.serviceNumber) >= 0) { continue; } - String language = captionTrack.getLanguage(); + String language = captionTrack.language; // The service number of the caption service is used for track id of a subtitle. // Later, when a subtitle is chosen, track id will be passed on to TsParser. TvTrackInfo.Builder builder = new TvTrackInfo.Builder( TvTrackInfo.TYPE_SUBTITLE, - SUBTITLE_TRACK_PREFIX + captionTrack.getServiceNumber()); + SUBTITLE_TRACK_PREFIX + captionTrack.serviceNumber); builder.setLanguage(language); mTvTracks.add(builder.build()); - mCaptionTrackMap.put(captionTrack.getServiceNumber(), captionTrack); + mCaptionTrackMap.put(captionTrack.serviceNumber, captionTrack); } } mSession.notifyTracksChanged(mTvTracks); @@ -1835,9 +1777,6 @@ public class TunerSessionWorker } else { mBufferStartTimeMs = mRecordStartTimeMs = System.currentTimeMillis(); } - if (mOnTuneUsesRecording) { - mBufferStartTimeMs = mRecordStartTimeMs = mRecordedProgramStartTimeMs; - } mLastPositionMs = 0; mCaptionTrack = null; mSignalStrength = TvInputConstantCompat.SIGNAL_STRENGTH_UNKNOWN; @@ -1845,14 +1784,6 @@ public class TunerSessionWorker mSession.notifySignalStrength(mSignalStrength); } mHandler.sendEmptyMessage(MSG_PARENTAL_CONTROLS); - if (mOnTuneUsesRecording) { - mHandler.obtainMessage( - MSG_TIMESHIFT_SEEK_TO, - 1, - 0, - System.currentTimeMillis() - SEEK_MARGIN_MS) - .sendToTarget(); - } } private void doReschedulePrograms() { @@ -1874,7 +1805,7 @@ public class TunerSessionWorker + " current program: " + getCurrentProgram()); } - mHandler.obtainMessage(MSG_SCHEDULE_OF_PROGRAMS, Pair.create(mChannel, mPrograms)) + mHandler.obtainMessage(MSG_SCHEDULE_OF_PROGRAMS, new Pair<>(mChannel, mPrograms)) .sendToTarget(); } mHandler.removeMessages(MSG_RESCHEDULE_PROGRAMS); @@ -2035,12 +1966,10 @@ public class TunerSessionWorker private void doDiscoverCaptionServiceNumber(int serviceNumber) { int index = mCaptionTrackMap.indexOfKey(serviceNumber); if (index < 0) { - AtscCaptionTrack captionTrack = - AtscCaptionTrack.newBuilder() - .setServiceNumber(serviceNumber) - .setWideAspectRatio(false) - .setEasyReader(false) - .build(); + AtscCaptionTrack captionTrack = new AtscCaptionTrack(); + captionTrack.serviceNumber = serviceNumber; + captionTrack.wideAspectRatio = false; + captionTrack.easyReader = false; mCaptionTrackMap.put(serviceNumber, captionTrack); mTvTracks.add( new TvTrackInfo.Builder( @@ -2059,7 +1988,7 @@ public class TunerSessionWorker ImmutableList<TvContentRating> ratings = mTvContentRatingCache.getRatings(currentProgram.getContentRating()); if ((ratings == null || ratings.isEmpty())) { - if (mLegacyFlags.enableUnratedContentSettings()) { + if (Experiments.ENABLE_UNRATED_CONTENT_SETTINGS.get()) { ratings = ImmutableList.of(TvContentRating.UNRATED); } else { ratings = NO_CONTENT_RATINGS; diff --git a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java index f56e4879..82afff15 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java +++ b/tuner/src/com/android/tv/tuner/tvinput/TunerSessionWorkerExoV2.java @@ -44,22 +44,22 @@ import android.util.Pair; import android.util.SparseArray; import android.view.Surface; import android.view.accessibility.CaptioningManager; - import com.android.tv.common.CommonPreferences.TrickplaySetting; import com.android.tv.common.SoftPreconditions; import com.android.tv.common.TvContentRatingCache; import com.android.tv.common.compat.TvInputConstantCompat; import com.android.tv.common.customization.CustomizationManager; import com.android.tv.common.customization.CustomizationManager.TRICKPLAY_MODE; -import com.android.tv.common.dev.DeveloperPreferences; +import com.android.tv.common.experiments.Experiments; import com.android.tv.common.feature.CommonFeatures; +import com.android.tv.common.util.SystemPropertiesProxy; import com.android.tv.tuner.data.Cea708Data; -import com.android.tv.tuner.data.Channel; import com.android.tv.tuner.data.PsipData.EitItem; import com.android.tv.tuner.data.PsipData.TvTracksInterface; -import com.android.tv.tuner.data.Track.AtscAudioTrack; -import com.android.tv.tuner.data.Track.AtscCaptionTrack; import com.android.tv.tuner.data.TunerChannel; +import com.android.tv.tuner.data.nano.Channel; +import com.android.tv.tuner.data.nano.Track.AtscAudioTrack; +import com.android.tv.tuner.data.nano.Track.AtscCaptionTrack; import com.android.tv.tuner.exoplayer.MpegTsPlayer; import com.android.tv.tuner.exoplayer.MpegTsRendererBuilder; import com.android.tv.tuner.exoplayer.buffer.BufferManager; @@ -74,15 +74,10 @@ import com.android.tv.tuner.ts.EventDetector.EventListener; import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; import com.android.tv.tuner.tvinput.debug.TunerDebug; import com.android.tv.tuner.util.StatusTextUtils; - import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.audio.AudioCapabilities; -import com.google.auto.factory.AutoFactory; -import com.google.auto.factory.Provided; import com.google.common.collect.ImmutableList; - -import com.android.tv.common.flags.LegacyFlags; - +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; import java.io.File; import java.util.ArrayList; import java.util.Iterator; @@ -105,6 +100,8 @@ public class TunerSessionWorkerExoV2 private static final boolean DEBUG = false; private static final boolean ENABLE_PROFILER = true; private static final String PLAY_FROM_CHANNEL = "channel"; + private static final String MAX_BUFFER_SIZE_KEY = "tv.tuner.buffersize_mbytes"; + private static final int MAX_BUFFER_SIZE_DEF = 2 * 1024; // 2GB private static final int MIN_BUFFER_SIZE_DEF = 256; // 256MB // Public messages @@ -191,7 +188,6 @@ public class TunerSessionWorkerExoV2 private final int mMaxTrickplayBufferSizeMb; private final File mTrickplayBufferDir; private final @TRICKPLAY_MODE int mTrickplayModeCustomization; - private final MpegTsRendererBuilder.Factory mMpegTsRendererBuilderFactory; private volatile Surface mSurface; private volatile float mVolume = 1.0f; private volatile boolean mCaptionEnabled; @@ -234,45 +230,25 @@ public class TunerSessionWorkerExoV2 private boolean mIsActiveSession; private boolean mReleaseRequested; // Guarded by mReleaseLock private final Object mReleaseLock = new Object(); - private final LegacyFlags mLegacyFlags; - private Uri mChannelUri; - private Uri mRecordingUri; - private boolean mOnTuneUsesRecording = false; + private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; private int mSignalStrength; private long mRecordedProgramStartTimeMs; - /** - * Factory for {@link TunerSessionWorkerExoV2}. - * - * <p>This wrapper class keeps other classes from needing to reference the {@link AutoFactory} - * generated class. - */ - public interface Factory { - public TunerSessionWorkerExoV2 create( - Context context, - ChannelDataManager channelDataManager, - TunerSessionExoV2 tunerSession, - TunerSessionOverlay tunerSessionOverlay); - } - - @AutoFactory(implementing = Factory.class) public TunerSessionWorkerExoV2( Context context, ChannelDataManager channelDataManager, TunerSessionExoV2 tunerSession, TunerSessionOverlay tunerSessionOverlay, - @Provided LegacyFlags legacyFlags, - @Provided MpegTsRendererBuilder.Factory mpegTsRendererBuilderFactory, - @Provided TsDataSourceManager.Factory tsDataSourceManagerFactory) { + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, + TsDataSourceManager.Factory tsDataSourceManagerFactory) { this( context, channelDataManager, tunerSession, tunerSessionOverlay, null, - legacyFlags, - mpegTsRendererBuilderFactory, + concurrentDvrPlaybackFlags, tsDataSourceManagerFactory); } @@ -283,10 +259,9 @@ public class TunerSessionWorkerExoV2 TunerSessionExoV2 tunerSession, TunerSessionOverlay tunerSessionOverlay, @Nullable Handler handler, - LegacyFlags legacyFlags, - MpegTsRendererBuilder.Factory mpegTsRendererBuilderFactory, + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, TsDataSourceManager.Factory tsDataSourceManagerFactory) { - mLegacyFlags = legacyFlags; + mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags; if (DEBUG) { Log.d(TAG, "TunerSessionWorkerExoV2 created"); } @@ -303,8 +278,6 @@ public class TunerSessionWorkerExoV2 mSession = tunerSession; mTunerSessionOverlay = tunerSessionOverlay; mChannelDataManager = channelDataManager; - mMpegTsRendererBuilderFactory = mpegTsRendererBuilderFactory; - mRecordingUri = null; mChannelDataManager.setListener(this); mChannelDataManager.checkDataVersion(mContext); mSourceManager = tsDataSourceManagerFactory.create(false); @@ -321,7 +294,8 @@ public class TunerSessionWorkerExoV2 (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE); mCaptionEnabled = captioningManager.isEnabled(); mPlaybackParams.setSpeed(1.0f); - mMaxTrickplayBufferSizeMb = DeveloperPreferences.MAX_BUFFER_SIZE_MBYTES.get(context); + mMaxTrickplayBufferSizeMb = + SystemPropertiesProxy.getInt(MAX_BUFFER_SIZE_KEY, MAX_BUFFER_SIZE_DEF); mTrickplayModeCustomization = CustomizationManager.getTrickplayMode(context); if (mTrickplayModeCustomization == CustomizationManager.TRICKPLAY_MODE_USE_EXTERNAL_STORAGE) { @@ -519,11 +493,6 @@ public class TunerSessionWorkerExoV2 // Final status // notification of STATE_ENDED from MpegTsPlayer will be ignored afterwards. Log.i(TAG, "Player ended: end of stream"); - if (mOnTuneUsesRecording) { - mRecordingUri = null; - mSession.notifyChannelRetuned(mChannelUri); - sendMessage(MSG_TUNE, mChannelUri); - } if (mChannel != null) { sendMessage(MSG_RETRY_PLAYBACK, System.identityHashCode(mPlayer)); } @@ -552,10 +521,10 @@ public class TunerSessionWorkerExoV2 @Override public void onVideoSizeChanged(int width, int height, float pixelWidthHeight) { if (mChannel != null && mChannel.hasVideo()) { - updateVideoTrack(width, height, pixelWidthHeight); + updateVideoTrack(width, height); } if (mRecordingId != null) { - updateVideoTrack(width, height, pixelWidthHeight); + updateVideoTrack(width, height); } } @@ -571,9 +540,6 @@ public class TunerSessionWorkerExoV2 } else { mBufferStartTimeMs = mRecordStartTimeMs = System.currentTimeMillis(); } - if (mOnTuneUsesRecording) { - mBufferStartTimeMs = mRecordStartTimeMs = mRecordedProgramStartTimeMs; - } notifyVideoAvailable(); mReportedDrawnToSurface = true; @@ -629,7 +595,7 @@ public class TunerSessionWorkerExoV2 // ChannelDataManager.ProgramInfoListener @Override public void onProgramsArrived(TunerChannel channel, List<EitItem> programs) { - sendMessage(MSG_SCHEDULE_OF_PROGRAMS, Pair.create(channel, programs)); + sendMessage(MSG_SCHEDULE_OF_PROGRAMS, new Pair<>(channel, programs)); } @Override @@ -644,7 +610,7 @@ public class TunerSessionWorkerExoV2 @Override public void onRequestProgramsResponse(TunerChannel channel, List<EitItem> programs) { - sendMessage(MSG_PROGRAM_DATA_RESULT, Pair.create(channel, programs)); + sendMessage(MSG_PROGRAM_DATA_RESULT, new Pair<>(channel, programs)); } // PlaybackBufferListener @@ -692,7 +658,7 @@ public class TunerSessionWorkerExoV2 } private static class RecordedProgram { - private final long mChannelId; + // private final long mChannelId; private final String mDataUri; private final long mStartTimeMillis; @@ -704,13 +670,14 @@ public class TunerSessionWorkerExoV2 public RecordedProgram(Cursor cursor) { int index = 0; - mChannelId = cursor.getLong(index++); + // mChannelId = cursor.getLong(index++); + index++; mDataUri = cursor.getString(index++); mStartTimeMillis = cursor.getLong(index++); } public RecordedProgram(long channelId, String dataUri) { - mChannelId = channelId; + // mChannelId = channelId; mDataUri = dataUri; mStartTimeMillis = 0; } @@ -730,10 +697,6 @@ public class TunerSessionWorkerExoV2 public long getStartTime() { return mStartTimeMillis; } - - public long getChannelId() { - return mChannelId; - } } private RecordedProgram getRecordedProgram(Uri recordedUri) { @@ -758,13 +721,9 @@ public class TunerSessionWorkerExoV2 } } - private String parseRecording(Uri uri, long channelId) { + private String parseRecording(Uri uri) { RecordedProgram recording = getRecordedProgram(uri); if (recording != null) { - if (channelId != -1 && channelId != recording.getChannelId()) { - // Recorded URI is of some other channel - return null; - } mRecordedProgramStartTimeMs = recording.getStartTime(); return recording.getDataUri(); } @@ -877,19 +836,10 @@ public class TunerSessionWorkerExoV2 mIsActiveSession = true; } String recording = null; - mOnTuneUsesRecording = false; long channelId = parseChannel(channelUri); TunerChannel channel = (channelId == -1) ? null : mChannelDataManager.getChannel(channelId); - mRecordingUri = mSession.getRecordingUri(channelUri); if (channelId == -1) { - recording = parseRecording(channelUri, channelId); - } else if (mRecordingUri != null) { - mChannelUri = channelUri; - recording = parseRecording(mRecordingUri, channelId); - if (recording != null) { - mOnTuneUsesRecording = true; - channel = null; - } + recording = parseRecording(channelUri); } if (channel == null && recording == null) { Log.w(TAG, "onTune() is failed. Can't find channel for " + channelUri); @@ -1492,7 +1442,8 @@ public class TunerSessionWorkerExoV2 } MpegTsPlayer player = new MpegTsPlayer( - mMpegTsRendererBuilderFactory.create(mContext, bufferManager, this), + new MpegTsRendererBuilder( + mContext, bufferManager, this, mConcurrentDvrPlaybackFlags), mHandler, mSourceManager, capabilities, @@ -1505,7 +1456,7 @@ public class TunerSessionWorkerExoV2 player.setVideoEventListener(this); player.setCaptionServiceNumber( mCaptionTrack != null - ? mCaptionTrack.getServiceNumber() + ? mCaptionTrack.serviceNumber : Cea708Data.EMPTY_SERVICE_NUMBER); return player; } @@ -1515,7 +1466,7 @@ public class TunerSessionWorkerExoV2 mTunerSessionOverlay.sendUiMessage( TunerSessionOverlay.MSG_UI_START_CAPTION_TRACK, mCaptionTrack); if (mPlayer != null) { - mPlayer.setCaptionServiceNumber(mCaptionTrack.getServiceNumber()); + mPlayer.setCaptionServiceNumber(mCaptionTrack.serviceNumber); } } } @@ -1572,13 +1523,12 @@ public class TunerSessionWorkerExoV2 } } - private void updateVideoTrack(int width, int height, float pixelWidthHeight) { + private void updateVideoTrack(int width, int height) { removeTvTracks(TvTrackInfo.TYPE_VIDEO); mTvTracks.add( new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID) .setVideoWidth(width) .setVideoHeight(height) - .setVideoPixelAspectRatio(pixelWidthHeight) .build()); mSession.notifyTracksChanged(mTvTracks); mSession.notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID); @@ -1592,7 +1542,7 @@ public class TunerSessionWorkerExoV2 if (audioTracks != null) { int index = 0; for (AtscAudioTrack audioTrack : audioTracks) { - audioTrack = audioTrack.toBuilder().setIndex(index).build(); + audioTrack.index = index; mAudioTrackMap.put(index, audioTrack); ++index; } @@ -1622,10 +1572,10 @@ public class TunerSessionWorkerExoV2 String language = !TextUtils.isEmpty(infoFromPlayer.language) ? infoFromPlayer.language - : (infoFromEit != null && infoFromEit.hasLanguage()) - ? infoFromEit.getLanguage() - : (infoFromVct != null && infoFromVct.hasLanguage()) - ? infoFromVct.getLanguage() + : (infoFromEit != null && infoFromEit.language != null) + ? infoFromEit.language + : (infoFromVct != null && infoFromVct.language != null) + ? infoFromVct.language : null; TvTrackInfo.Builder builder = new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, AUDIO_TRACK_PREFIX + i); @@ -1646,20 +1596,20 @@ public class TunerSessionWorkerExoV2 mCaptionTrackMap.clear(); if (captionTracks != null) { for (AtscCaptionTrack captionTrack : captionTracks) { - if (mCaptionTrackMap.indexOfKey(captionTrack.getServiceNumber()) >= 0) { + if (mCaptionTrackMap.indexOfKey(captionTrack.serviceNumber) >= 0) { continue; } - String language = captionTrack.getLanguage(); + String language = captionTrack.language; // The service number of the caption service is used for track id of a subtitle. // Later, when a subtitle is chosen, track id will be passed on to TsParser. TvTrackInfo.Builder builder = new TvTrackInfo.Builder( TvTrackInfo.TYPE_SUBTITLE, - SUBTITLE_TRACK_PREFIX + captionTrack.getServiceNumber()); + SUBTITLE_TRACK_PREFIX + captionTrack.serviceNumber); builder.setLanguage(language); mTvTracks.add(builder.build()); - mCaptionTrackMap.put(captionTrack.getServiceNumber(), captionTrack); + mCaptionTrackMap.put(captionTrack.serviceNumber, captionTrack); } } mSession.notifyTracksChanged(mTvTracks); @@ -1841,9 +1791,6 @@ public class TunerSessionWorkerExoV2 } else { mBufferStartTimeMs = mRecordStartTimeMs = System.currentTimeMillis(); } - if (mOnTuneUsesRecording) { - mBufferStartTimeMs = mRecordStartTimeMs = mRecordedProgramStartTimeMs; - } mLastPositionMs = 0; mCaptionTrack = null; mSignalStrength = TvInputConstantCompat.SIGNAL_STRENGTH_UNKNOWN; @@ -1851,14 +1798,6 @@ public class TunerSessionWorkerExoV2 mSession.notifySignalStrength(mSignalStrength); } mHandler.sendEmptyMessage(MSG_PARENTAL_CONTROLS); - if (mOnTuneUsesRecording) { - mHandler.obtainMessage( - MSG_TIMESHIFT_SEEK_TO, - 1, - 0, - System.currentTimeMillis() - SEEK_MARGIN_MS) - .sendToTarget(); - } } private void doReschedulePrograms() { @@ -1880,7 +1819,7 @@ public class TunerSessionWorkerExoV2 + " current program: " + getCurrentProgram()); } - mHandler.obtainMessage(MSG_SCHEDULE_OF_PROGRAMS, Pair.create(mChannel, mPrograms)) + mHandler.obtainMessage(MSG_SCHEDULE_OF_PROGRAMS, new Pair<>(mChannel, mPrograms)) .sendToTarget(); } mHandler.removeMessages(MSG_RESCHEDULE_PROGRAMS); @@ -2041,13 +1980,10 @@ public class TunerSessionWorkerExoV2 private void doDiscoverCaptionServiceNumber(int serviceNumber) { int index = mCaptionTrackMap.indexOfKey(serviceNumber); if (index < 0) { - AtscCaptionTrack.Builder captionTrackBuilder = AtscCaptionTrack.newBuilder(); - AtscCaptionTrack captionTrack = - captionTrackBuilder - .setServiceNumber(serviceNumber) - .setWideAspectRatio(false) - .setEasyReader(false) - .build(); + AtscCaptionTrack captionTrack = new AtscCaptionTrack(); + captionTrack.serviceNumber = serviceNumber; + captionTrack.wideAspectRatio = false; + captionTrack.easyReader = false; mCaptionTrackMap.put(serviceNumber, captionTrack); mTvTracks.add( new TvTrackInfo.Builder( @@ -2066,7 +2002,7 @@ public class TunerSessionWorkerExoV2 ImmutableList<TvContentRating> ratings = mTvContentRatingCache.getRatings(currentProgram.getContentRating()); if ((ratings == null || ratings.isEmpty())) { - if (mLegacyFlags.enableUnratedContentSettings()) { + if (Experiments.ENABLE_UNRATED_CONTENT_SETTINGS.get()) { ratings = ImmutableList.of(TvContentRating.UNRATED); } else { ratings = NO_CONTENT_RATINGS; diff --git a/tuner/src/com/android/tv/tuner/tvinput/datamanager/ChannelDataManager.java b/tuner/src/com/android/tv/tuner/tvinput/datamanager/ChannelDataManager.java index 447618a4..585b28bc 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/datamanager/ChannelDataManager.java +++ b/tuner/src/com/android/tv/tuner/tvinput/datamanager/ChannelDataManager.java @@ -29,10 +29,11 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.RemoteException; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.format.DateUtils; import android.util.Log; +import com.android.tv.common.singletons.HasSingletons; +import com.android.tv.common.singletons.HasTvInputId; import com.android.tv.common.util.PermissionUtils; import com.android.tv.tuner.data.PsipData.EitItem; import com.android.tv.tuner.data.TunerChannel; @@ -50,7 +51,7 @@ import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -/** Manages the channel info and EPG data for a specific inputId. */ +/** Manages the channel info and EPG data through {@link TvInputManager}. */ public class ChannelDataManager implements Handler.Callback { private static final String TAG = "ChannelDataManager"; @@ -145,9 +146,9 @@ public class ChannelDataManager implements Handler.Callback { void onChannelHandlingDone(); } - public ChannelDataManager(Context context, String inputId) { + public ChannelDataManager(Context context) { mContext = context; - mInputId = inputId; + mInputId = HasSingletons.get(HasTvInputId.class, context).getEmbeddedTunerInputId(); mChannelsUri = TvContract.buildChannelsUriForInput(mInputId); mTunerChannelMap = new ConcurrentHashMap<>(); mTunerChannelIdMap = new ConcurrentSkipListMap<>(); @@ -381,12 +382,6 @@ public class ChannelDataManager implements Handler.Callback { return false; } - @NonNull - @Override - public String toString() { - return "ChannelDataManager[" + mInputId + "]"; - } - // Private methods private void handleEvents(TunerChannel channel, List<EitItem> items) { long channelId = getChannelId(channel); diff --git a/tuner/src/com/android/tv/tuner/tvinput/factory/TunerRecordingSessionFactory.java b/tuner/src/com/android/tv/tuner/tvinput/factory/TunerRecordingSessionFactory.java deleted file mode 100644 index c5950756..00000000 --- a/tuner/src/com/android/tv/tuner/tvinput/factory/TunerRecordingSessionFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 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.tvinput.factory; - -import android.media.tv.TvInputService.RecordingSession; - -import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; - -/** {@link RecordingSession} factory */ -public interface TunerRecordingSessionFactory { - - /** Called when a recording session is released */ - interface RecordingSessionReleasedCallback { - - /** - * Called when the given recording session is released. - * - * @param session The recording session that has been released. - */ - void onReleased(RecordingSession session); - } - - RecordingSession create( - String inputId, - RecordingSessionReleasedCallback releasedCallback, - ChannelDataManager channelDataManager); -} diff --git a/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactory.java b/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactory.java index e22562ac..a27cb22a 100644 --- a/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactory.java +++ b/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactory.java @@ -1,24 +1,7 @@ -/* - * Copyright (C) 2019 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.tvinput.factory; +import android.content.Context; import android.media.tv.TvInputService.Session; -import android.net.Uri; - import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; /** {@link android.media.tv.TvInputService.Session} factory */ @@ -35,19 +18,8 @@ public interface TunerSessionFactory { void onReleased(Session session); } - /** Called when recording URI is required for playback */ - interface SessionRecordingCallback { - - /** - * Called when recording URI is required for playback. - * - * @param channelUri for which recording URI is requested. - */ - Uri getRecordingUri(Uri channelUri); - } - Session create( + Context context, ChannelDataManager channelDataManager, - SessionReleasedCallback releasedCallback, - SessionRecordingCallback recordingCallback); + SessionReleasedCallback releasedCallback); } diff --git a/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactoryImpl.java b/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactoryImpl.java new file mode 100644 index 00000000..54e959e6 --- /dev/null +++ b/tuner/src/com/android/tv/tuner/tvinput/factory/TunerSessionFactoryImpl.java @@ -0,0 +1,49 @@ +package com.android.tv.tuner.tvinput.factory; + +import android.content.Context; +import android.media.tv.TvInputService.Session; +import com.android.tv.tuner.source.TsDataSourceManager; +import com.android.tv.tuner.tvinput.TunerSession; +import com.android.tv.tuner.tvinput.TunerSessionExoV2; +import com.android.tv.tuner.tvinput.datamanager.ChannelDataManager; +import com.android.tv.common.flags.ConcurrentDvrPlaybackFlags; +import com.android.tv.common.flags.TunerFlags; +import javax.inject.Inject; + +/** Creates a {@link TunerSessionFactory}. */ +public class TunerSessionFactoryImpl implements TunerSessionFactory { + + private final TunerFlags mTunerFlags; + private final ConcurrentDvrPlaybackFlags mConcurrentDvrPlaybackFlags; + private final TsDataSourceManager.Factory mTsDataSourceManagerFactory; + + @Inject + public TunerSessionFactoryImpl( + TunerFlags tunerFlags, + ConcurrentDvrPlaybackFlags concurrentDvrPlaybackFlags, + TsDataSourceManager.Factory tsDataSourceManagerFactory) { + mTunerFlags = tunerFlags; + mConcurrentDvrPlaybackFlags = concurrentDvrPlaybackFlags; + mTsDataSourceManagerFactory = tsDataSourceManagerFactory; + } + + @Override + public Session create( + Context context, + ChannelDataManager channelDataManager, + SessionReleasedCallback releasedCallback) { + return mTunerFlags.useExoplayerV2() + ? new TunerSessionExoV2( + context, + channelDataManager, + releasedCallback, + mConcurrentDvrPlaybackFlags, + mTsDataSourceManagerFactory) + : new TunerSession( + context, + channelDataManager, + releasedCallback, + mConcurrentDvrPlaybackFlags, + mTsDataSourceManagerFactory); + } +} |