SectionParser.java revision 721bd0da688cd552737fbb753a00597f95103b95
11abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/*
21abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Copyright (C) 2015 The Android Open Source Project
31abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
41abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
51abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * you may not use this file except in compliance with the License.
61abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * You may obtain a copy of the License at
71abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
81abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
91abddd9f6225298066094e20a6c29061b6af4590Nick Chalko *
101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Unless required by applicable law or agreed to in writing, software
111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * See the License for the specific language governing permissions and
141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * limitations under the License.
151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkopackage com.android.tv.tuner.ts;
181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
191abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.media.tv.TvContentRating;
201abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.media.tv.TvContract.Programs.Genres;
211abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.text.TextUtils;
221abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.Log;
231abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport android.util.SparseArray;
241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
25721bd0da688cd552737fbb753a00597f95103b95Adrian Roosimport com.android.tv.tuner.data.nano.Channel;
2665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsiData.PatItem;
2765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsiData.PmtItem;
2865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.Ac3AudioDescriptor;
2965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.CaptionServiceDescriptor;
3065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.ContentAdvisoryDescriptor;
3165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.EitItem;
3265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.EttItem;
3365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.ExtendedChannelNameDescriptor;
3465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.GenreDescriptor;
3565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.Iso639LanguageDescriptor;
3665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.MgtItem;
3765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.PsipSection;
3865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.RatingRegion;
3965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.RegionalRating;
4065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.TsDescriptor;
4165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.data.PsipData.VctItem;
42721bd0da688cd552737fbb753a00597f95103b95Adrian Roosimport com.android.tv.tuner.data.nano.Track.AtscAudioTrack;
43721bd0da688cd552737fbb753a00597f95103b95Adrian Roosimport com.android.tv.tuner.data.nano.Track.AtscCaptionTrack;
4465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalkoimport com.android.tv.tuner.util.ByteArrayBuffer;
451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
461abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.ibm.icu.text.UnicodeDecompressor;
471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
481abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.io.UnsupportedEncodingException;
491abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.nio.charset.Charset;
501abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.ArrayList;
511abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.Arrays;
521abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.HashMap;
531abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.HashSet;
541abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport java.util.List;
551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko/**
571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko * Parses ATSC PSIP sections.
581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko */
591abddd9f6225298066094e20a6c29061b6af4590Nick Chalkopublic class SectionParser {
601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String TAG = "SectionParser";
611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final boolean DEBUG = false;
621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_PAT = (byte) 0x00;
641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_PMT = (byte) 0x02;
651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_MGT = (byte) 0xc7;
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_TVCT = (byte) 0xc8;
671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_CVCT = (byte) 0xc9;
681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_EIT = (byte) 0xcb;
691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte TABLE_ID_ETT = (byte) 0xcc;
701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // For details of the structure for the tags of descriptors, see ATSC A/65 Table 6.25.
721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_ISO639LANGUAGE = 0x0a;
731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_CAPTION_SERVICE = 0x86;
741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_CONTENT_ADVISORY = 0x87;
751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_AC3_AUDIO_STREAM = 0x81;
761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME = 0xa0;
771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int DESCRIPTOR_TAG_GENRE = 0xab;
781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte COMPRESSION_TYPE_NO_COMPRESSION = (byte) 0x00;
801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte MODE_SELECTED_UNICODE_RANGE_1 = (byte) 0x00;  // 0x0000 - 0x00ff
811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte MODE_UTF16 = (byte) 0x3f;
821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final byte MODE_SCSU = (byte) 0x3e;
831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final int MAX_SHORT_NAME_BYTES = 14;
841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // See ANSI/CEA-766-C.
861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final int RATING_REGION_US_TV = 1;
871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final int RATING_REGION_KR_TV = 4;
881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // The following values are defined in the live channels app.
901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // See https://developer.android.com/reference/android/media/tv/TvContentRating.html.
911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String RATING_REGION_RATING_SYSTEM_US_TV = "US_TV";
921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String RATING_REGION_RATING_SYSTEM_KR_TV = "KR_TV";
931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String[] RATING_REGION_TABLE_US_TV = {
951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        "US_TV_Y", "US_TV_Y7", "US_TV_G", "US_TV_PG", "US_TV_14", "US_TV_MA"
961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String[] RATING_REGION_TABLE_KR_TV = {
991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        "KR_TV_ALL", "KR_TV_7", "KR_TV_12", "KR_TV_15", "KR_TV_19"
1001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
1011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    /*
1031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * The following CRC table is from the code generated by the following command.
1041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * $ python pycrc.py --model crc-32-mpeg --algorithm table-driven --generate c
1051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     * To see the details of pycrc, visit http://www.tty1.net/pycrc/index_en.html
1061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko     */
1071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public static final int[] CRC_TABLE = {
1081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
1091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
1101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
1111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
1121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
1131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
1141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
1151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
1161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
1171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
1181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
1191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
1201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
1211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
1221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
1231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
1241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
1251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
1261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
1271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
1281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
1291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
1301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
1311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
1321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
1331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
1341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
1351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
1361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
1371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
1381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
1391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
1401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
1411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
1421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
1431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
1441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
1451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
1461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
1471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
1481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
1491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
1501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
1511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
1521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
1531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
1541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
1551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
1561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
1571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
1581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
1591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
1601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
1611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
1621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
1631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
1641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
1651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
1661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
1671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
1681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
1691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
1701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
1711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
1721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
1731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
1741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // A table which maps ATSC genres to TIF genres.
1751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // See ATSC/65 Table 6.20.
1761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String[] CANONICAL_GENRES_TABLE = {
1771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
1851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.EDUCATION, Genres.ENTERTAINMENT, Genres.MOVIES, Genres.NEWS,
1861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.LIFE_STYLE, Genres.SPORTS, null, Genres.MOVIES,
1871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
1881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.FAMILY_KIDS, Genres.DRAMA, null, Genres.ENTERTAINMENT, Genres.SPORTS,
1891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS,
1901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null,
1911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MUSIC, Genres.EDUCATION,
1921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
1931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.COMEDY,
1941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
1951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MUSIC,
1961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null,
1971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MOVIES, Genres.ENTERTAINMENT, Genres.NEWS, Genres.DRAMA,
1981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.EDUCATION, Genres.MOVIES, Genres.SPORTS, Genres.MOVIES,
1991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.LIFE_STYLE, Genres.ARTS, Genres.LIFE_STYLE, Genres.SPORTS,
2011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null,
2021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.GAMING, Genres.LIFE_STYLE, Genres.SPORTS,
2031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.LIFE_STYLE, Genres.EDUCATION, Genres.EDUCATION, Genres.LIFE_STYLE,
2051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.LIFE_STYLE, Genres.MOVIES, Genres.NEWS,
2061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null,
2071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.EDUCATION,
2081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null,
2091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.EDUCATION,
2101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null,
2111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.DRAMA, Genres.MUSIC, Genres.MOVIES,
2121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.ANIMAL_WILDLIFE,
2141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null,
2151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.PREMIER,
2161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null, null,
2171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.ARTS,
2181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null, null, null,
2191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MOVIES, Genres.TECH_SCIENCE, Genres.DRAMA,
2201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SHOPPING, Genres.DRAMA,
2221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MOVIES, Genres.ENTERTAINMENT, Genres.TECH_SCIENCE, Genres.SPORTS,
2241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.TRAVEL, Genres.ENTERTAINMENT, Genres.ARTS, Genres.NEWS,
2251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.ARTS, Genres.SPORTS, Genres.SPORTS, Genres.NEWS,
2271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.SPORTS, Genres.SPORTS, Genres.FAMILY_KIDS,
2281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.FAMILY_KIDS, Genres.MOVIES,
2291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.TECH_SCIENCE, Genres.MUSIC,
2311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.FAMILY_KIDS, Genres.NEWS, Genres.SPORTS,
2331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.NEWS, Genres.SPORTS, Genres.ANIMAL_WILDLIFE,
2341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MUSIC, Genres.NEWS, Genres.SPORTS,
2361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.NEWS, Genres.NEWS, Genres.NEWS, Genres.NEWS,
2381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.MOVIES, Genres.ARTS, Genres.ANIMAL_WILDLIFE,
2391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.MUSIC, Genres.MUSIC, Genres.MOVIES, Genres.EDUCATION,
2401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.DRAMA, Genres.SPORTS, Genres.SPORTS, Genres.SPORTS,
2411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS,
2421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        null,
2431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Genres.SPORTS, Genres.SPORTS,
2441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
2451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
2461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // A table which contains ATSC categorical genre code assignments.
2471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // See ATSC/65 Table 6.20.
2481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static final String[] BROADCAST_GENRES_TABLE = new String[] {
2491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            null, null, null, null,
2571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Education", "Entertainment", "Movie", "News",
2581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Religious", "Sports", "Other", "Action",
2591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Advertisement", "Animated", "Anthology", "Automobile",
2601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Awards", "Baseball", "Basketball", "Bulletin",
2611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Business", "Classical", "College", "Combat",
2621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Comedy", "Commentary", "Concert", "Consumer",
2631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Contemporary", "Crime", "Dance", "Documentary",
2641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Drama", "Elementary", "Erotica", "Exercise",
2651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Fantasy", "Farm", "Fashion", "Fiction",
2661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Food", "Football", "Foreign", "Fund Raiser",
2671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Game/Quiz", "Garden", "Golf", "Government",
2681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Health", "High School", "History", "Hobby",
2691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Hockey", "Home", "Horror", "Information",
2701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Instruction", "International", "Interview", "Language",
2711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Legal", "Live", "Local", "Math",
2721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Medical", "Meeting", "Military", "Miniseries",
2731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Music", "Mystery", "National", "Nature",
2741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Police", "Politics", "Premier", "Prerecorded",
2751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Product", "Professional", "Public", "Racing",
2761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Reading", "Repair", "Repeat", "Review",
2771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Romance", "Science", "Series", "Service",
2781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Shopping", "Soap Opera", "Special", "Suspense",
2791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Talk", "Technical", "Tennis", "Travel",
2801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Variety", "Video", "Weather", "Western",
2811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Art", "Auto Racing", "Aviation", "Biography",
2821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Boating", "Bowling", "Boxing", "Cartoon",
2831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Children", "Classic Film", "Community", "Computers",
2841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Country Music", "Court", "Extreme Sports", "Family",
2851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Financial", "Gymnastics", "Headlines", "Horse Racing",
2861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Hunting/Fishing/Outdoors", "Independent", "Jazz", "Magazine",
2871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Motorcycle Racing", "Music/Film/Books", "News-International", "News-Local",
2881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "News-National", "News-Regional", "Olympics", "Original",
2891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Performing Arts", "Pets/Animals", "Pop", "Rock & Roll",
2901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Sci-Fi", "Self Improvement", "Sitcom", "Skating",
2911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Skiing", "Soccer", "Track/Field", "True",
2921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            "Volleyball", "Wrestling",
2931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    };
2941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
29565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    // Audio language code map from ISO 639-2/B to 639-2/T, in order to show correct audio language.
29665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private static final HashMap<String, String> ISO_LANGUAGE_CODE_MAP;
29765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    static {
29865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP = new HashMap<>();
29965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("alb", "sqi");
30065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("arm", "hye");
30165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("baq", "eus");
30265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("bur", "mya");
30365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("chi", "zho");
30465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("cze", "ces");
30565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("dut", "nld");
30665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("fre", "fra");
30765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("geo", "kat");
30865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("ger", "deu");
30965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("gre", "ell");
31065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("ice", "isl");
31165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("mac", "mkd");
31265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("mao", "mri");
31365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("may", "msa");
31465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("per", "fas");
31565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("rum", "ron");
31665fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("slo", "slk");
31765fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("tib", "bod");
31865fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("wel", "cym");
31965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        ISO_LANGUAGE_CODE_MAP.put("esl", "spa"); // Special entry for channel 9-1 KQED in bay area.
32065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    }
32165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
3221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    // Containers to store the last version numbers of the PSIP sections.
3231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final HashMap<PsipSection, Integer> mSectionVersionMap = new HashMap<>();
3241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private final SparseArray<List<EttItem>> mParsedEttItems = new SparseArray<>();
3251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public interface OutputListener {
3271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onPatParsed(List<PatItem> items);
3281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onPmtParsed(int programNumber, List<PmtItem> items);
3291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onMgtParsed(List<MgtItem> items);
33065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        void onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber);
3311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onEitParsed(int sourceId, List<EitItem> items);
3321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        void onEttParsed(int sourceId, List<EttItem> descriptions);
3331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
33565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko    private final OutputListener mListener;
3361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public SectionParser(OutputListener listener) {
3381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mListener = listener;
3391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    public void parseSections(ByteArrayBuffer data) {
3421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 0;
3431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        while (pos + 3 <= data.length()) {
3441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data.byteAt(pos) & 0xff) == 0xff) {
3451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                // Clear stuffing bytes according to H222.0 section 2.4.4.
3461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                data.setLength(0);
3471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
3481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int sectionLength =
3501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    (((data.byteAt(pos + 1) & 0x0f) << 8) | (data.byteAt(pos + 2) & 0xff)) + 3;
3511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (pos + sectionLength > data.length()) {
3521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
3531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
3551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, "parseSections 0x" + Integer.toHexString(data.byteAt(pos) & 0xff));
3561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            parseSection(Arrays.copyOfRange(data.buffer(), pos, pos + sectionLength));
3581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += sectionLength;
3591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
3611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (int i = 0; i < mParsedEttItems.size(); ++i) {
3621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int sourceId = mParsedEttItems.keyAt(i);
3631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                List<EttItem> descriptions = mParsedEttItems.valueAt(i);
3641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                mListener.onEttParsed(sourceId, descriptions);
3651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
3661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mParsedEttItems.clear();
3681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
3691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private void parseSection(byte[] data) {
3711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (!checkSanity(data)) {
3721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "Bad CRC!");
3731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
3741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        PsipSection section = PsipSection.create(data);
3761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (section == null) {
3771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
3781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // The currentNextIndicator indicates that the section sent is currently applicable.
3811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (!section.getCurrentNextIndicator()) {
3821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
3831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int versionNumber = (data[5] & 0x3e) >> 1;
3851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        Integer oldVersionNumber = mSectionVersionMap.get(section);
3861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
3871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // The versionNumber shall be incremented when a change in the information carried within
3881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // the section occurs.
3891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (oldVersionNumber != null && versionNumber == oldVersionNumber) {
3901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return;
3911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
3921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        boolean result = false;
3931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        switch (data[0]) {
3941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_PAT:
3951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parsePAT(data);
3961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
3971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_PMT:
3981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parsePMT(data);
3991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_MGT:
4011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parseMGT(data);
4021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_TVCT:
4041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_CVCT:
4051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parseVCT(data);
4061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_EIT:
4081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parseEIT(data);
4091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case TABLE_ID_ETT:
4111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                result = parseETT(data);
4121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            default:
4141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
4151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (result) {
4171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mSectionVersionMap.put(section, versionNumber);
4181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
4201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parsePAT(byte[] data) {
4221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
4231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "PAT is discovered.");
4241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 8;
4261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<PatItem> results = new ArrayList<>();
4281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (; pos < data.length - 4; pos = pos + 4) {
4291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (pos > data.length - 4 - 4) {
4301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken PAT.");
4311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
4321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
4331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int programNo = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
4341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int pmtPid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
4351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            results.add(new PatItem(programNo, pmtPid));
4361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
4381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mListener.onPatParsed(results);
4391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
4411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
4421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parsePMT(byte[] data) {
4441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int table_id_ext = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
4451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
4461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "PMT is discovered. programNo = " + table_id_ext);
4471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 11) {
4491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken PMT.");
4501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
4511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pcrPid = (data[8] & 0x1f) << 8 | data[9];
4531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int programInfoLen = (data[10] & 0x0f) << 8 | data[11];
4541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 12;
4551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<TsDescriptor> descriptors = parseDescriptors(data, pos, pos + programInfoLen);
4561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += programInfoLen;
4571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
4581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "PMT descriptors size: " + descriptors.size());
4591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<PmtItem> results = new ArrayList<>();
4611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (; pos < data.length - 4;) {
46265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (pos < 0) {
46365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                Log.e(TAG, "Broken PMT.");
46465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                return false;
46565fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
4661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int streamType = data[pos] & 0xff;
4671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int esPid = (data[pos + 1] & 0x1f) << 8 | (data[pos + 2] & 0xff);
4681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int esInfoLen = (data[pos + 3] & 0xf) << 8 | (data[pos + 4] & 0xff);
4691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length < pos + esInfoLen + 5) {
4701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken PMT.");
4711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
4721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
4731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            descriptors = parseDescriptors(data, pos + 5, pos + 5 + esInfoLen);
4741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
4751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
4761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            PmtItem pmtItem = new PmtItem(streamType, esPid, audioTracks, captionTracks);
4771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
4781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, "PMT " + pmtItem + " descriptors size: " + descriptors.size());
4791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
4801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            results.add(pmtItem);
4811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos = pos + esInfoLen + 5;
4821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        results.add(new PmtItem(PmtItem.ES_PID_PCR, pcrPid, null, null));
4841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
4851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mListener.onPmtParsed(table_id_ext, results);
4861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
4881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
4891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
4901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parseMGT(byte[] data) {
4911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for MGT, see ATSC A/65 Table 6.2.
4921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
4931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "MGT is discovered.");
4941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 10) {
4961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken MGT.");
4971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
4981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
4991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int tablesDefined = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
5001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 11;
5011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<MgtItem> results = new ArrayList<>();
5021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < tablesDefined; ++i) {
5031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 10) {
5041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken MGT.");
5051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int tableType = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
5081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int tableTypePid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
5091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsLength = ((data[pos + 9] & 0x0f) << 8) | (data[pos + 10] & 0xff);
5101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 11 + descriptorsLength;
5111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            results.add(new MgtItem(tableType, tableTypePid));
5121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
513721bd0da688cd552737fbb753a00597f95103b95Adrian Roos        if ((data[pos] & 0xf0) != 0xf0) {
514721bd0da688cd552737fbb753a00597f95103b95Adrian Roos            Log.e(TAG, "Broken MGT.");
515721bd0da688cd552737fbb753a00597f95103b95Adrian Roos            return false;
516721bd0da688cd552737fbb753a00597f95103b95Adrian Roos        }
5171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
5181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mListener.onMgtParsed(results);
5191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
5201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
5211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
5221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
5231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parseVCT(byte[] data) {
5241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for VCT, see ATSC A/65 Table 6.4 and 6.8.
5251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
5261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "VCT is discovered.");
5271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
5281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 9) {
5291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken VCT.");
5301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
5311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
5321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numChannelsInSection = (data[9] & 0xff);
53365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        int sectionNumber = (data[6] & 0xff);
53465fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        int lastSectionNumber = (data[7] & 0xff);
535d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        if (sectionNumber > lastSectionNumber) {
536d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // According to section 6.3.1 of the spec ATSC A/65,
537d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            // last section number is the largest section number.
538d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            Log.w(TAG, "Invalid VCT. Section Number " + sectionNumber + " > Last Section Number "
539d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko                    + lastSectionNumber);
540d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko            return false;
541d41f0075a7d2ea826204e81fcec57d0aa57171a9Nick Chalko        }
5421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 10;
5431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<VctItem> results = new ArrayList<>();
5441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < numChannelsInSection; ++i) {
5451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 31) {
5461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken VCT.");
5471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String shortName = "";
5501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int shortNameSize = getShortNameSize(data, pos);
5511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            try {
5521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                shortName = new String(
5531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        Arrays.copyOfRange(data, pos, pos + shortNameSize), "UTF-16");
5541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            } catch (UnsupportedEncodingException e) {
5551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken VCT.", e);
5561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos + 14] & 0xf0) != 0xf0) {
5591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken VCT.");
5601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int majorNumber = ((data[pos + 14] & 0x0f) << 6) | ((data[pos + 15] & 0xff) >> 2);
5631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int minorNumber = ((data[pos + 15] & 0x03) << 8) | (data[pos + 16] & 0xff);
5641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((majorNumber & 0x3f0) == 0x3f0) {
5651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                // If the six MSBs are 111111, these indicate that there is only one-part channel
5661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                // number. To see details, refer A/65 Section 6.3.2.
5671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                majorNumber = ((majorNumber & 0xf) << 10) + minorNumber;
5681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                minorNumber = 0;
5691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int channelTsid = ((data[pos + 22] & 0xff) << 8) | (data[pos + 23] & 0xff);
5711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int programNumber = ((data[pos + 24] & 0xff) << 8) | (data[pos + 25] & 0xff);
5721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean accessControlled = (data[pos + 26] & 0x20) != 0;
5731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean hidden = (data[pos + 26] & 0x10) != 0;
5741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int serviceType = (data[pos + 27] & 0x3f);
5751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int sourceId = ((data[pos + 28] & 0xff) << 8) | (data[pos + 29] & 0xff);
5761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsPos = pos + 32;
5771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsLength = ((data[pos + 30] & 0x03) << 8) | (data[pos + 31] & 0xff);
5781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 32 + descriptorsLength;
5791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length < pos) {
5801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken VCT.");
5811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
5821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<TsDescriptor> descriptors = parseDescriptors(
5841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    data, descriptorsPos, descriptorsPos + descriptorsLength);
5851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String longName = null;
5861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (TsDescriptor descriptor : descriptors) {
5871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (descriptor instanceof ExtendedChannelNameDescriptor) {
5881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    ExtendedChannelNameDescriptor extendedChannelNameDescriptor =
5891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            (ExtendedChannelNameDescriptor) descriptor;
5901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    longName = extendedChannelNameDescriptor.getLongChannelName();
5911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
5921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
5931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
5941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
5951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, String.format(
5961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d "
5971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                + "channel: %d-%d encrypted: %b hidden: %b, descriptors: %d",
5981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        shortName, longName, serviceType, channelTsid, programNumber, majorNumber,
5991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        minorNumber, accessControlled, hidden, descriptors.size()));
6001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (!accessControlled && !hidden && (serviceType == Channel.SERVICE_TYPE_ATSC_AUDIO ||
6021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    serviceType == Channel.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION ||
6031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    serviceType == Channel.SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE)) {
6041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                // Hide hidden, encrypted, or unsupported ATSC service type channels
6051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                results.add(new VctItem(shortName, longName, serviceType, channelTsid,
6061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        programNumber, majorNumber, minorNumber, sourceId));
6071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
60965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        // Skip the remaining descriptor part which we don't use.
61065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko
6111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
61265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            mListener.onVctParsed(results, sectionNumber, lastSectionNumber);
6131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
6151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
6161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
6171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parseEIT(byte[] data) {
6181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for EIT, see ATSC A/65 Table 6.11.
6191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
6201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "EIT is discovered.");
6211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 9) {
6231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken EIT.");
6241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
6251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int sourceId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
6271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numEventsInSection = (data[9] & 0xff);
6281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
6291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = 10;
6301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<EitItem> results = new ArrayList<>();
6311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < numEventsInSection; ++i) {
6321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 9) {
6331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos] & 0xc0) != 0xc0) {
6371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int eventId = ((data[pos] & 0x3f) << 8) + (data[pos + 1] & 0xff);
6411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            long startTime = ((data[pos + 2] & (long) 0xff) << 24) | ((data[pos + 3] & 0xff) << 16)
6421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    | ((data[pos + 4] & 0xff) << 8) | (data[pos + 5] & 0xff);
6431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int lengthInSecond = ((data[pos + 6] & 0x0f) << 16)
6441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    | ((data[pos + 7] & 0xff) << 8) | (data[pos + 8] & 0xff);
6451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int titleLength = (data[pos + 9] & 0xff);
6461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 10 + titleLength + 1) {
6471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String titleText = "";
6511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (titleLength > 0) {
6521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                titleText = extractText(data, pos + 10);
6531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos + 10 + titleLength] & 0xf0) != 0xf0) {
6551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsLength = ((data[pos + 10 + titleLength] & 0x0f) << 8)
6591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    | (data[pos + 10 + titleLength + 1] & 0xff);
6601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int descriptorsPos = pos + 10 + titleLength + 2;
6611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length < descriptorsPos + descriptorsLength) {
6621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken EIT.");
6631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
6641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<TsDescriptor> descriptors = parseDescriptors(
6661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    data, descriptorsPos, descriptorsPos + descriptorsLength);
6671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
6681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, String.format("EIT descriptors size: %d", descriptors.size()));
6691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
6701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String contentRating = generateContentRating(descriptors);
6711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String broadcastGenre = generateBroadcastGenre(descriptors);
6721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String canonicalGenre = generateCanonicalGenre(descriptors);
6731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
6741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
6751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 10 + titleLength + 2 + descriptorsLength;
6761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            results.add(new EitItem(EitItem.INVALID_PROGRAM_ID, eventId, titleText,
6771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    startTime, lengthInSecond, contentRating, audioTracks, captionTracks,
6781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    broadcastGenre, canonicalGenre, null));
6791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (mListener != null) {
6811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mListener.onEitParsed(sourceId, results);
6821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
6841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
6851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
6861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private boolean parseETT(byte[] data) {
6871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for ETT, see ATSC A/65 Table 6.13.
6881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (DEBUG) {
6891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.d(TAG, "ETT is discovered.");
6901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 12) {
6921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken ETT.");
6931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
6941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
6951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int sourceId = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
6961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int eventId = (((data[11] & 0xff) << 8) | (data[12] & 0xff)) >> 2;
6971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String text = extractText(data, 13);
6981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<EttItem> ettItems = mParsedEttItems.get(sourceId);
6991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (ettItems == null) {
7001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ettItems = new ArrayList<>();
7011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mParsedEttItems.put(sourceId, ettItems);
7021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ettItems.add(new EttItem(eventId, text));
7041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
7051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
7061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
7071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static List<AtscAudioTrack> generateAudioTracks(List<TsDescriptor> descriptors) {
7081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // The list of audio tracks sent is located at both AC3 Audio descriptor and ISO 639
7091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Language descriptor.
7101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscAudioTrack> ac3Tracks = new ArrayList<>();
7111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscAudioTrack> iso639LanguageTracks = new ArrayList<>();
71265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko        for (TsDescriptor descriptor : descriptors) {
7131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof Ac3AudioDescriptor) {
7141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Ac3AudioDescriptor audioDescriptor =
7151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (Ac3AudioDescriptor) descriptor;
7161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                AtscAudioTrack audioTrack = new AtscAudioTrack();
7171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (audioDescriptor.getLanguage() != null) {
7181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    audioTrack.language = audioDescriptor.getLanguage();
7191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
7201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                audioTrack.audioType = AtscAudioTrack.AUDIOTYPE_UNDEFINED;
7211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                audioTrack.channelCount = audioDescriptor.getNumChannels();
7221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                audioTrack.sampleRate = audioDescriptor.getSampleRate();
7231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                ac3Tracks.add(audioTrack);
7241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
7251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
7271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof Iso639LanguageDescriptor) {
7281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Iso639LanguageDescriptor iso639LanguageDescriptor =
7291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (Iso639LanguageDescriptor) descriptor;
7301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                iso639LanguageTracks.addAll(iso639LanguageDescriptor.getAudioTracks());
7311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
7321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
7341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // An AC3 audio stream descriptor only has a audio channel count and a audio sample rate
7351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // while a ISO 639 Language descriptor only has a audio type, which describes a main use
7361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // case of its audio track.
7371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Some channels contain only AC3 audio stream descriptors with valid language values.
7381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Other channels contain both an AC3 audio stream descriptor and a ISO 639 Language
7391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // descriptor per audio track, and those AC3 audio stream descriptors often have a null
7401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // value of language field.
7411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // Combines two descriptors into one in order to gather more audio track specific
7421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // information as much as possible.
7431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscAudioTrack> tracks = new ArrayList<>();
7441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (!ac3Tracks.isEmpty() && !iso639LanguageTracks.isEmpty()
7451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                && ac3Tracks.size() != iso639LanguageTracks.size()) {
7461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // This shouldn't be happen. In here, it handles two cases. The first case is that the
7471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // only one type of descriptors arrives. The second case is that the two types of
7481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // descriptors have the same number of tracks.
7491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "AC3 audio stream descriptors size != ISO 639 Language descriptors size");
7501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return tracks;
7511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int size = Math.max(ac3Tracks.size(), iso639LanguageTracks.size());
7531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < size; ++i) {
7541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            AtscAudioTrack audioTrack = null;
7551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (i < ac3Tracks.size()) {
7561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                audioTrack = ac3Tracks.get(i);
7571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
7581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (i < iso639LanguageTracks.size()) {
7591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (audioTrack == null) {
7601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    audioTrack = iso639LanguageTracks.get(i);
7611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                } else {
7621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    AtscAudioTrack iso639LanguageTrack = iso639LanguageTracks.get(i);
76365fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                    if (audioTrack.language == null || TextUtils.equals(audioTrack.language, "")) {
7641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        audioTrack.language = iso639LanguageTrack.language;
7651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
7661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    audioTrack.audioType = iso639LanguageTrack.audioType;
7671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
7681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
76965fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            String language = ISO_LANGUAGE_CODE_MAP.get(audioTrack.language);
77065fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            if (language != null) {
77165fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko                audioTrack.language = language;
77265fda1eaa94968bb55d5ded10dcb0b3f37fb05f2Nick Chalko            }
7731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            tracks.add(audioTrack);
7741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return tracks;
7761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
7771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
7781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static List<AtscCaptionTrack> generateCaptionTracks(List<TsDescriptor> descriptors) {
7791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscCaptionTrack> services = new ArrayList<>();
7801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
7811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof CaptionServiceDescriptor) {
7821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                CaptionServiceDescriptor captionServiceDescriptor =
7831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (CaptionServiceDescriptor) descriptor;
7841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                services.addAll(captionServiceDescriptor.getCaptionTracks());
7851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
7861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
7871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return services;
7881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
7891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
7901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static String generateContentRating(List<TsDescriptor> descriptors) {
7911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<String> contentRatings = new ArrayList<>();
7921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
7931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof ContentAdvisoryDescriptor) {
7941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                ContentAdvisoryDescriptor contentAdvisoryDescriptor =
7951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (ContentAdvisoryDescriptor) descriptor;
7961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                for (RatingRegion ratingRegion : contentAdvisoryDescriptor.getRatingRegions()) {
7971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    for (RegionalRating index : ratingRegion.getRegionalRatings()) {
7981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        String ratingSystem = null;
7991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        String rating = null;
8001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        switch (ratingRegion.getName()) {
8011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case RATING_REGION_US_TV:
8021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                ratingSystem = RATING_REGION_RATING_SYSTEM_US_TV;
8031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                if (index.getDimension() == 0 && index.getRating() >= 0
8041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                        && index.getRating() < RATING_REGION_TABLE_US_TV.length) {
8051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                    rating = RATING_REGION_TABLE_US_TV[index.getRating()];
8061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                }
8071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                break;
8081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case RATING_REGION_KR_TV:
8091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                ratingSystem = RATING_REGION_RATING_SYSTEM_KR_TV;
8101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                if (index.getDimension() == 0 && index.getRating() >= 0
8111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                        && index.getRating() < RATING_REGION_TABLE_KR_TV.length) {
8121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                    rating = RATING_REGION_TABLE_KR_TV[index.getRating()];
8131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                }
8141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                break;
8151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            default:
8161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                break;
8171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        }
8181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        if (ratingSystem != null && rating != null) {
8191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            contentRatings.add(TvContentRating
8201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                    .createRating("com.android.tv", ratingSystem, rating)
8211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                    .flattenToString());
8221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        }
8231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
8241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
8251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
8271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return TextUtils.join(",", contentRatings);
8281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
8291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static String generateBroadcastGenre(List<TsDescriptor> descriptors) {
8311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
8321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof GenreDescriptor) {
8331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                GenreDescriptor genreDescriptor =
8341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (GenreDescriptor) descriptor;
8351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return TextUtils.join(",", genreDescriptor.getBroadcastGenres());
8361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
8381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return null;
8391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
8401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static String generateCanonicalGenre(List<TsDescriptor> descriptors) {
8421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (TsDescriptor descriptor : descriptors) {
8431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor instanceof GenreDescriptor) {
8441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                GenreDescriptor genreDescriptor =
8451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        (GenreDescriptor) descriptor;
8461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return Genres.encode(genreDescriptor.getCanonicalGenres());
8471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
8491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return null;
8501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
8511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static List<TsDescriptor> parseDescriptors(byte[] data, int offset, int limit) {
8531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for descriptors, see ATSC A/65 Section 6.9.
8541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<TsDescriptor> descriptors = new ArrayList<>();
8551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length < limit) {
8561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return descriptors;
8571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
8581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int pos = offset;
8591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        while (pos + 1 < limit) {
8601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int tag = data[pos] & 0xff;
8611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int length = data[pos + 1] & 0xff;
8621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (length <= 0) {
8631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
8641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit < pos + length + 2) {
8661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
8671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (DEBUG) {
8691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.d(TAG, String.format("Descriptor tag: %02x", tag));
8701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            TsDescriptor descriptor = null;
8721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            switch (tag) {
8731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_CONTENT_ADVISORY:
8741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseContentAdvisory(data, pos, pos + length + 2);
8751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_CAPTION_SERVICE:
8781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseCaptionService(data, pos, pos + length + 2);
8791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME:
8821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseLongChannelName(data, pos, pos + length + 2);
8831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_GENRE:
8861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseGenre(data, pos, pos + length + 2);
8871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_AC3_AUDIO_STREAM:
8901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseAc3AudioStream(data, pos, pos + length + 2);
8911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                case DESCRIPTOR_TAG_ISO639LANGUAGE:
8941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    descriptor = parseIso639Language(data, pos, pos + length + 2);
8951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    break;
8961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
8971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                default:
8981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
8991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (descriptor != null) {
9001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (DEBUG) {
9011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.d(TAG, "Descriptor parsed: " + descriptor);
9021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
9031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                descriptors.add(descriptor);
9041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
9051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += length + 2;
9061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return descriptors;
9081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
9091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
9101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static Iso639LanguageDescriptor parseIso639Language(byte[] data, int pos, int limit) {
9111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For the details of the structure of ISO 639 language descriptor,
9121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // see ISO13818-1 second edition Section 2.6.18.
9131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
9141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscAudioTrack> audioTracks = new ArrayList<>();
9151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        while (pos + 4 <= limit) {
9161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit <= pos + 3) {
9171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken Iso639Language.");
9181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
9191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
9201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String language = new String(data, pos, 3);
9211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int audioType = data[pos + 3] & 0xff;
9221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            AtscAudioTrack audioTrack = new AtscAudioTrack();
9231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            audioTrack.language = language;
9241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            audioTrack.audioType = audioType;
9251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            audioTracks.add(audioTrack);
9261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 4;
9271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new Iso639LanguageDescriptor(audioTracks);
9291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
9301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
9311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static CaptionServiceDescriptor parseCaptionService(byte[] data, int pos, int limit) {
9321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For the details of the structure of caption service descriptor,
9331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // see ATSC A/65 Section 6.9.2.
9341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 2) {
9351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken CaptionServiceDescriptor.");
9361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
9371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<AtscCaptionTrack> services = new ArrayList<>();
9391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
9401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numberServices = data[pos] & 0x1f;
9411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
9421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit < pos + numberServices * 6) {
9431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken CaptionServiceDescriptor.");
9441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
9451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < numberServices; ++i) {
9471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String language = new String(Arrays.copyOfRange(data, pos, pos + 3));
9481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 3;
9491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean ccType = (data[pos] & 0x80) != 0;
9501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (!ccType) {
9511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                continue;
9521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
9531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int captionServiceNumber = data[pos] & 0x3f;
9541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
9551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean easyReader = (data[pos] & 0x80) != 0;
9561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean wideAspectRatio = (data[pos] & 0x40) != 0;
9571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            byte[] reserved = new byte[2];
9581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            reserved[0] = (byte) (data[pos] << 2);
9591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            reserved[0] |= (byte) ((data[pos + 1] & 0xc0) >>> 6);
9601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            reserved[1] = (byte) ((data[pos + 1] & 0x3f) << 2);
9611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 2;
9621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            AtscCaptionTrack captionTrack = new AtscCaptionTrack();
9631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            captionTrack.language = language;
9641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            captionTrack.serviceNumber = captionServiceNumber;
9651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            captionTrack.easyReader = easyReader;
9661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            captionTrack.wideAspectRatio = wideAspectRatio;
9671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            services.add(captionTrack);
9681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new CaptionServiceDescriptor(services);
9701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
9711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
9721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static ContentAdvisoryDescriptor parseContentAdvisory(byte[] data, int pos, int limit) {
9731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the structure for content advisory descriptor, see A/65 Table 6.27.
9741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 2) {
9751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken ContentAdvisory");
9761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
9771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
9781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int count = data[pos + 2] & 0x3f;
9791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 3;
9801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        List<RatingRegion> ratingRegions = new ArrayList<>();
9811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < count; ++i) {
9821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit <= pos + 1) {
9831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken ContentAdvisory");
9841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
9851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
9861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            List<RegionalRating> indices = new ArrayList<>();
9871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int ratingRegion = data[pos] & 0xff;
9881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int dimensionCount = data[pos + 1] & 0xff;
9891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 2;
9901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (int j = 0; j < dimensionCount; ++j) {
9911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (limit <= pos + 1) {
9921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.e(TAG, "Broken ContentAdvisory");
9931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    return null;
9941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
9951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int dimensionIndex = data[pos] & 0xff;
9961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int ratingValue = data[pos + 1] & 0x0f;
9971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                pos += 2;
9981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                indices.add(new RegionalRating(dimensionIndex, ratingValue));
9991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit <= pos) {
10011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken ContentAdvisory");
10021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
10031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int ratingDescriptionLength = data[pos] & 0xff;
10051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
10061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit < pos + ratingDescriptionLength) {
10071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken ContentAdvisory");
10081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
10091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            String ratingDescription = extractText(data, pos);
10111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += ratingDescriptionLength;
10121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ratingRegions.add(new RatingRegion(ratingRegion, ratingDescription, indices));
10131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new ContentAdvisoryDescriptor(ratingRegions);
10151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
10161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
10171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static ExtendedChannelNameDescriptor parseLongChannelName(byte[] data, int pos,
10181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int limit) {
10191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 2) {
10201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken ExtendedChannelName.");
10211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
10241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String text = extractText(data, pos);
10251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (text == null) {
10261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken ExtendedChannelName.");
10271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new ExtendedChannelNameDescriptor(text);
10301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
10311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
10321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static GenreDescriptor parseGenre(byte[] data, int pos, int limit) {
10331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
10341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int attributeCount = data[pos] & 0x1f;
10351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + attributeCount) {
10361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken Genre.");
10371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        HashSet<String> broadcastGenreSet = new HashSet<>();
10401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        HashSet<String> canonicalGenreSet = new HashSet<>();
10411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < attributeCount; ++i) {
10421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
10431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int genreCode = data[pos] & 0xff;
10441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (genreCode < BROADCAST_GENRES_TABLE.length) {
10451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                String broadcastGenre = BROADCAST_GENRES_TABLE[genreCode];
10461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (broadcastGenre != null && !broadcastGenreSet.contains(broadcastGenre)) {
10471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    broadcastGenreSet.add(broadcastGenre);
10481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
10491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (genreCode < CANONICAL_GENRES_TABLE.length) {
10511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                String canonicalGenre = CANONICAL_GENRES_TABLE[genreCode];
10521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (canonicalGenre != null && !canonicalGenreSet.contains(canonicalGenre)) {
10531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    canonicalGenreSet.add(canonicalGenre);
10541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
10551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new GenreDescriptor(broadcastGenreSet.toArray(new String[broadcastGenreSet.size()]),
10581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                canonicalGenreSet.toArray(new String[canonicalGenreSet.size()]));
10591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
10601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
10611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static TsDescriptor parseAc3AudioStream(byte[] data, int pos, int limit) {
10621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // For details of the AC3 audio stream descriptor, see A/52 Table A4.1.
10631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 5) {
10641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken AC3 audio stream descriptor.");
10651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos += 2;
10681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte sampleRateCode = (byte) ((data[pos] & 0xe0) >> 5);
10691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte bsid = (byte) (data[pos] & 0x1f);
10701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
10711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte bitRateCode = (byte) ((data[pos] & 0xfc) >> 2);
10721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte surroundMode = (byte) (data[pos] & 0x03);
10731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
10741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte bsmod = (byte) ((data[pos] & 0xe0) >> 5);
10751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numChannels = (data[pos] & 0x1e) >> 1;
10761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        boolean fullSvc = (data[pos] & 0x01) != 0;
10771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
10781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte langCod = data[pos];
10791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte langCod2 = 0;
10801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (numChannels == 0) {
10811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit <= pos) {
10821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor.");
10831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
10841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
10851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
10861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            langCod2 = data[pos];
10871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 1) {
10891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.e(TAG, "Broken AC3 audio stream descriptor.");
10901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
10911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
10921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte mainId = 0;
10931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte priority = 0;
10941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        byte asvcflags = 0;
10951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
10961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (bsmod < 2) {
10971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            mainId = (byte) ((data[pos] & 0xe0) >> 5);
10981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            priority = (byte) ((data[pos] & 0x18) >> 3);
10991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos] & 0x07) != 0x07) {
11001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor reserved failed");
11011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
11021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        } else {
11041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            asvcflags = data[pos];
11051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
11071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        // See A/52B Table A3.6 num_channels.
11081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numEncodedChannels;
11091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        switch (numChannels) {
11101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 1:
11111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 8:
11121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 1;
11131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 2:
11151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 9:
11161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 2;
11171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 3:
11191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 4:
11201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 10:
11211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 3;
11221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 5:
11241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 6:
11251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 11:
11261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 4;
11271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 7:
11291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 12:
11301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 5;
11311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            case 13:
11331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 6;
11341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            default:
11361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels = 0;
11371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                break;
11381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
11401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (limit <= pos + 1) {
11411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            Log.w(TAG, "Missing text and language fields on AC3 audio stream descriptor.");
11421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
11431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags,
11441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    null, null, null);
11451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
11471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int textLen = (data[pos] & 0xfe) >> 1;
11481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        boolean textCode = (data[pos] & 0x01) != 0;
11491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ++pos;
11501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String text = "";
11511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (textLen > 0) {
11521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (limit < pos + textLen) {
11531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor");
11541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
11551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (textCode) {
11571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                text = new String(data, pos, textLen);
11581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            } else {
11591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                text = new String(data, pos, textLen, Charset.forName("UTF-16"));
11601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += textLen;
11621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String language = null;
11641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        String language2 = null;
11651abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (pos < limit) {
11661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            // Many AC3 audio stream descriptors skip the language fields.
11671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean languageFlag1 = (data[pos] & 0x80) != 0;
11681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            boolean languageFlag2 = (data[pos] & 0x40) != 0;
11691abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if ((data[pos] & 0x3f) != 0x3f) {
11701abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor");
11711abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
11721abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (pos + (languageFlag1 ? 3 : 0) + (languageFlag2 ? 3 : 0) > limit) {
11741abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken AC3 audio stream descriptor");
11751abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
11761abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11771abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            ++pos;
11781abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (languageFlag1) {
11791abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                language = new String(data, pos, 3);
11801abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                pos += 3;
11811abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11821abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (languageFlag2) {
11831abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                language2 = new String(data, pos, 3);
11841abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11851abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11861abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
11871abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return new Ac3AudioDescriptor(sampleRateCode, bsid, bitRateCode, surroundMode, bsmod,
11881abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                numEncodedChannels, fullSvc, langCod, langCod2, mainId, priority, asvcflags, text,
11891abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                language, language2);
11901abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
11911abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
11921abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static int getShortNameSize(byte[] data, int offset) {
11931abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < MAX_SHORT_NAME_BYTES; i += 2) {
11941abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data[offset + i] == 0 && data[offset + i + 1] == 0) {
11951abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return i;
11961abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
11971abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
11981abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return MAX_SHORT_NAME_BYTES;
11991abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
12001abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
12011abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static String extractText(byte[] data, int pos) {
12021abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length < pos)  {
12031abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return null;
12041abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
12051abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        int numStrings = data[pos] & 0xff;
12061abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        pos++;
12071abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        for (int i = 0; i < numStrings; ++i) {
12081abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if (data.length <= pos + 3) {
12091abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                Log.e(TAG, "Broken text.");
12101abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return null;
12111abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
12121abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int numSegments = data[pos + 3] & 0xff;
12131abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            pos += 4;
12141abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for (int j = 0; j < numSegments; ++j) {
12151abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (data.length <= pos + 2) {
12161abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.e(TAG, "Broken text.");
12171abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    return null;
12181abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
12191abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int compressionType = data[pos] & 0xff;
12201abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int mode = data[pos + 1] & 0xff;
12211abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int numBytes = data[pos + 2] & 0xff;
12221abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (data.length < pos + 3 + numBytes) {
12231abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    Log.e(TAG, "Broken text.");
12241abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    return null;
12251abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
12261abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                byte[] bytes = Arrays.copyOfRange(data, pos + 3, pos + 3 + numBytes);
12271abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                if (compressionType == COMPRESSION_TYPE_NO_COMPRESSION) {
12281abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    try {
12291abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        switch (mode) {
12301abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case MODE_SELECTED_UNICODE_RANGE_1:
12311abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                return new String(bytes, "ISO-8859-1");
12321abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case MODE_SCSU:
12331abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                return UnicodeDecompressor.decompress(bytes);
12341abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                            case MODE_UTF16:
12351abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                                return new String(bytes, "UTF-16");
12361abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        }
12371abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    } catch (UnsupportedEncodingException e) {
12381abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                        Log.e(TAG, "Unsupported text format.", e);
12391abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                    }
12401abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                }
12411abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                pos += 3 + numBytes;
12421abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
12431abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
12441abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return null;
12451abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
12461abddd9f6225298066094e20a6c29061b6af4590Nick Chalko
12471abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    private static boolean checkSanity(byte[] data) {
12481abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (data.length <= 1) {
12491abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            return false;
12501abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
12511abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        boolean hasCRC = (data[1] & 0x80) != 0; // section_syntax_indicator
12521abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        if (hasCRC) {
12531abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            int crc = 0xffffffff;
12541abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            for(byte b : data) {
12551abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                int index = ((crc >> 24) ^ (b & 0xff)) & 0xff;
12561abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                crc = CRC_TABLE[index] ^ (crc << 8);
12571abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
12581abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            if(crc != 0){
12591abddd9f6225298066094e20a6c29061b6af4590Nick Chalko                return false;
12601abddd9f6225298066094e20a6c29061b6af4590Nick Chalko            }
12611abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        }
12621abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        return true;
12631abddd9f6225298066094e20a6c29061b6af4590Nick Chalko    }
12641abddd9f6225298066094e20a6c29061b6af4590Nick Chalko}
1265