1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tv.tuner.data;
18 
19 import android.media.tv.TvContentRating;
20 import android.media.tv.TvContract.Programs.Genres;
21 import android.support.annotation.Nullable;
22 import android.support.annotation.VisibleForTesting;
23 import android.text.TextUtils;
24 import android.util.ArraySet;
25 import android.util.Log;
26 import android.util.SparseArray;
27 import com.android.tv.common.feature.Model;
28 import com.android.tv.tuner.data.Channel.AtscServiceType;
29 import com.android.tv.tuner.data.PsiData.PatItem;
30 import com.android.tv.tuner.data.PsiData.PmtItem;
31 import com.android.tv.tuner.data.PsipData.Ac3AudioDescriptor;
32 import com.android.tv.tuner.data.PsipData.CaptionServiceDescriptor;
33 import com.android.tv.tuner.data.PsipData.ContentAdvisoryDescriptor;
34 import com.android.tv.tuner.data.PsipData.EitItem;
35 import com.android.tv.tuner.data.PsipData.EttItem;
36 import com.android.tv.tuner.data.PsipData.ExtendedChannelNameDescriptor;
37 import com.android.tv.tuner.data.PsipData.GenreDescriptor;
38 import com.android.tv.tuner.data.PsipData.Iso639LanguageDescriptor;
39 import com.android.tv.tuner.data.PsipData.MgtItem;
40 import com.android.tv.tuner.data.PsipData.ParentalRatingDescriptor;
41 import com.android.tv.tuner.data.PsipData.PsipSection;
42 import com.android.tv.tuner.data.PsipData.RatingRegion;
43 import com.android.tv.tuner.data.PsipData.RegionalRating;
44 import com.android.tv.tuner.data.PsipData.SdtItem;
45 import com.android.tv.tuner.data.PsipData.ServiceDescriptor;
46 import com.android.tv.tuner.data.PsipData.ShortEventDescriptor;
47 import com.android.tv.tuner.data.PsipData.TsDescriptor;
48 import com.android.tv.tuner.data.PsipData.VctItem;
49 import com.android.tv.tuner.data.Track.AtscAudioTrack;
50 import com.android.tv.tuner.data.Track.AtscCaptionTrack;
51 import com.android.tv.tuner.util.ByteArrayBuffer;
52 import com.android.tv.tuner.util.ConvertUtils;
53 import java.io.UnsupportedEncodingException;
54 import java.nio.charset.Charset;
55 import java.nio.charset.StandardCharsets;
56 import java.util.ArrayList;
57 import java.util.Arrays;
58 import java.util.Calendar;
59 import java.util.HashMap;
60 import java.util.HashSet;
61 import java.util.List;
62 import java.util.Set;
63 
64 /** Parses ATSC PSIP sections. */
65 public class SectionParser {
66     private static final String TAG = "SectionParser";
67     private static final boolean DEBUG = false;
68 
69     private static final byte TABLE_ID_PAT = (byte) 0x00;
70     private static final byte TABLE_ID_PMT = (byte) 0x02;
71     private static final byte TABLE_ID_MGT = (byte) 0xc7;
72     private static final byte TABLE_ID_TVCT = (byte) 0xc8;
73     private static final byte TABLE_ID_CVCT = (byte) 0xc9;
74     private static final byte TABLE_ID_EIT = (byte) 0xcb;
75     private static final byte TABLE_ID_ETT = (byte) 0xcc;
76 
77     // Table id for DVB
78     private static final byte TABLE_ID_SDT = (byte) 0x42;
79     private static final byte TABLE_ID_DVB_ACTUAL_P_F_EIT = (byte) 0x4e;
80     private static final byte TABLE_ID_DVB_OTHER_P_F_EIT = (byte) 0x4f;
81     private static final byte TABLE_ID_DVB_ACTUAL_SCHEDULE_EIT = (byte) 0x50;
82     private static final byte TABLE_ID_DVB_OTHER_SCHEDULE_EIT = (byte) 0x60;
83 
84     // For details of the structure for the tags of descriptors, see ATSC A/65 Table 6.25.
85     public static final int DESCRIPTOR_TAG_ISO639LANGUAGE = 0x0a;
86     public static final int DESCRIPTOR_TAG_CAPTION_SERVICE = 0x86;
87     public static final int DESCRIPTOR_TAG_CONTENT_ADVISORY = 0x87;
88     public static final int DESCRIPTOR_TAG_AC3_AUDIO_STREAM = 0x81;
89     public static final int DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME = 0xa0;
90     public static final int DESCRIPTOR_TAG_GENRE = 0xab;
91 
92     // For details of the structure for the tags of DVB descriptors, see DVB Document A038 Table 12.
93     public static final int DVB_DESCRIPTOR_TAG_SERVICE = 0x48;
94     public static final int DVB_DESCRIPTOR_TAG_SHORT_EVENT = 0X4d;
95     public static final int DVB_DESCRIPTOR_TAG_CONTENT = 0x54;
96     public static final int DVB_DESCRIPTOR_TAG_PARENTAL_RATING = 0x55;
97 
98     private static final byte COMPRESSION_TYPE_NO_COMPRESSION = (byte) 0x00;
99     private static final byte MODE_SELECTED_UNICODE_RANGE_1 = (byte) 0x00; // 0x0000 - 0x00ff
100     private static final byte MODE_UTF16 = (byte) 0x3f;
101     private static final byte MODE_SCSU = (byte) 0x3e;
102     private static final int MAX_SHORT_NAME_BYTES = 14;
103 
104     // See ANSI/CEA-766-C.
105     private static final int RATING_REGION_US_TV = 1;
106     private static final int RATING_REGION_KR_TV = 4;
107 
108     // The following values are defined in the TV app.
109     // See https://developer.android.com/reference/android/media/tv/TvContentRating.html.
110     private static final String RATING_DOMAIN = "com.android.tv";
111     private static final String RATING_REGION_RATING_SYSTEM_US_TV = "US_TV";
112     private static final String RATING_REGION_RATING_SYSTEM_US_MV = "US_MV";
113     private static final String RATING_REGION_RATING_SYSTEM_KR_TV = "KR_TV";
114 
115     private static final String[] RATING_REGION_TABLE_US_TV = {
116         "US_TV_Y", "US_TV_Y7", "US_TV_G", "US_TV_PG", "US_TV_14", "US_TV_MA"
117     };
118 
119     private static final String[] RATING_REGION_TABLE_US_MV = {
120         "US_MV_G", "US_MV_PG", "US_MV_PG13", "US_MV_R", "US_MV_NC17"
121     };
122 
123     private static final String[] RATING_REGION_TABLE_KR_TV = {
124         "KR_TV_ALL", "KR_TV_7", "KR_TV_12", "KR_TV_15", "KR_TV_19"
125     };
126 
127     private static final String[] RATING_REGION_TABLE_US_TV_SUBRATING = {
128         "US_TV_D", "US_TV_L", "US_TV_S", "US_TV_V", "US_TV_FV"
129     };
130 
131     // According to ANSI-CEA-766-D
132     private static final int VALUE_US_TV_Y = 1;
133     private static final int VALUE_US_TV_Y7 = 2;
134     private static final int VALUE_US_TV_NONE = 1;
135     private static final int VALUE_US_TV_G = 2;
136     private static final int VALUE_US_TV_PG = 3;
137     private static final int VALUE_US_TV_14 = 4;
138     private static final int VALUE_US_TV_MA = 5;
139 
140     private static final int DIMENSION_US_TV_RATING = 0;
141     private static final int DIMENSION_US_TV_D = 1;
142     private static final int DIMENSION_US_TV_L = 2;
143     private static final int DIMENSION_US_TV_S = 3;
144     private static final int DIMENSION_US_TV_V = 4;
145     private static final int DIMENSION_US_TV_Y = 5;
146     private static final int DIMENSION_US_TV_FV = 6;
147     private static final int DIMENSION_US_MV_RATING = 7;
148 
149     private static final int VALUE_US_MV_G = 2;
150     private static final int VALUE_US_MV_PG = 3;
151     private static final int VALUE_US_MV_PG13 = 4;
152     private static final int VALUE_US_MV_R = 5;
153     private static final int VALUE_US_MV_NC17 = 6;
154     private static final int VALUE_US_MV_X = 7;
155 
156     private static final String STRING_US_TV_Y = "US_TV_Y";
157     private static final String STRING_US_TV_Y7 = "US_TV_Y7";
158     private static final String STRING_US_TV_FV = "US_TV_FV";
159 
160     /*
161      * The following CRC table is from the code generated by the following command.
162      * $ python pycrc.py --model crc-32-mpeg --algorithm table-driven --generate c
163      * To see the details of pycrc, visit http://www.tty1.net/pycrc/index_en.html
164      */
165     public static final int[] CRC_TABLE = {
166         0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
167         0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
168         0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
169         0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
170         0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
171         0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
172         0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
173         0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
174         0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
175         0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
176         0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
177         0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
178         0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
179         0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
180         0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
181         0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
182         0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
183         0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
184         0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
185         0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
186         0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
187         0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
188         0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
189         0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
190         0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
191         0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
192         0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
193         0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
194         0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
195         0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
196         0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
197         0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
198         0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
199         0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
200         0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
201         0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
202         0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
203         0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
204         0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
205         0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
206         0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
207         0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
208         0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
209         0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
210         0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
211         0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
212         0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
213         0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
214         0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
215         0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
216         0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
217         0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
218         0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
219         0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
220         0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
221         0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
222         0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
223         0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
224         0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
225         0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
226         0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
227         0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
228         0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
229         0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
230     };
231 
232     // A table which maps ATSC genres to TIF genres.
233     // See ATSC/65 Table 6.20.
234     private static final String[] CANONICAL_GENRES_TABLE = {
235         null,
236         null,
237         null,
238         null,
239         null,
240         null,
241         null,
242         null,
243         null,
244         null,
245         null,
246         null,
247         null,
248         null,
249         null,
250         null,
251         null,
252         null,
253         null,
254         null,
255         null,
256         null,
257         null,
258         null,
259         null,
260         null,
261         null,
262         null,
263         null,
264         null,
265         null,
266         null,
267         Genres.EDUCATION,
268         Genres.ENTERTAINMENT,
269         Genres.MOVIES,
270         Genres.NEWS,
271         Genres.LIFE_STYLE,
272         Genres.SPORTS,
273         null,
274         Genres.MOVIES,
275         null,
276         Genres.FAMILY_KIDS,
277         Genres.DRAMA,
278         null,
279         Genres.ENTERTAINMENT,
280         Genres.SPORTS,
281         Genres.SPORTS,
282         null,
283         null,
284         Genres.MUSIC,
285         Genres.EDUCATION,
286         null,
287         Genres.COMEDY,
288         null,
289         Genres.MUSIC,
290         null,
291         null,
292         Genres.MOVIES,
293         Genres.ENTERTAINMENT,
294         Genres.NEWS,
295         Genres.DRAMA,
296         Genres.EDUCATION,
297         Genres.MOVIES,
298         Genres.SPORTS,
299         Genres.MOVIES,
300         null,
301         Genres.LIFE_STYLE,
302         Genres.ARTS,
303         Genres.LIFE_STYLE,
304         Genres.SPORTS,
305         null,
306         null,
307         Genres.GAMING,
308         Genres.LIFE_STYLE,
309         Genres.SPORTS,
310         null,
311         Genres.LIFE_STYLE,
312         Genres.EDUCATION,
313         Genres.EDUCATION,
314         Genres.LIFE_STYLE,
315         Genres.SPORTS,
316         Genres.LIFE_STYLE,
317         Genres.MOVIES,
318         Genres.NEWS,
319         null,
320         null,
321         null,
322         Genres.EDUCATION,
323         null,
324         null,
325         null,
326         Genres.EDUCATION,
327         null,
328         null,
329         null,
330         Genres.DRAMA,
331         Genres.MUSIC,
332         Genres.MOVIES,
333         null,
334         Genres.ANIMAL_WILDLIFE,
335         null,
336         null,
337         Genres.PREMIER,
338         null,
339         null,
340         null,
341         null,
342         Genres.SPORTS,
343         Genres.ARTS,
344         null,
345         null,
346         null,
347         Genres.MOVIES,
348         Genres.TECH_SCIENCE,
349         Genres.DRAMA,
350         null,
351         Genres.SHOPPING,
352         Genres.DRAMA,
353         null,
354         Genres.MOVIES,
355         Genres.ENTERTAINMENT,
356         Genres.TECH_SCIENCE,
357         Genres.SPORTS,
358         Genres.TRAVEL,
359         Genres.ENTERTAINMENT,
360         Genres.ARTS,
361         Genres.NEWS,
362         null,
363         Genres.ARTS,
364         Genres.SPORTS,
365         Genres.SPORTS,
366         Genres.NEWS,
367         Genres.SPORTS,
368         Genres.SPORTS,
369         Genres.SPORTS,
370         Genres.FAMILY_KIDS,
371         Genres.FAMILY_KIDS,
372         Genres.MOVIES,
373         null,
374         Genres.TECH_SCIENCE,
375         Genres.MUSIC,
376         null,
377         Genres.SPORTS,
378         Genres.FAMILY_KIDS,
379         Genres.NEWS,
380         Genres.SPORTS,
381         Genres.NEWS,
382         Genres.SPORTS,
383         Genres.ANIMAL_WILDLIFE,
384         null,
385         Genres.MUSIC,
386         Genres.NEWS,
387         Genres.SPORTS,
388         null,
389         Genres.NEWS,
390         Genres.NEWS,
391         Genres.NEWS,
392         Genres.NEWS,
393         Genres.SPORTS,
394         Genres.MOVIES,
395         Genres.ARTS,
396         Genres.ANIMAL_WILDLIFE,
397         Genres.MUSIC,
398         Genres.MUSIC,
399         Genres.MOVIES,
400         Genres.EDUCATION,
401         Genres.DRAMA,
402         Genres.SPORTS,
403         Genres.SPORTS,
404         Genres.SPORTS,
405         Genres.SPORTS,
406         null,
407         Genres.SPORTS,
408         Genres.SPORTS,
409     };
410 
411     // A table which contains ATSC categorical genre code assignments.
412     // See ATSC/65 Table 6.20.
413     private static final String[] BROADCAST_GENRES_TABLE =
414             new String[] {
415                 null,
416                 null,
417                 null,
418                 null,
419                 null,
420                 null,
421                 null,
422                 null,
423                 null,
424                 null,
425                 null,
426                 null,
427                 null,
428                 null,
429                 null,
430                 null,
431                 null,
432                 null,
433                 null,
434                 null,
435                 null,
436                 null,
437                 null,
438                 null,
439                 null,
440                 null,
441                 null,
442                 null,
443                 null,
444                 null,
445                 null,
446                 null,
447                 "Education",
448                 "Entertainment",
449                 "Movie",
450                 "News",
451                 "Religious",
452                 "Sports",
453                 "Other",
454                 "Action",
455                 "Advertisement",
456                 "Animated",
457                 "Anthology",
458                 "Automobile",
459                 "Awards",
460                 "Baseball",
461                 "Basketball",
462                 "Bulletin",
463                 "Business",
464                 "Classical",
465                 "College",
466                 "Combat",
467                 "Comedy",
468                 "Commentary",
469                 "Concert",
470                 "Consumer",
471                 "Contemporary",
472                 "Crime",
473                 "Dance",
474                 "Documentary",
475                 "Drama",
476                 "Elementary",
477                 "Erotica",
478                 "Exercise",
479                 "Fantasy",
480                 "Farm",
481                 "Fashion",
482                 "Fiction",
483                 "Food",
484                 "Football",
485                 "Foreign",
486                 "Fund Raiser",
487                 "Game/Quiz",
488                 "Garden",
489                 "Golf",
490                 "Government",
491                 "Health",
492                 "High School",
493                 "History",
494                 "Hobby",
495                 "Hockey",
496                 "Home",
497                 "Horror",
498                 "Information",
499                 "Instruction",
500                 "International",
501                 "Interview",
502                 "Language",
503                 "Legal",
504                 "Live",
505                 "Local",
506                 "Math",
507                 "Medical",
508                 "Meeting",
509                 "Military",
510                 "Miniseries",
511                 "Music",
512                 "Mystery",
513                 "National",
514                 "Nature",
515                 "Police",
516                 "Politics",
517                 "Premier",
518                 "Prerecorded",
519                 "Product",
520                 "Professional",
521                 "Public",
522                 "Racing",
523                 "Reading",
524                 "Repair",
525                 "Repeat",
526                 "Review",
527                 "Romance",
528                 "Science",
529                 "Series",
530                 "Service",
531                 "Shopping",
532                 "Soap Opera",
533                 "Special",
534                 "Suspense",
535                 "Talk",
536                 "Technical",
537                 "Tennis",
538                 "Travel",
539                 "Variety",
540                 "Video",
541                 "Weather",
542                 "Western",
543                 "Art",
544                 "Auto Racing",
545                 "Aviation",
546                 "Biography",
547                 "Boating",
548                 "Bowling",
549                 "Boxing",
550                 "Cartoon",
551                 "Children",
552                 "Classic Film",
553                 "Community",
554                 "Computers",
555                 "Country Music",
556                 "Court",
557                 "Extreme Sports",
558                 "Family",
559                 "Financial",
560                 "Gymnastics",
561                 "Headlines",
562                 "Horse Racing",
563                 "Hunting/Fishing/Outdoors",
564                 "Independent",
565                 "Jazz",
566                 "Magazine",
567                 "Motorcycle Racing",
568                 "Music/Film/Books",
569                 "News-International",
570                 "News-Local",
571                 "News-National",
572                 "News-Regional",
573                 "Olympics",
574                 "Original",
575                 "Performing Arts",
576                 "Pets/Animals",
577                 "Pop",
578                 "Rock & Roll",
579                 "Sci-Fi",
580                 "Self Improvement",
581                 "Sitcom",
582                 "Skating",
583                 "Skiing",
584                 "Soccer",
585                 "Track/Field",
586                 "True",
587                 "Volleyball",
588                 "Wrestling",
589             };
590 
591     // Audio language code map from ISO 639-2/B to 639-2/T, in order to show correct audio language.
592     private static final HashMap<String, String> ISO_LANGUAGE_CODE_MAP;
593 
594     static {
595         ISO_LANGUAGE_CODE_MAP = new HashMap<>();
596         ISO_LANGUAGE_CODE_MAP.put("alb", "sqi");
597         ISO_LANGUAGE_CODE_MAP.put("arm", "hye");
598         ISO_LANGUAGE_CODE_MAP.put("baq", "eus");
599         ISO_LANGUAGE_CODE_MAP.put("bur", "mya");
600         ISO_LANGUAGE_CODE_MAP.put("chi", "zho");
601         ISO_LANGUAGE_CODE_MAP.put("cze", "ces");
602         ISO_LANGUAGE_CODE_MAP.put("dut", "nld");
603         ISO_LANGUAGE_CODE_MAP.put("fre", "fra");
604         ISO_LANGUAGE_CODE_MAP.put("geo", "kat");
605         ISO_LANGUAGE_CODE_MAP.put("ger", "deu");
606         ISO_LANGUAGE_CODE_MAP.put("gre", "ell");
607         ISO_LANGUAGE_CODE_MAP.put("ice", "isl");
608         ISO_LANGUAGE_CODE_MAP.put("mac", "mkd");
609         ISO_LANGUAGE_CODE_MAP.put("mao", "mri");
610         ISO_LANGUAGE_CODE_MAP.put("may", "msa");
611         ISO_LANGUAGE_CODE_MAP.put("per", "fas");
612         ISO_LANGUAGE_CODE_MAP.put("rum", "ron");
613         ISO_LANGUAGE_CODE_MAP.put("slo", "slk");
614         ISO_LANGUAGE_CODE_MAP.put("tib", "bod");
615         ISO_LANGUAGE_CODE_MAP.put("wel", "cym");
616         ISO_LANGUAGE_CODE_MAP.put("esl", "spa"); // Special entry for channel 9-1 KQED in bay area.
617     }
618 
619     @Nullable
620     private static final Charset SCSU_CHARSET =
621             Charset.isSupported("SCSU") ? Charset.forName("SCSU") : null;
622 
623     // Containers to store the last version numbers of the PSIP sections.
624     private final HashMap<PsipSection, Integer> mSectionVersionMap = new HashMap<>();
625     private final SparseArray<List<EttItem>> mParsedEttItems = new SparseArray<>();
626 
627     public interface OutputListener {
onPatParsed(List<PatItem> items)628         void onPatParsed(List<PatItem> items);
629 
onPmtParsed(int programNumber, List<PmtItem> items)630         void onPmtParsed(int programNumber, List<PmtItem> items);
631 
onMgtParsed(List<MgtItem> items)632         void onMgtParsed(List<MgtItem> items);
633 
onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber)634         void onVctParsed(List<VctItem> items, int sectionNumber, int lastSectionNumber);
635 
onEitParsed(int sourceId, List<EitItem> items)636         void onEitParsed(int sourceId, List<EitItem> items);
637 
onEttParsed(int sourceId, List<EttItem> descriptions)638         void onEttParsed(int sourceId, List<EttItem> descriptions);
639 
onSdtParsed(List<SdtItem> items)640         void onSdtParsed(List<SdtItem> items);
641     }
642 
643     private final OutputListener mListener;
644 
SectionParser(OutputListener listener)645     public SectionParser(OutputListener listener) {
646         mListener = listener;
647     }
648 
parseSections(ByteArrayBuffer data)649     public void parseSections(ByteArrayBuffer data) {
650         int pos = 0;
651         while (pos + 3 <= data.length()) {
652             if ((data.byteAt(pos) & 0xff) == 0xff) {
653                 // Clear stuffing bytes according to H222.0 section 2.4.4.
654                 data.setLength(0);
655                 break;
656             }
657             int sectionLength =
658                     (((data.byteAt(pos + 1) & 0x0f) << 8) | (data.byteAt(pos + 2) & 0xff)) + 3;
659             if (pos + sectionLength > data.length()) {
660                 break;
661             }
662             if (DEBUG) {
663                 Log.d(TAG, "parseSections 0x" + Integer.toHexString(data.byteAt(pos) & 0xff));
664             }
665             parseSection(Arrays.copyOfRange(data.buffer(), pos, pos + sectionLength));
666             pos += sectionLength;
667         }
668         if (mListener != null) {
669             for (int i = 0; i < mParsedEttItems.size(); ++i) {
670                 int sourceId = mParsedEttItems.keyAt(i);
671                 List<EttItem> descriptions = mParsedEttItems.valueAt(i);
672                 mListener.onEttParsed(sourceId, descriptions);
673             }
674         }
675         mParsedEttItems.clear();
676     }
677 
resetVersionNumbers()678     public void resetVersionNumbers() {
679         mSectionVersionMap.clear();
680     }
681 
parseSection(byte[] data)682     private void parseSection(byte[] data) {
683         if (!checkSanity(data)) {
684             Log.d(TAG, "Bad CRC!");
685             return;
686         }
687         PsipSection section = PsipSection.create(data);
688         if (section == null) {
689             return;
690         }
691 
692         // The currentNextIndicator indicates that the section sent is currently applicable.
693         if (!section.getCurrentNextIndicator()) {
694             return;
695         }
696         int versionNumber = (data[5] & 0x3e) >> 1;
697         Integer oldVersionNumber = mSectionVersionMap.get(section);
698 
699         // The versionNumber shall be incremented when a change in the information carried within
700         // the section occurs.
701         if (oldVersionNumber != null && versionNumber == oldVersionNumber) {
702             return;
703         }
704         boolean result = false;
705         switch (data[0]) {
706             case TABLE_ID_PAT:
707                 result = parsePAT(data);
708                 break;
709             case TABLE_ID_PMT:
710                 result = parsePMT(data);
711                 break;
712             case TABLE_ID_MGT:
713                 result = parseMGT(data);
714                 break;
715             case TABLE_ID_TVCT:
716             case TABLE_ID_CVCT:
717                 result = parseVCT(data);
718                 break;
719             case TABLE_ID_EIT:
720                 result = parseEIT(data);
721                 break;
722             case TABLE_ID_ETT:
723                 result = parseETT(data);
724                 break;
725             case TABLE_ID_SDT:
726                 result = parseSDT(data);
727                 break;
728             case TABLE_ID_DVB_ACTUAL_P_F_EIT:
729             case TABLE_ID_DVB_ACTUAL_SCHEDULE_EIT:
730                 result = parseDVBEIT(data);
731                 break;
732             default:
733                 break;
734         }
735         if (result) {
736             mSectionVersionMap.put(section, versionNumber);
737         }
738     }
739 
parsePAT(byte[] data)740     private boolean parsePAT(byte[] data) {
741         if (DEBUG) {
742             Log.d(TAG, "PAT is discovered.");
743         }
744         int pos = 8;
745 
746         List<PatItem> results = new ArrayList<>();
747         for (; pos < data.length - 4; pos = pos + 4) {
748             if (pos > data.length - 4 - 4) {
749                 Log.e(TAG, "Broken PAT.");
750                 return false;
751             }
752             int programNo = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
753             int pmtPid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
754             results.add(new PatItem(programNo, pmtPid));
755         }
756         if (mListener != null) {
757             mListener.onPatParsed(results);
758         }
759         return true;
760     }
761 
parsePMT(byte[] data)762     private boolean parsePMT(byte[] data) {
763         int table_id_ext = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
764         if (DEBUG) {
765             Log.d(TAG, "PMT is discovered. programNo = " + table_id_ext);
766         }
767         if (data.length <= 11) {
768             Log.e(TAG, "Broken PMT.");
769             return false;
770         }
771         int pcrPid = (data[8] & 0x1f) << 8 | data[9];
772         int programInfoLen = (data[10] & 0x0f) << 8 | data[11];
773         int pos = 12;
774         List<TsDescriptor> descriptors = parseDescriptors(data, pos, pos + programInfoLen);
775         pos += programInfoLen;
776         if (DEBUG) {
777             Log.d(TAG, "PMT descriptors size: " + descriptors.size());
778         }
779         List<PmtItem> results = new ArrayList<>();
780         for (; pos < data.length - 4; ) {
781             if (pos < 0) {
782                 Log.e(TAG, "Broken PMT.");
783                 return false;
784             }
785             int streamType = data[pos] & 0xff;
786             int esPid = (data[pos + 1] & 0x1f) << 8 | (data[pos + 2] & 0xff);
787             int esInfoLen = (data[pos + 3] & 0xf) << 8 | (data[pos + 4] & 0xff);
788             if (data.length < pos + esInfoLen + 5) {
789                 Log.e(TAG, "Broken PMT.");
790                 return false;
791             }
792             descriptors = parseDescriptors(data, pos + 5, pos + 5 + esInfoLen);
793             List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
794             List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
795             PmtItem pmtItem = new PmtItem(streamType, esPid, audioTracks, captionTracks);
796             if (DEBUG) {
797                 Log.d(TAG, "PMT " + pmtItem + " descriptors size: " + descriptors.size());
798             }
799             results.add(pmtItem);
800             pos = pos + esInfoLen + 5;
801         }
802         results.add(new PmtItem(PmtItem.ES_PID_PCR, pcrPid, null, null));
803         if (mListener != null) {
804             mListener.onPmtParsed(table_id_ext, results);
805         }
806         return true;
807     }
808 
parseMGT(byte[] data)809     private boolean parseMGT(byte[] data) {
810         // For details of the structure for MGT, see ATSC A/65 Table 6.2.
811         if (DEBUG) {
812             Log.d(TAG, "MGT is discovered.");
813         }
814         if (data.length <= 10) {
815             Log.e(TAG, "Broken MGT.");
816             return false;
817         }
818         int tablesDefined = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
819         int pos = 11;
820         List<MgtItem> results = new ArrayList<>();
821         for (int i = 0; i < tablesDefined; ++i) {
822             if (data.length <= pos + 10) {
823                 Log.e(TAG, "Broken MGT.");
824                 return false;
825             }
826             int tableType = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
827             int tableTypePid = ((data[pos + 2] & 0x1f) << 8) | (data[pos + 3] & 0xff);
828             int descriptorsLength = ((data[pos + 9] & 0x0f) << 8) | (data[pos + 10] & 0xff);
829             pos += 11 + descriptorsLength;
830             results.add(new MgtItem(tableType, tableTypePid));
831         }
832         // Skip the remaining descriptor part which we don't use.
833 
834         if (mListener != null) {
835             mListener.onMgtParsed(results);
836         }
837         return true;
838     }
839 
parseVCT(byte[] data)840     private boolean parseVCT(byte[] data) {
841         // For details of the structure for VCT, see ATSC A/65 Table 6.4 and 6.8.
842         if (DEBUG) {
843             Log.d(TAG, "VCT is discovered.");
844         }
845         if (data.length <= 9) {
846             Log.e(TAG, "Broken VCT.");
847             return false;
848         }
849         int numChannelsInSection = (data[9] & 0xff);
850         int sectionNumber = (data[6] & 0xff);
851         int lastSectionNumber = (data[7] & 0xff);
852         if (sectionNumber > lastSectionNumber) {
853             // According to section 6.3.1 of the spec ATSC A/65,
854             // last section number is the largest section number.
855             Log.w(
856                     TAG,
857                     "Invalid VCT. Section Number "
858                             + sectionNumber
859                             + " > Last Section Number "
860                             + lastSectionNumber);
861             return false;
862         }
863         int pos = 10;
864         List<VctItem> results = new ArrayList<>();
865         for (int i = 0; i < numChannelsInSection; ++i) {
866             if (data.length <= pos + 31) {
867                 Log.e(TAG, "Broken VCT.");
868                 return false;
869             }
870             String shortName = "";
871             int shortNameSize = getShortNameSize(data, pos);
872             try {
873                 shortName =
874                         new String(Arrays.copyOfRange(data, pos, pos + shortNameSize), "UTF-16");
875             } catch (UnsupportedEncodingException e) {
876                 Log.e(TAG, "Broken VCT.", e);
877                 return false;
878             }
879             if ((data[pos + 14] & 0xf0) != 0xf0) {
880                 Log.e(TAG, "Broken VCT.");
881                 return false;
882             }
883             int majorNumber = ((data[pos + 14] & 0x0f) << 6) | ((data[pos + 15] & 0xff) >> 2);
884             int minorNumber = ((data[pos + 15] & 0x03) << 8) | (data[pos + 16] & 0xff);
885             if ((majorNumber & 0x3f0) == 0x3f0) {
886                 // If the six MSBs are 111111, these indicate that there is only one-part channel
887                 // number. To see details, refer A/65 Section 6.3.2.
888                 majorNumber = ((majorNumber & 0xf) << 10) + minorNumber;
889                 minorNumber = 0;
890             }
891             int channelTsid = ((data[pos + 22] & 0xff) << 8) | (data[pos + 23] & 0xff);
892             int programNumber = ((data[pos + 24] & 0xff) << 8) | (data[pos + 25] & 0xff);
893             boolean accessControlled = (data[pos + 26] & 0x20) != 0;
894             boolean hidden = (data[pos + 26] & 0x10) != 0;
895             int serviceType = (data[pos + 27] & 0x3f);
896             int sourceId = ((data[pos + 28] & 0xff) << 8) | (data[pos + 29] & 0xff);
897             int descriptorsPos = pos + 32;
898             int descriptorsLength = ((data[pos + 30] & 0x03) << 8) | (data[pos + 31] & 0xff);
899             pos += 32 + descriptorsLength;
900             if (data.length < pos) {
901                 Log.e(TAG, "Broken VCT.");
902                 return false;
903             }
904             List<TsDescriptor> descriptors =
905                     parseDescriptors(data, descriptorsPos, descriptorsPos + descriptorsLength);
906             String longName = null;
907             for (TsDescriptor descriptor : descriptors) {
908                 if (descriptor instanceof ExtendedChannelNameDescriptor) {
909                     ExtendedChannelNameDescriptor extendedChannelNameDescriptor =
910                             (ExtendedChannelNameDescriptor) descriptor;
911                     longName = extendedChannelNameDescriptor.getLongChannelName();
912                     break;
913                 }
914             }
915             if (DEBUG) {
916                 Log.d(
917                         TAG,
918                         String.format(
919                                 "Found channel [%s] %s - serviceType: %d tsid: 0x%x program: %d"
920                                     + " channel: %d-%d encrypted: %b hidden: %b, descriptors: %d",
921                                 shortName,
922                                 longName,
923                                 serviceType,
924                                 channelTsid,
925                                 programNumber,
926                                 majorNumber,
927                                 minorNumber,
928                                 accessControlled,
929                                 hidden,
930                                 descriptors.size()));
931             }
932             if ((serviceType == AtscServiceType.SERVICE_TYPE_ATSC_AUDIO_VALUE
933                             || serviceType
934                                     == AtscServiceType.SERVICE_TYPE_ATSC_DIGITAL_TELEVISION_VALUE
935                             || serviceType
936                                     == AtscServiceType
937                                             .SERVICE_TYPE_UNASSOCIATED_SMALL_SCREEN_SERVICE_VALUE)
938                     && !accessControlled
939                     && !hidden) {
940                 // Hide hidden, encrypted, or unsupported ATSC service type channels
941                 results.add(
942                         new VctItem(
943                                 shortName,
944                                 longName,
945                                 serviceType,
946                                 channelTsid,
947                                 programNumber,
948                                 majorNumber,
949                                 minorNumber,
950                                 sourceId));
951             }
952         }
953         // Skip the remaining descriptor part which we don't use.
954 
955         if (mListener != null) {
956             mListener.onVctParsed(results, sectionNumber, lastSectionNumber);
957         }
958         return true;
959     }
960 
parseEIT(byte[] data)961     private boolean parseEIT(byte[] data) {
962         // For details of the structure for EIT, see ATSC A/65 Table 6.11.
963         if (DEBUG) {
964             Log.d(TAG, "EIT is discovered.");
965         }
966         if (data.length <= 9) {
967             Log.e(TAG, "Broken EIT.");
968             return false;
969         }
970         int sourceId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
971         int numEventsInSection = (data[9] & 0xff);
972 
973         int pos = 10;
974         List<EitItem> results = new ArrayList<>();
975         for (int i = 0; i < numEventsInSection; ++i) {
976             if (data.length <= pos + 9) {
977                 Log.e(TAG, "Broken EIT.");
978                 return false;
979             }
980             if ((data[pos] & 0xc0) != 0xc0) {
981                 Log.e(TAG, "Broken EIT.");
982                 return false;
983             }
984             int eventId = ((data[pos] & 0x3f) << 8) + (data[pos + 1] & 0xff);
985             long startTime =
986                     ((data[pos + 2] & (long) 0xff) << 24)
987                             | ((data[pos + 3] & 0xff) << 16)
988                             | ((data[pos + 4] & 0xff) << 8)
989                             | (data[pos + 5] & 0xff);
990             int lengthInSecond =
991                     ((data[pos + 6] & 0x0f) << 16)
992                             | ((data[pos + 7] & 0xff) << 8)
993                             | (data[pos + 8] & 0xff);
994             int titleLength = (data[pos + 9] & 0xff);
995             if (data.length <= pos + 10 + titleLength + 1) {
996                 Log.e(TAG, "Broken EIT.");
997                 return false;
998             }
999             String titleText = "";
1000             if (titleLength > 0) {
1001                 titleText = extractText(data, pos + 10);
1002             }
1003             if ((data[pos + 10 + titleLength] & 0xf0) != 0xf0) {
1004                 Log.e(TAG, "Broken EIT.");
1005                 return false;
1006             }
1007             int descriptorsLength =
1008                     ((data[pos + 10 + titleLength] & 0x0f) << 8)
1009                             | (data[pos + 10 + titleLength + 1] & 0xff);
1010             int descriptorsPos = pos + 10 + titleLength + 2;
1011             if (data.length < descriptorsPos + descriptorsLength) {
1012                 Log.e(TAG, "Broken EIT.");
1013                 return false;
1014             }
1015             List<TsDescriptor> descriptors =
1016                     parseDescriptors(data, descriptorsPos, descriptorsPos + descriptorsLength);
1017             if (DEBUG) {
1018                 Log.d(TAG, String.format("EIT descriptors size: %d", descriptors.size()));
1019             }
1020             String contentRating = generateContentRating(descriptors);
1021             String broadcastGenre = generateBroadcastGenre(descriptors);
1022             String canonicalGenre = generateCanonicalGenre(descriptors);
1023             List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
1024             List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
1025             pos += 10 + titleLength + 2 + descriptorsLength;
1026             results.add(
1027                     new EitItem(
1028                             EitItem.INVALID_PROGRAM_ID,
1029                             eventId,
1030                             titleText,
1031                             startTime,
1032                             lengthInSecond,
1033                             contentRating,
1034                             audioTracks,
1035                             captionTracks,
1036                             broadcastGenre,
1037                             canonicalGenre,
1038                             null));
1039         }
1040         if (mListener != null) {
1041             mListener.onEitParsed(sourceId, results);
1042         }
1043         return true;
1044     }
1045 
parseETT(byte[] data)1046     private boolean parseETT(byte[] data) {
1047         // For details of the structure for ETT, see ATSC A/65 Table 6.13.
1048         if (DEBUG) {
1049             Log.d(TAG, "ETT is discovered.");
1050         }
1051         if (data.length <= 12) {
1052             Log.e(TAG, "Broken ETT.");
1053             return false;
1054         }
1055         int sourceId = ((data[9] & 0xff) << 8) | (data[10] & 0xff);
1056         int eventId = (((data[11] & 0xff) << 8) | (data[12] & 0xff)) >> 2;
1057         String text = extractText(data, 13);
1058         List<EttItem> ettItems = mParsedEttItems.get(sourceId);
1059         if (ettItems == null) {
1060             ettItems = new ArrayList<>();
1061             mParsedEttItems.put(sourceId, ettItems);
1062         }
1063         ettItems.add(new EttItem(eventId, text));
1064         return true;
1065     }
1066 
parseSDT(byte[] data)1067     private boolean parseSDT(byte[] data) {
1068         // For details of the structure for SDT, see DVB Document A038 Table 5.
1069         if (DEBUG) {
1070             Log.d(TAG, "SDT id discovered");
1071         }
1072         if (data.length <= 11) {
1073             Log.e(TAG, "Broken SDT.");
1074             return false;
1075         }
1076         if ((data[1] & 0x80) >> 7 != 1) {
1077             Log.e(TAG, "Broken SDT, section syntax indicator error.");
1078             return false;
1079         }
1080         int sectionLength = ((data[1] & 0x0f) << 8) | (data[2] & 0xff);
1081         int transportStreamId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
1082         int originalNetworkId = ((data[8] & 0xff) << 8) | (data[9] & 0xff);
1083         int pos = 11;
1084         if (sectionLength + 3 > data.length) {
1085             Log.e(TAG, "Broken SDT.");
1086         }
1087         List<SdtItem> sdtItems = new ArrayList<>();
1088         while (pos + 9 < data.length) {
1089             int serviceId = ((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff);
1090             int descriptorsLength = ((data[pos + 3] & 0x0f) << 8) | (data[pos + 4] & 0xff);
1091             pos += 5;
1092             List<TsDescriptor> descriptors = parseDescriptors(data, pos, pos + descriptorsLength);
1093             List<ServiceDescriptor> serviceDescriptors = generateServiceDescriptors(descriptors);
1094             String serviceName = "";
1095             String serviceProviderName = "";
1096             int serviceType = 0;
1097             for (ServiceDescriptor serviceDescriptor : serviceDescriptors) {
1098                 serviceName = serviceDescriptor.getServiceName();
1099                 serviceProviderName = serviceDescriptor.getServiceProviderName();
1100                 serviceType = serviceDescriptor.getServiceType();
1101             }
1102             if (serviceDescriptors.size() > 0) {
1103                 sdtItems.add(
1104                         new SdtItem(
1105                                 serviceName,
1106                                 serviceProviderName,
1107                                 serviceType,
1108                                 serviceId,
1109                                 originalNetworkId));
1110             }
1111             pos += descriptorsLength;
1112         }
1113         if (mListener != null) {
1114             mListener.onSdtParsed(sdtItems);
1115         }
1116         return true;
1117     }
1118 
parseDVBEIT(byte[] data)1119     private boolean parseDVBEIT(byte[] data) {
1120         // For details of the structure for DVB ETT, see DVB Document A038 Table 7.
1121         if (DEBUG) {
1122             Log.d(TAG, "DVB EIT is discovered.");
1123         }
1124         if (data.length < 18) {
1125             Log.e(TAG, "Broken DVB EIT.");
1126             return false;
1127         }
1128         int sectionLength = ((data[1] & 0x0f) << 8) | (data[2] & 0xff);
1129         int sourceId = ((data[3] & 0xff) << 8) | (data[4] & 0xff);
1130         int transportStreamId = ((data[8] & 0xff) << 8) | (data[9] & 0xff);
1131         int originalNetworkId = ((data[10] & 0xff) << 8) | (data[11] & 0xff);
1132 
1133         int pos = 14;
1134         List<EitItem> results = new ArrayList<>();
1135         while (pos + 12 < data.length) {
1136             int eventId = ((data[pos] & 0xff) << 8) + (data[pos + 1] & 0xff);
1137             float modifiedJulianDate = ((data[pos + 2] & 0xff) << 8) | (data[pos + 3] & 0xff);
1138             int startYear = (int) ((modifiedJulianDate - 15078.2f) / 365.25f);
1139             int mjdMonth =
1140                     (int)
1141                             ((modifiedJulianDate - 14956.1f - (int) (startYear * 365.25f))
1142                                     / 30.6001f);
1143             int startDay =
1144                     (int) modifiedJulianDate
1145                             - 14956
1146                             - (int) (startYear * 365.25f)
1147                             - (int) (mjdMonth * 30.6001f);
1148             int startMonth = mjdMonth - 1;
1149             if (mjdMonth == 14 || mjdMonth == 15) {
1150                 startYear += 1;
1151                 startMonth -= 12;
1152             }
1153             int startHour = ((data[pos + 4] & 0xf0) >> 4) * 10 + (data[pos + 4] & 0x0f);
1154             int startMinute = ((data[pos + 5] & 0xf0) >> 4) * 10 + (data[pos + 5] & 0x0f);
1155             int startSecond = ((data[pos + 6] & 0xf0) >> 4) * 10 + (data[pos + 6] & 0x0f);
1156             Calendar calendar = Calendar.getInstance();
1157             startYear += 1900;
1158             calendar.set(startYear, startMonth, startDay, startHour, startMinute, startSecond);
1159             long startTime =
1160                     ConvertUtils.convertUnixEpochToGPSTime(calendar.getTimeInMillis() / 1000);
1161             int durationInSecond =
1162                     (((data[pos + 7] & 0xf0) >> 4) * 10 + (data[pos + 7] & 0x0f)) * 3600
1163                             + (((data[pos + 8] & 0xf0) >> 4) * 10 + (data[pos + 8] & 0x0f)) * 60
1164                             + (((data[pos + 9] & 0xf0) >> 4) * 10 + (data[pos + 9] & 0x0f));
1165             int descriptorsLength = ((data[pos + 10] & 0x0f) << 8) | (data[pos + 10 + 1] & 0xff);
1166             int descriptorsPos = pos + 10 + 2;
1167             if (data.length < descriptorsPos + descriptorsLength) {
1168                 Log.e(TAG, "Broken EIT.");
1169                 return false;
1170             }
1171             List<TsDescriptor> descriptors =
1172                     parseDescriptors(data, descriptorsPos, descriptorsPos + descriptorsLength);
1173             if (DEBUG) {
1174                 Log.d(TAG, String.format("DVB EIT descriptors size: %d", descriptors.size()));
1175             }
1176             // TODO: Add logic to generating content rating for dvb. See DVB document 6.2.28 for
1177             // details. Content rating here will be null
1178             String contentRating = generateContentRating(descriptors);
1179             // TODO: Add logic for generating genre for dvb. See DVB document 6.2.9 for details.
1180             // Genre here will be null here.
1181             String broadcastGenre = generateBroadcastGenre(descriptors);
1182             String canonicalGenre = generateCanonicalGenre(descriptors);
1183             String titleText = generateShortEventName(descriptors);
1184             List<AtscAudioTrack> audioTracks = generateAudioTracks(descriptors);
1185             List<AtscCaptionTrack> captionTracks = generateCaptionTracks(descriptors);
1186             pos += 12 + descriptorsLength;
1187             results.add(
1188                     new EitItem(
1189                             EitItem.INVALID_PROGRAM_ID,
1190                             eventId,
1191                             titleText,
1192                             startTime,
1193                             durationInSecond,
1194                             contentRating,
1195                             audioTracks,
1196                             captionTracks,
1197                             broadcastGenre,
1198                             canonicalGenre,
1199                             null));
1200         }
1201         if (mListener != null) {
1202             mListener.onEitParsed(sourceId, results);
1203         }
1204         return true;
1205     }
1206 
generateAudioTracks(List<TsDescriptor> descriptors)1207     private static List<AtscAudioTrack> generateAudioTracks(List<TsDescriptor> descriptors) {
1208         // The list of audio tracks sent is located at both AC3 Audio descriptor and ISO 639
1209         // Language descriptor.
1210         List<AtscAudioTrack> ac3Tracks = new ArrayList<>();
1211         List<AtscAudioTrack> iso639LanguageTracks = new ArrayList<>();
1212         for (TsDescriptor descriptor : descriptors) {
1213             if (descriptor instanceof Ac3AudioDescriptor) {
1214                 Ac3AudioDescriptor audioDescriptor = (Ac3AudioDescriptor) descriptor;
1215                 String language = null;
1216                 if (audioDescriptor.getLanguage() != null) {
1217                     language = audioDescriptor.getLanguage();
1218                 }
1219                 if (language == null) {
1220                     language = "";
1221                 }
1222                 AtscAudioTrack audioTrack =
1223                         AtscAudioTrack.newBuilder()
1224                                 .setLanguage(language)
1225                                 .setAudioType(AtscAudioTrack.AudioType.AUDIOTYPE_UNDEFINED)
1226                                 .setChannelCount(audioDescriptor.getNumChannels())
1227                                 .setSampleRate(audioDescriptor.getSampleRate())
1228                                 .build();
1229                 ac3Tracks.add(audioTrack);
1230             }
1231         }
1232         for (TsDescriptor descriptor : descriptors) {
1233             if (descriptor instanceof Iso639LanguageDescriptor) {
1234                 Iso639LanguageDescriptor iso639LanguageDescriptor =
1235                         (Iso639LanguageDescriptor) descriptor;
1236                 iso639LanguageTracks.addAll(iso639LanguageDescriptor.getAudioTracks());
1237             }
1238         }
1239 
1240         // An AC3 audio stream descriptor only has a audio channel count and a audio sample rate
1241         // while a ISO 639 Language descriptor only has a audio type, which describes a main use
1242         // case of its audio track.
1243         // Some channels contain only AC3 audio stream descriptors with valid language values.
1244         // Other channels contain both an AC3 audio stream descriptor and a ISO 639 Language
1245         // descriptor per audio track, and those AC3 audio stream descriptors often have a null
1246         // value of language field.
1247         // Combines two descriptors into one in order to gather more audio track specific
1248         // information as much as possible.
1249         List<AtscAudioTrack> tracks = new ArrayList<>();
1250         if (!ac3Tracks.isEmpty()
1251                 && !iso639LanguageTracks.isEmpty()
1252                 && ac3Tracks.size() != iso639LanguageTracks.size()) {
1253             // This shouldn't be happen. In here, it handles two cases. The first case is that the
1254             // only one type of descriptors arrives. The second case is that the two types of
1255             // descriptors have the same number of tracks.
1256             Log.e(TAG, "AC3 audio stream descriptors size != ISO 639 Language descriptors size");
1257             return tracks;
1258         }
1259         int size = Math.max(ac3Tracks.size(), iso639LanguageTracks.size());
1260         for (int i = 0; i < size; ++i) {
1261             AtscAudioTrack.Builder audioTrack = null;
1262             if (i < ac3Tracks.size()) {
1263                 audioTrack = ac3Tracks.get(i).toBuilder();
1264             }
1265             if (i < iso639LanguageTracks.size()) {
1266                 if (audioTrack == null) {
1267                     audioTrack = iso639LanguageTracks.get(i).toBuilder();
1268                 } else {
1269                     AtscAudioTrack iso639LanguageTrack = iso639LanguageTracks.get(i);
1270                     if (!audioTrack.hasLanguage()
1271                             || TextUtils.equals(audioTrack.getLanguage(), "")) {
1272                         audioTrack.setLanguage(iso639LanguageTrack.getLanguage());
1273                     }
1274                     audioTrack.setAudioType(iso639LanguageTrack.getAudioType());
1275                 }
1276             }
1277             String language = ISO_LANGUAGE_CODE_MAP.get(audioTrack.getLanguage());
1278             if (language != null) {
1279                 audioTrack = audioTrack.setLanguage(language);
1280             }
1281             tracks.add(audioTrack.build());
1282         }
1283         return tracks;
1284     }
1285 
generateCaptionTracks(List<TsDescriptor> descriptors)1286     private static List<AtscCaptionTrack> generateCaptionTracks(List<TsDescriptor> descriptors) {
1287         List<AtscCaptionTrack> services = new ArrayList<>();
1288         for (TsDescriptor descriptor : descriptors) {
1289             if (descriptor instanceof CaptionServiceDescriptor) {
1290                 CaptionServiceDescriptor captionServiceDescriptor =
1291                         (CaptionServiceDescriptor) descriptor;
1292                 services.addAll(captionServiceDescriptor.getCaptionTracks());
1293             }
1294         }
1295         return services;
1296     }
1297 
1298     @VisibleForTesting
generateContentRating(List<TsDescriptor> descriptors)1299     static String generateContentRating(List<TsDescriptor> descriptors) {
1300         Set<String> contentRatings = new ArraySet<>();
1301         List<RatingRegion> usRatingRegions = getRatingRegions(descriptors, RATING_REGION_US_TV);
1302         List<RatingRegion> krRatingRegions = getRatingRegions(descriptors, RATING_REGION_KR_TV);
1303         for (RatingRegion region : usRatingRegions) {
1304             String contentRating = getUsRating(region);
1305             if (contentRating != null) {
1306                 contentRatings.add(contentRating);
1307             }
1308         }
1309         for (RatingRegion region : krRatingRegions) {
1310             String contentRating = getKrRating(region);
1311             if (contentRating != null) {
1312                 contentRatings.add(contentRating);
1313             }
1314         }
1315         return TextUtils.join(",", contentRatings);
1316     }
1317 
1318     /**
1319      * Gets a list of {@link RatingRegion} in the specific region.
1320      *
1321      * @param descriptors {@link TsDescriptor} list which may contains rating information
1322      * @param region the specific region
1323      * @return a list of {@link RatingRegion} in the specific region
1324      */
getRatingRegions(List<TsDescriptor> descriptors, int region)1325     private static List<RatingRegion> getRatingRegions(List<TsDescriptor> descriptors, int region) {
1326         List<RatingRegion> ratingRegions = new ArrayList<>();
1327         for (TsDescriptor descriptor : descriptors) {
1328             if (!(descriptor instanceof ContentAdvisoryDescriptor)) {
1329                 continue;
1330             }
1331             ContentAdvisoryDescriptor contentAdvisoryDescriptor =
1332                     (ContentAdvisoryDescriptor) descriptor;
1333             for (RatingRegion ratingRegion : contentAdvisoryDescriptor.getRatingRegions()) {
1334                 if (ratingRegion.getName() == region) {
1335                     ratingRegions.add(ratingRegion);
1336                 }
1337             }
1338         }
1339         return ratingRegions;
1340     }
1341 
1342     /**
1343      * Gets US content rating and subratings (if any).
1344      *
1345      * @param ratingRegion a {@link RatingRegion} instance which may contain rating information.
1346      * @return A string representing the US content rating and subratings. The format of the string
1347      *     is defined in {@link TvContentRating}. null, if no such a string exists.
1348      */
getUsRating(RatingRegion ratingRegion)1349     private static String getUsRating(RatingRegion ratingRegion) {
1350         if (ratingRegion.getName() != RATING_REGION_US_TV) {
1351             return null;
1352         }
1353         List<RegionalRating> regionalRatings = ratingRegion.getRegionalRatings();
1354         String rating = null;
1355         int ratingIndex = VALUE_US_TV_NONE;
1356         List<String> subratings = new ArrayList<>();
1357         for (RegionalRating index : regionalRatings) {
1358             // See Table 3 of ANSI-CEA-766-D
1359             int dimension = index.getDimension();
1360             int value = index.getRating();
1361             switch (dimension) {
1362                     // According to Table 6.27 of ATSC A65,
1363                     // the dimensions shall be in increasing order.
1364                     // Therefore, rating and ratingIndex are assigned before any corresponding
1365                     // subrating.
1366                 case DIMENSION_US_TV_RATING:
1367                     if (value >= VALUE_US_TV_G && value < RATING_REGION_TABLE_US_TV.length) {
1368                         rating = RATING_REGION_TABLE_US_TV[value];
1369                         ratingIndex = value;
1370                     }
1371                     break;
1372                 case DIMENSION_US_TV_D:
1373                     if (value == 1
1374                             && (ratingIndex == VALUE_US_TV_PG || ratingIndex == VALUE_US_TV_14)) {
1375                         // US_TV_D is applicable to US_TV_PG and US_TV_14
1376                         subratings.add(RATING_REGION_TABLE_US_TV_SUBRATING[dimension - 1]);
1377                     }
1378                     break;
1379                 case DIMENSION_US_TV_L:
1380                 case DIMENSION_US_TV_S:
1381                 case DIMENSION_US_TV_V:
1382                     if (value == 1
1383                             && ratingIndex >= VALUE_US_TV_PG
1384                             && ratingIndex <= VALUE_US_TV_MA) {
1385                         // US_TV_L, US_TV_S, and US_TV_V are applicable to
1386                         // US_TV_PG, US_TV_14 and US_TV_MA
1387                         subratings.add(RATING_REGION_TABLE_US_TV_SUBRATING[dimension - 1]);
1388                     }
1389                     break;
1390                 case DIMENSION_US_TV_Y:
1391                     if (rating == null) {
1392                         if (value == VALUE_US_TV_Y) {
1393                             rating = STRING_US_TV_Y;
1394                         } else if (value == VALUE_US_TV_Y7) {
1395                             rating = STRING_US_TV_Y7;
1396                         }
1397                     }
1398                     break;
1399                 case DIMENSION_US_TV_FV:
1400                     if (STRING_US_TV_Y7.equals(rating) && value == 1) {
1401                         // US_TV_FV is applicable to US_TV_Y7
1402                         subratings.add(STRING_US_TV_FV);
1403                     }
1404                     break;
1405                 case DIMENSION_US_MV_RATING:
1406                     if (value >= VALUE_US_MV_G && value <= VALUE_US_MV_X) {
1407                         if (value == VALUE_US_MV_X) {
1408                             // US_MV_X was replaced by US_MV_NC17 in 1990,
1409                             // and it's not supported by TvContentRating
1410                             value = VALUE_US_MV_NC17;
1411                         }
1412                         if (rating != null) {
1413                             // According to Table 3 of ANSI-CEA-766-D,
1414                             // DIMENSION_US_TV_RATING and DIMENSION_US_MV_RATING shall not be
1415                             // present in the same descriptor.
1416                             Log.w(
1417                                     TAG,
1418                                     "DIMENSION_US_TV_RATING and DIMENSION_US_MV_RATING are "
1419                                             + "present in the same descriptor");
1420                         } else {
1421                             return TvContentRating.createRating(
1422                                             RATING_DOMAIN,
1423                                             RATING_REGION_RATING_SYSTEM_US_MV,
1424                                             RATING_REGION_TABLE_US_MV[value - 2])
1425                                     .flattenToString();
1426                         }
1427                     }
1428                     break;
1429 
1430                 default:
1431                     break;
1432             }
1433         }
1434         if (rating == null) {
1435             return null;
1436         }
1437 
1438         String[] subratingArray = subratings.toArray(new String[subratings.size()]);
1439         return TvContentRating.createRating(
1440                         RATING_DOMAIN, RATING_REGION_RATING_SYSTEM_US_TV, rating, subratingArray)
1441                 .flattenToString();
1442     }
1443 
1444     /**
1445      * Gets KR(South Korea) content rating.
1446      *
1447      * @param ratingRegion a {@link RatingRegion} instance which may contain rating information.
1448      * @return A string representing the KR content rating. The format of the string is defined in
1449      *     {@link TvContentRating}. null, if no such a string exists.
1450      */
getKrRating(RatingRegion ratingRegion)1451     private static String getKrRating(RatingRegion ratingRegion) {
1452         if (ratingRegion.getName() != RATING_REGION_KR_TV) {
1453             return null;
1454         }
1455         List<RegionalRating> regionalRatings = ratingRegion.getRegionalRatings();
1456         String rating = null;
1457         for (RegionalRating index : regionalRatings) {
1458             if (index.getDimension() == 0
1459                     && index.getRating() >= 0
1460                     && index.getRating() < RATING_REGION_TABLE_KR_TV.length) {
1461                 rating = RATING_REGION_TABLE_KR_TV[index.getRating()];
1462                 break;
1463             }
1464         }
1465         if (rating == null) {
1466             return null;
1467         }
1468         return TvContentRating.createRating(
1469                         RATING_DOMAIN, RATING_REGION_RATING_SYSTEM_KR_TV, rating)
1470                 .flattenToString();
1471     }
1472 
generateBroadcastGenre(List<TsDescriptor> descriptors)1473     private static String generateBroadcastGenre(List<TsDescriptor> descriptors) {
1474         for (TsDescriptor descriptor : descriptors) {
1475             if (descriptor instanceof GenreDescriptor) {
1476                 GenreDescriptor genreDescriptor = (GenreDescriptor) descriptor;
1477                 return TextUtils.join(",", genreDescriptor.getBroadcastGenres());
1478             }
1479         }
1480         return null;
1481     }
1482 
generateCanonicalGenre(List<TsDescriptor> descriptors)1483     private static String generateCanonicalGenre(List<TsDescriptor> descriptors) {
1484         for (TsDescriptor descriptor : descriptors) {
1485             if (descriptor instanceof GenreDescriptor) {
1486                 GenreDescriptor genreDescriptor = (GenreDescriptor) descriptor;
1487                 return Genres.encode(genreDescriptor.getCanonicalGenres());
1488             }
1489         }
1490         return null;
1491     }
1492 
generateServiceDescriptors( List<TsDescriptor> descriptors)1493     private static List<ServiceDescriptor> generateServiceDescriptors(
1494             List<TsDescriptor> descriptors) {
1495         List<ServiceDescriptor> serviceDescriptors = new ArrayList<>();
1496         for (TsDescriptor descriptor : descriptors) {
1497             if (descriptor instanceof ServiceDescriptor) {
1498                 ServiceDescriptor serviceDescriptor = (ServiceDescriptor) descriptor;
1499                 serviceDescriptors.add(serviceDescriptor);
1500             }
1501         }
1502         return serviceDescriptors;
1503     }
1504 
generateShortEventName(List<TsDescriptor> descriptors)1505     private static String generateShortEventName(List<TsDescriptor> descriptors) {
1506         for (TsDescriptor descriptor : descriptors) {
1507             if (descriptor instanceof ShortEventDescriptor) {
1508                 ShortEventDescriptor shortEventDescriptor = (ShortEventDescriptor) descriptor;
1509                 return shortEventDescriptor.getEventName();
1510             }
1511         }
1512         return "";
1513     }
1514 
parseDescriptors(byte[] data, int offset, int limit)1515     private static List<TsDescriptor> parseDescriptors(byte[] data, int offset, int limit) {
1516         // For details of the structure for descriptors, see ATSC A/65 Section 6.9.
1517         List<TsDescriptor> descriptors = new ArrayList<>();
1518         if (data.length < limit) {
1519             return descriptors;
1520         }
1521         int pos = offset;
1522         while (pos + 1 < limit) {
1523             int tag = data[pos] & 0xff;
1524             int length = data[pos + 1] & 0xff;
1525             if (length <= 0) {
1526                 break;
1527             }
1528             if (limit < pos + length + 2) {
1529                 break;
1530             }
1531             if (DEBUG) {
1532                 Log.d(TAG, String.format("Descriptor tag: %02x", tag));
1533             }
1534             TsDescriptor descriptor = null;
1535             switch (tag) {
1536                 case DESCRIPTOR_TAG_CONTENT_ADVISORY:
1537                     descriptor = parseContentAdvisory(data, pos, pos + length + 2);
1538                     break;
1539 
1540                 case DESCRIPTOR_TAG_CAPTION_SERVICE:
1541                     descriptor = parseCaptionService(data, pos, pos + length + 2);
1542                     break;
1543 
1544                 case DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME:
1545                     descriptor = parseLongChannelName(data, pos, pos + length + 2);
1546                     break;
1547 
1548                 case DESCRIPTOR_TAG_GENRE:
1549                     descriptor = parseGenre(data, pos, pos + length + 2);
1550                     break;
1551 
1552                 case DESCRIPTOR_TAG_AC3_AUDIO_STREAM:
1553                     descriptor = parseAc3AudioStream(data, pos, pos + length + 2);
1554                     break;
1555 
1556                 case DESCRIPTOR_TAG_ISO639LANGUAGE:
1557                     descriptor = parseIso639Language(data, pos, pos + length + 2);
1558                     break;
1559 
1560                 case DVB_DESCRIPTOR_TAG_SERVICE:
1561                     descriptor = parseDvbService(data, pos, pos + length + 2);
1562                     break;
1563 
1564                 case DVB_DESCRIPTOR_TAG_SHORT_EVENT:
1565                     descriptor = parseDvbShortEvent(data, pos, pos + length + 2);
1566                     break;
1567 
1568                 case DVB_DESCRIPTOR_TAG_CONTENT:
1569                     descriptor = parseDvbContent(data, pos, pos + length + 2);
1570                     break;
1571 
1572                 case DVB_DESCRIPTOR_TAG_PARENTAL_RATING:
1573                     descriptor = parseDvbParentalRating(data, pos, pos + length + 2);
1574                     break;
1575 
1576                 default:
1577             }
1578             if (descriptor != null) {
1579                 if (DEBUG) {
1580                     Log.d(TAG, "Descriptor parsed: " + descriptor);
1581                 }
1582                 descriptors.add(descriptor);
1583             }
1584             pos += length + 2;
1585         }
1586         return descriptors;
1587     }
1588 
parseIso639Language(byte[] data, int pos, int limit)1589     private static Iso639LanguageDescriptor parseIso639Language(byte[] data, int pos, int limit) {
1590         // For the details of the structure of ISO 639 language descriptor,
1591         // see ISO13818-1 second edition Section 2.6.18.
1592         pos += 2;
1593         List<AtscAudioTrack> audioTracks = new ArrayList<>();
1594         while (pos + 4 <= limit) {
1595             if (limit <= pos + 3) {
1596                 Log.e(TAG, "Broken Iso639Language.");
1597                 return null;
1598             }
1599             String language = new String(data, pos, 3);
1600             int audioTypeInt = data[pos + 3] & 0xff;
1601             AtscAudioTrack.AudioType audioType = AtscAudioTrack.AudioType.forNumber(audioTypeInt);
1602             if (audioType == null) {
1603                 audioType = AtscAudioTrack.AudioType.AUDIOTYPE_UNDEFINED;
1604             }
1605             AtscAudioTrack audioTrack =
1606                     AtscAudioTrack.newBuilder()
1607                             .setLanguage(language)
1608                             .setAudioType(audioType)
1609                             .build();
1610             audioTracks.add(audioTrack);
1611             pos += 4;
1612         }
1613         return new Iso639LanguageDescriptor(audioTracks);
1614     }
1615 
parseCaptionService(byte[] data, int pos, int limit)1616     private static CaptionServiceDescriptor parseCaptionService(byte[] data, int pos, int limit) {
1617         // For the details of the structure of caption service descriptor,
1618         // see ATSC A/65 Section 6.9.2.
1619         if (limit <= pos + 2) {
1620             Log.e(TAG, "Broken CaptionServiceDescriptor.");
1621             return null;
1622         }
1623         List<AtscCaptionTrack> services = new ArrayList<>();
1624         pos += 2;
1625         int numberServices = data[pos] & 0x1f;
1626         ++pos;
1627         if (limit < pos + numberServices * 6) {
1628             Log.e(TAG, "Broken CaptionServiceDescriptor.");
1629             return null;
1630         }
1631         for (int i = 0; i < numberServices; ++i) {
1632             String language = new String(Arrays.copyOfRange(data, pos, pos + 3));
1633             pos += 3;
1634             boolean ccType = (data[pos] & 0x80) != 0;
1635             if (!ccType) {
1636                 pos += 3;
1637                 continue;
1638             }
1639             int captionServiceNumber = data[pos] & 0x3f;
1640             ++pos;
1641             boolean easyReader = (data[pos] & 0x80) != 0;
1642             boolean wideAspectRatio = (data[pos] & 0x40) != 0;
1643             byte[] reserved = new byte[2];
1644             reserved[0] = (byte) (data[pos] << 2);
1645             reserved[0] |= (byte) ((data[pos + 1] & 0xc0) >>> 6);
1646             reserved[1] = (byte) ((data[pos + 1] & 0x3f) << 2);
1647             pos += 2;
1648             AtscCaptionTrack captionTrack =
1649                     AtscCaptionTrack.newBuilder()
1650                             .setLanguage(language)
1651                             .setServiceNumber(captionServiceNumber)
1652                             .setEasyReader(easyReader)
1653                             .setWideAspectRatio(wideAspectRatio)
1654                             .build();
1655             services.add(captionTrack);
1656         }
1657         return new CaptionServiceDescriptor(services);
1658     }
1659 
parseContentAdvisory(byte[] data, int pos, int limit)1660     private static ContentAdvisoryDescriptor parseContentAdvisory(byte[] data, int pos, int limit) {
1661         // For details of the structure for content advisory descriptor, see A/65 Table 6.27.
1662         if (limit <= pos + 2) {
1663             Log.e(TAG, "Broken ContentAdvisory");
1664             return null;
1665         }
1666         int count = data[pos + 2] & 0x3f;
1667         pos += 3;
1668         List<RatingRegion> ratingRegions = new ArrayList<>();
1669         for (int i = 0; i < count; ++i) {
1670             if (limit <= pos + 1) {
1671                 Log.e(TAG, "Broken ContentAdvisory");
1672                 return null;
1673             }
1674             List<RegionalRating> indices = new ArrayList<>();
1675             int ratingRegion = data[pos] & 0xff;
1676             int dimensionCount = data[pos + 1] & 0xff;
1677             pos += 2;
1678             int previousDimension = -1;
1679             for (int j = 0; j < dimensionCount; ++j) {
1680                 if (limit <= pos + 1) {
1681                     Log.e(TAG, "Broken ContentAdvisory");
1682                     return null;
1683                 }
1684                 int dimensionIndex = data[pos] & 0xff;
1685                 int ratingValue = data[pos + 1] & 0x0f;
1686                 if (dimensionIndex <= previousDimension) {
1687                     // According to Table 6.27 of ATSC A65,
1688                     // the indices shall be in increasing order.
1689                     Log.e(TAG, "Broken ContentAdvisory");
1690                     return null;
1691                 }
1692                 previousDimension = dimensionIndex;
1693                 pos += 2;
1694                 indices.add(new RegionalRating(dimensionIndex, ratingValue));
1695             }
1696             if (limit <= pos) {
1697                 Log.e(TAG, "Broken ContentAdvisory");
1698                 return null;
1699             }
1700             int ratingDescriptionLength = data[pos] & 0xff;
1701             ++pos;
1702             if (limit < pos + ratingDescriptionLength) {
1703                 Log.e(TAG, "Broken ContentAdvisory");
1704                 return null;
1705             }
1706             String ratingDescription = extractText(data, pos);
1707             pos += ratingDescriptionLength;
1708             ratingRegions.add(new RatingRegion(ratingRegion, ratingDescription, indices));
1709         }
1710         return new ContentAdvisoryDescriptor(ratingRegions);
1711     }
1712 
parseLongChannelName( byte[] data, int pos, int limit)1713     private static ExtendedChannelNameDescriptor parseLongChannelName(
1714             byte[] data, int pos, int limit) {
1715         if (limit <= pos + 2) {
1716             Log.e(TAG, "Broken ExtendedChannelName.");
1717             return null;
1718         }
1719         pos += 2;
1720         String text = extractText(data, pos);
1721         if (text == null) {
1722             Log.e(TAG, "Broken ExtendedChannelName.");
1723             return null;
1724         }
1725         return new ExtendedChannelNameDescriptor(text);
1726     }
1727 
parseGenre(byte[] data, int pos, int limit)1728     private static GenreDescriptor parseGenre(byte[] data, int pos, int limit) {
1729         pos += 2;
1730         int attributeCount = data[pos] & 0x1f;
1731         if (limit <= pos + attributeCount) {
1732             Log.e(TAG, "Broken Genre.");
1733             return null;
1734         }
1735         HashSet<String> broadcastGenreSet = new HashSet<>();
1736         HashSet<String> canonicalGenreSet = new HashSet<>();
1737         for (int i = 0; i < attributeCount; ++i) {
1738             ++pos;
1739             int genreCode = data[pos] & 0xff;
1740             if (genreCode < BROADCAST_GENRES_TABLE.length) {
1741                 String broadcastGenre = BROADCAST_GENRES_TABLE[genreCode];
1742                 if (broadcastGenre != null && !broadcastGenreSet.contains(broadcastGenre)) {
1743                     broadcastGenreSet.add(broadcastGenre);
1744                 }
1745             }
1746             if (genreCode < CANONICAL_GENRES_TABLE.length) {
1747                 String canonicalGenre = CANONICAL_GENRES_TABLE[genreCode];
1748                 if (canonicalGenre != null && !canonicalGenreSet.contains(canonicalGenre)) {
1749                     canonicalGenreSet.add(canonicalGenre);
1750                 }
1751             }
1752         }
1753         return new GenreDescriptor(
1754                 broadcastGenreSet.toArray(new String[broadcastGenreSet.size()]),
1755                 canonicalGenreSet.toArray(new String[canonicalGenreSet.size()]));
1756     }
1757 
parseAc3AudioStream(byte[] data, int pos, int limit)1758     private static TsDescriptor parseAc3AudioStream(byte[] data, int pos, int limit) {
1759         // For details of the AC3 audio stream descriptor, see A/52 Table A4.1.
1760         if (limit <= pos + 5) {
1761             Log.e(TAG, "Broken AC3 audio stream descriptor.");
1762             return null;
1763         }
1764         pos += 2;
1765         byte sampleRateCode = (byte) ((data[pos] & 0xe0) >> 5);
1766         byte bsid = (byte) (data[pos] & 0x1f);
1767         ++pos;
1768         byte bitRateCode = (byte) ((data[pos] & 0xfc) >> 2);
1769         byte surroundMode = (byte) (data[pos] & 0x03);
1770         ++pos;
1771         byte bsmod = (byte) ((data[pos] & 0xe0) >> 5);
1772         int numChannels = (data[pos] & 0x1e) >> 1;
1773         boolean fullSvc = (data[pos] & 0x01) != 0;
1774         ++pos;
1775         byte langCod = data[pos];
1776         byte langCod2 = 0;
1777         if (numChannels == 0) {
1778             if (limit <= pos) {
1779                 Log.e(TAG, "Broken AC3 audio stream descriptor.");
1780                 return null;
1781             }
1782             ++pos;
1783             langCod2 = data[pos];
1784         }
1785         if (limit <= pos + 1) {
1786             Log.e(TAG, "Broken AC3 audio stream descriptor.");
1787             return null;
1788         }
1789         byte mainId = 0;
1790         byte priority = 0;
1791         byte asvcflags = 0;
1792         ++pos;
1793         if (bsmod < 2) {
1794             mainId = (byte) ((data[pos] & 0xe0) >> 5);
1795             priority = (byte) ((data[pos] & 0x18) >> 3);
1796             if ((data[pos] & 0x07) != 0x07) {
1797                 Log.e(TAG, "Broken AC3 audio stream descriptor reserved failed");
1798                 return null;
1799             }
1800         } else {
1801             asvcflags = data[pos];
1802         }
1803 
1804         // See A/52B Table A3.6 num_channels.
1805         int numEncodedChannels;
1806         switch (numChannels) {
1807             case 1:
1808             case 8:
1809                 numEncodedChannels = 1;
1810                 break;
1811             case 2:
1812             case 9:
1813                 numEncodedChannels = 2;
1814                 break;
1815             case 3:
1816             case 4:
1817             case 10:
1818                 numEncodedChannels = 3;
1819                 break;
1820             case 5:
1821             case 6:
1822             case 11:
1823                 numEncodedChannels = 4;
1824                 break;
1825             case 7:
1826             case 12:
1827                 numEncodedChannels = 5;
1828                 break;
1829             case 13:
1830                 numEncodedChannels = 6;
1831                 break;
1832             default:
1833                 numEncodedChannels = 0;
1834                 break;
1835         }
1836 
1837         if (limit <= pos + 1) {
1838             Log.w(TAG, "Missing text and language fields on AC3 audio stream descriptor.");
1839             return new Ac3AudioDescriptor(
1840                     sampleRateCode,
1841                     bsid,
1842                     bitRateCode,
1843                     surroundMode,
1844                     bsmod,
1845                     numEncodedChannels,
1846                     fullSvc,
1847                     langCod,
1848                     langCod2,
1849                     mainId,
1850                     priority,
1851                     asvcflags,
1852                     null,
1853                     null,
1854                     null);
1855         }
1856         ++pos;
1857         int textLen = (data[pos] & 0xfe) >> 1;
1858         boolean textCode = (data[pos] & 0x01) != 0;
1859         ++pos;
1860         String text = "";
1861         if (textLen > 0) {
1862             if (limit < pos + textLen) {
1863                 Log.e(TAG, "Broken AC3 audio stream descriptor");
1864                 return null;
1865             }
1866             if (textCode) {
1867                 text = new String(data, pos, textLen);
1868             } else {
1869                 text = new String(data, pos, textLen, Charset.forName("UTF-16"));
1870             }
1871             pos += textLen;
1872         }
1873         String language = null;
1874         String language2 = null;
1875         if (pos < limit) {
1876             // Many AC3 audio stream descriptors skip the language fields.
1877             boolean languageFlag1 = (data[pos] & 0x80) != 0;
1878             boolean languageFlag2 = (data[pos] & 0x40) != 0;
1879             if ((data[pos] & 0x3f) != 0x3f) {
1880                 Log.e(TAG, "Broken AC3 audio stream descriptor");
1881                 return null;
1882             }
1883             if (pos + (languageFlag1 ? 3 : 0) + (languageFlag2 ? 3 : 0) > limit) {
1884                 Log.e(TAG, "Broken AC3 audio stream descriptor");
1885                 return null;
1886             }
1887             ++pos;
1888             if (languageFlag1) {
1889                 language = new String(data, pos, 3);
1890                 pos += 3;
1891             }
1892             if (languageFlag2) {
1893                 language2 = new String(data, pos, 3);
1894             }
1895         }
1896 
1897         return new Ac3AudioDescriptor(
1898                 sampleRateCode,
1899                 bsid,
1900                 bitRateCode,
1901                 surroundMode,
1902                 bsmod,
1903                 numEncodedChannels,
1904                 fullSvc,
1905                 langCod,
1906                 langCod2,
1907                 mainId,
1908                 priority,
1909                 asvcflags,
1910                 text,
1911                 language,
1912                 language2);
1913     }
1914 
parseDvbService(byte[] data, int pos, int limit)1915     private static TsDescriptor parseDvbService(byte[] data, int pos, int limit) {
1916         // For details of DVB service descriptors, see DVB Document A038 Table 86.
1917         if (limit < pos + 5) {
1918             Log.e(TAG, "Broken service descriptor.");
1919             return null;
1920         }
1921         pos += 2;
1922         int serviceType = data[pos] & 0xff;
1923         pos++;
1924         int serviceProviderNameLength = data[pos] & 0xff;
1925         pos++;
1926         String serviceProviderName = extractTextFromDvb(data, pos, serviceProviderNameLength);
1927         pos += serviceProviderNameLength;
1928         int serviceNameLength = data[pos] & 0xff;
1929         pos++;
1930         String serviceName = extractTextFromDvb(data, pos, serviceNameLength);
1931         return new ServiceDescriptor(serviceType, serviceProviderName, serviceName);
1932     }
1933 
parseDvbShortEvent(byte[] data, int pos, int limit)1934     private static TsDescriptor parseDvbShortEvent(byte[] data, int pos, int limit) {
1935         // For details of DVB service descriptors, see DVB Document A038 Table 91.
1936         if (limit < pos + 7) {
1937             Log.e(TAG, "Broken short event descriptor.");
1938             return null;
1939         }
1940         pos += 2;
1941         String language = new String(data, pos, 3);
1942         int eventNameLength = data[pos + 3] & 0xff;
1943         pos += 4;
1944         if (pos + eventNameLength > limit) {
1945             Log.e(TAG, "Broken short event descriptor.");
1946             return null;
1947         }
1948         String eventName = new String(data, pos, eventNameLength);
1949         pos += eventNameLength;
1950         int textLength = data[pos] & 0xff;
1951         if (pos + textLength > limit) {
1952             Log.e(TAG, "Broken short event descriptor.");
1953             return null;
1954         }
1955         pos++;
1956         String text = new String(data, pos, textLength);
1957         return new ShortEventDescriptor(language, eventName, text);
1958     }
1959 
parseDvbContent(byte[] data, int pos, int limit)1960     private static TsDescriptor parseDvbContent(byte[] data, int pos, int limit) {
1961         // TODO: According to DVB Document A038 Table 27 to add a parser for content descriptor to
1962         // get content genre.
1963         return null;
1964     }
1965 
parseDvbParentalRating(byte[] data, int pos, int limit)1966     private static TsDescriptor parseDvbParentalRating(byte[] data, int pos, int limit) {
1967         // For details of DVB service descriptors, see DVB Document A038 Table 81.
1968         HashMap<String, Integer> ratings = new HashMap<>();
1969         pos += 2;
1970         while (pos + 4 <= limit) {
1971             String countryCode = new String(data, pos, 3);
1972             int rating = data[pos + 3] & 0xff;
1973             pos += 4;
1974             if (rating > 15) {
1975                 // Rating > 15 means that the ratings is defined by broadcaster.
1976                 continue;
1977             }
1978             ratings.put(countryCode, rating + 3);
1979         }
1980         return new ParentalRatingDescriptor(ratings);
1981     }
1982 
getShortNameSize(byte[] data, int offset)1983     private static int getShortNameSize(byte[] data, int offset) {
1984         for (int i = 0; i < MAX_SHORT_NAME_BYTES; i += 2) {
1985             if (data[offset + i] == 0 && data[offset + i + 1] == 0) {
1986                 return i;
1987             }
1988         }
1989         return MAX_SHORT_NAME_BYTES;
1990     }
1991 
extractText(byte[] data, int pos)1992     private static String extractText(byte[] data, int pos) {
1993         if (data.length < pos) {
1994             return null;
1995         }
1996         int numStrings = data[pos] & 0xff;
1997         pos++;
1998         for (int i = 0; i < numStrings; ++i) {
1999             if (data.length <= pos + 3) {
2000                 Log.e(TAG, "Broken text.");
2001                 return null;
2002             }
2003             int numSegments = data[pos + 3] & 0xff;
2004             pos += 4;
2005             for (int j = 0; j < numSegments; ++j) {
2006                 if (data.length <= pos + 2) {
2007                     Log.e(TAG, "Broken text.");
2008                     return null;
2009                 }
2010                 int compressionType = data[pos] & 0xff;
2011                 int mode = data[pos + 1] & 0xff;
2012                 int numBytes = data[pos + 2] & 0xff;
2013                 if (data.length < pos + 3 + numBytes) {
2014                     Log.e(TAG, "Broken text.");
2015                     return null;
2016                 }
2017                 if (compressionType == COMPRESSION_TYPE_NO_COMPRESSION) {
2018                     switch (mode) {
2019                         case MODE_SELECTED_UNICODE_RANGE_1:
2020                             return new String(data, pos + 3, numBytes, StandardCharsets.ISO_8859_1);
2021                         case MODE_SCSU:
2022                             if (SCSU_CHARSET != null) {
2023                                 return new String(data, pos + 3, numBytes, SCSU_CHARSET);
2024                             } else {
2025                                 Log.w(TAG, "SCSU not supported");
2026                                 return null;
2027                             }
2028                         case MODE_UTF16:
2029                             return new String(data, pos + 3, numBytes, StandardCharsets.UTF_16);
2030                         default:
2031                             Log.w(TAG, "Unsupported text mode " + mode);
2032                             return null;
2033                     }
2034                 }
2035                 pos += 3 + numBytes;
2036             }
2037         }
2038         return null;
2039     }
2040 
extractTextFromDvb(byte[] data, int pos, int length)2041     private static String extractTextFromDvb(byte[] data, int pos, int length) {
2042         // For details of DVB character set selection, see DVB Document A038 Annex A.
2043         if (data.length < pos + length) {
2044             return null;
2045         }
2046         try {
2047             String charsetPrefix = "ISO-8859-";
2048             switch (data[0]) {
2049                 case 0x01:
2050                 case 0x02:
2051                 case 0x03:
2052                 case 0x04:
2053                 case 0x05:
2054                 case 0x06:
2055                 case 0x07:
2056                 case 0x09:
2057                 case 0x0A:
2058                 case 0x0B:
2059                     String charset = charsetPrefix + String.valueOf(data[0] & 0xff + 4);
2060                     return new String(data, pos, length, charset);
2061                 case 0x10:
2062                     if (length < 3) {
2063                         Log.e(TAG, "Broken DVB text");
2064                         return null;
2065                     }
2066                     int codeTable = data[pos + 2] & 0xff;
2067                     if (data[pos + 1] == 0 && codeTable > 0 && codeTable < 15) {
2068                         return new String(
2069                                 data, pos, length, charsetPrefix + String.valueOf(codeTable));
2070                     } else {
2071                         return new String(data, pos, length, "ISO-8859-1");
2072                     }
2073                 case 0x11:
2074                 case 0x14:
2075                 case 0x15:
2076                     return new String(data, pos, length, "UTF-16BE");
2077                 case 0x12:
2078                     return new String(data, pos, length, "EUC-KR");
2079                 case 0x13:
2080                     return new String(data, pos, length, "GB2312");
2081                 default:
2082                     return new String(data, pos, length, "ISO-8859-1");
2083             }
2084         } catch (UnsupportedEncodingException e) {
2085             Log.e(TAG, "Unsupported text format.", e);
2086         }
2087         return new String(data, pos, length);
2088     }
2089 
checkSanity(byte[] data)2090     private static boolean checkSanity(byte[] data) {
2091         // Skipping CRC checking on Archer since TS data here was modified without updating CRC
2092         // value. For details, see b/28616908.
2093         if (Model.ARCHER.isEnabled()) {
2094             return true;
2095         }
2096         if (data.length <= 1) {
2097             return false;
2098         }
2099         boolean hasCRC = (data[1] & 0x80) != 0; // section_syntax_indicator
2100         if (hasCRC) {
2101             int crc = 0xffffffff;
2102             for (byte b : data) {
2103                 int index = ((crc >> 24) ^ (b & 0xff)) & 0xff;
2104                 crc = CRC_TABLE[index] ^ (crc << 8);
2105             }
2106             if (crc != 0) {
2107                 return false;
2108             }
2109         }
2110         return true;
2111     }
2112 }
2113