1 /*
2  * Copyright (C) 2014 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 android.media.tv.cts;
18 
19 import android.content.ComponentName;
20 import android.content.ContentResolver;
21 import android.content.ContentUris;
22 import android.content.ContentValues;
23 import android.content.res.AssetFileDescriptor;
24 import android.database.Cursor;
25 import android.graphics.Bitmap;
26 import android.graphics.BitmapFactory;
27 import android.media.tv.TvContentRating;
28 import android.media.tv.TvContract;
29 import android.media.tv.TvContract.Channels;
30 import android.media.tv.TvContract.Programs.Genres;
31 import android.net.Uri;
32 import android.test.AndroidTestCase;
33 
34 import com.android.cts.tv.R;
35 
36 import java.io.InputStream;
37 import java.io.OutputStream;
38 import java.util.Arrays;
39 import java.util.List;
40 
41 /**
42  * Test for {@link android.media.tv.TvContract}.
43  */
44 public class TvContractTest extends AndroidTestCase {
45     private static final String[] CHANNELS_PROJECTION = {
46         TvContract.Channels._ID,
47         TvContract.Channels.COLUMN_INPUT_ID,
48         TvContract.Channels.COLUMN_TYPE,
49         TvContract.Channels.COLUMN_SERVICE_TYPE,
50         TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID,
51         TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID,
52         TvContract.Channels.COLUMN_SERVICE_ID,
53         TvContract.Channels.COLUMN_DISPLAY_NUMBER,
54         TvContract.Channels.COLUMN_DISPLAY_NAME,
55         TvContract.Channels.COLUMN_NETWORK_AFFILIATION,
56         TvContract.Channels.COLUMN_DESCRIPTION,
57         TvContract.Channels.COLUMN_VIDEO_FORMAT,
58         TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA,
59         TvContract.Channels.COLUMN_VERSION_NUMBER,
60     };
61 
62     private static final String[] PROGRAMS_PROJECTION = {
63         TvContract.Programs._ID,
64         TvContract.Programs.COLUMN_CHANNEL_ID,
65         TvContract.Programs.COLUMN_TITLE,
66         TvContract.Programs.COLUMN_SEASON_NUMBER,
67         TvContract.Programs.COLUMN_EPISODE_NUMBER,
68         TvContract.Programs.COLUMN_EPISODE_TITLE,
69         TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
70         TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
71         TvContract.Programs.COLUMN_BROADCAST_GENRE,
72         TvContract.Programs.COLUMN_CANONICAL_GENRE,
73         TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
74         TvContract.Programs.COLUMN_LONG_DESCRIPTION,
75         TvContract.Programs.COLUMN_VIDEO_WIDTH,
76         TvContract.Programs.COLUMN_VIDEO_HEIGHT,
77         TvContract.Programs.COLUMN_AUDIO_LANGUAGE,
78         TvContract.Programs.COLUMN_CONTENT_RATING,
79         TvContract.Programs.COLUMN_POSTER_ART_URI,
80         TvContract.Programs.COLUMN_THUMBNAIL_URI,
81         TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA,
82         TvContract.Programs.COLUMN_VERSION_NUMBER,
83     };
84 
85     private static long OPERATION_TIME = 1000l;
86 
87     private static final String ENCODED_GENRE_STRING = Genres.ANIMAL_WILDLIFE + "," + Genres.COMEDY
88             + "," + Genres.DRAMA + "," + Genres.EDUCATION + "," + Genres.FAMILY_KIDS + ","
89             + Genres.GAMING + "," + Genres.MOVIES + "," + Genres.NEWS + "," + Genres.SHOPPING + ","
90             + Genres.SPORTS + "," + Genres.TRAVEL;
91 
92     private String mInputId;
93     private ContentResolver mContentResolver;
94     private Uri mChannelsUri;
95 
96     @Override
setUp()97     protected void setUp() throws Exception {
98         super.setUp();
99         if (!Utils.hasTvInputFramework(getContext())) {
100             return;
101         }
102         mInputId = TvContract.buildInputId(
103                 new ComponentName(getContext(), StubTunerTvInputService.class));
104         mContentResolver = getContext().getContentResolver();
105         mChannelsUri = TvContract.buildChannelsUriForInput(mInputId);
106     }
107 
108     @Override
tearDown()109     protected void tearDown() throws Exception {
110         if (!Utils.hasTvInputFramework(getContext())) {
111             super.tearDown();
112             return;
113         }
114         // Clean up, just in case we failed to delete the entry when a test failed.
115         // The cotentUris are specific to this package, so this will delete only the
116         // entries inserted by this package.
117         String[] projection = { TvContract.Channels._ID };
118         try (Cursor cursor = mContentResolver.query(mChannelsUri, projection, null, null, null)) {
119             while (cursor != null && cursor.moveToNext()) {
120                 long channelId = cursor.getLong(0);
121                 mContentResolver.delete(
122                         TvContract.buildProgramsUriForChannel(channelId), null, null);
123             }
124         }
125         mContentResolver.delete(mChannelsUri, null, null);
126         super.tearDown();
127     }
128 
createDummyChannelValues(String inputId)129     private static ContentValues createDummyChannelValues(String inputId) {
130         ContentValues values = new ContentValues();
131         values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
132         values.put(TvContract.Channels.COLUMN_TYPE, TvContract.Channels.TYPE_OTHER);
133         values.put(TvContract.Channels.COLUMN_SERVICE_TYPE,
134                 TvContract.Channels.SERVICE_TYPE_AUDIO_VIDEO);
135         values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "1");
136         values.put(TvContract.Channels.COLUMN_VIDEO_FORMAT, TvContract.Channels.VIDEO_FORMAT_480P);
137 
138         return values;
139     }
140 
createDummyProgramValues(long channelId)141     private static ContentValues createDummyProgramValues(long channelId) {
142         ContentValues values = new ContentValues();
143         values.put(TvContract.Programs.COLUMN_CHANNEL_ID, channelId);
144         values.put(TvContract.Programs.COLUMN_EPISODE_TITLE, "Title");
145         values.put(TvContract.Programs.COLUMN_CANONICAL_GENRE, TvContract.Programs.Genres.encode(
146                 TvContract.Programs.Genres.MOVIES, TvContract.Programs.Genres.DRAMA));
147         TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
148                 "US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
149         values.put(TvContract.Programs.COLUMN_CONTENT_RATING, rating.flattenToString());
150 
151         return values;
152     }
153 
verifyStringColumn(Cursor cursor, ContentValues expectedValues, String columnName)154     private static void verifyStringColumn(Cursor cursor, ContentValues expectedValues,
155             String columnName) {
156         if (expectedValues.containsKey(columnName)) {
157             assertEquals(expectedValues.getAsString(columnName),
158                     cursor.getString(cursor.getColumnIndex(columnName)));
159         }
160     }
161 
verifyIntegerColumn(Cursor cursor, ContentValues expectedValues, String columnName)162     private static void verifyIntegerColumn(Cursor cursor, ContentValues expectedValues,
163             String columnName) {
164         if (expectedValues.containsKey(columnName)) {
165             assertEquals(expectedValues.getAsInteger(columnName).intValue(),
166                     cursor.getInt(cursor.getColumnIndex(columnName)));
167         }
168     }
169 
verifyLongColumn(Cursor cursor, ContentValues expectedValues, String columnName)170     private static void verifyLongColumn(Cursor cursor, ContentValues expectedValues,
171             String columnName) {
172         if (expectedValues.containsKey(columnName)) {
173             assertEquals(expectedValues.getAsLong(columnName).longValue(),
174                     cursor.getLong(cursor.getColumnIndex(columnName)));
175         }
176     }
177 
verifyBlobColumn(Cursor cursor, ContentValues expectedValues, String columnName)178     private static void verifyBlobColumn(Cursor cursor, ContentValues expectedValues,
179             String columnName) {
180         if (expectedValues.containsKey(columnName)) {
181             byte[] expected = expectedValues.getAsByteArray(columnName);
182             byte[] actual = cursor.getBlob(cursor.getColumnIndex(columnName));
183             assertEquals(expected.length, actual.length);
184             for (int i = 0; i < expected.length; ++i) {
185                 assertEquals(expected[i], actual[i]);
186             }
187         }
188     }
189 
verifyChannel(Uri channelUri, ContentValues expectedValues, long channelId)190     private void verifyChannel(Uri channelUri, ContentValues expectedValues, long channelId) {
191         try (Cursor cursor = mContentResolver.query(
192                 channelUri, CHANNELS_PROJECTION, null, null, null)) {
193             assertNotNull(cursor);
194             assertEquals(cursor.getCount(), 1);
195             assertTrue(cursor.moveToNext());
196             assertEquals(channelId, cursor.getLong(cursor.getColumnIndex(TvContract.Channels._ID)));
197             verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_INPUT_ID);
198             verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_TYPE);
199             verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_SERVICE_TYPE);
200             verifyIntegerColumn(cursor, expectedValues,
201                     TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID);
202             verifyIntegerColumn(cursor, expectedValues,
203                     TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID);
204             verifyIntegerColumn(cursor, expectedValues,
205                     TvContract.Channels.COLUMN_SERVICE_ID);
206             verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_DISPLAY_NUMBER);
207             verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_DISPLAY_NAME);
208             verifyStringColumn(cursor, expectedValues,
209                     TvContract.Channels.COLUMN_NETWORK_AFFILIATION);
210             verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_DESCRIPTION);
211             verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_VIDEO_FORMAT);
212             verifyBlobColumn(cursor, expectedValues,
213                     TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA);
214             verifyIntegerColumn(cursor, expectedValues, TvContract.Channels.COLUMN_VERSION_NUMBER);
215         }
216     }
217 
testChannelsTable()218     public void testChannelsTable() throws Exception {
219         if (!Utils.hasTvInputFramework(getContext())) {
220             return;
221         }
222         // Test: insert
223         ContentValues values = createDummyChannelValues(mInputId);
224 
225         Uri rowUri = mContentResolver.insert(mChannelsUri, values);
226         long channelId = ContentUris.parseId(rowUri);
227         Uri channelUri = TvContract.buildChannelUri(channelId);
228         verifyChannel(channelUri, values, channelId);
229 
230         // Test: update
231         values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "1-1");
232         values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "One dash one");
233         values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes());
234 
235         mContentResolver.update(channelUri, values, null, null);
236         verifyChannel(channelUri, values, channelId);
237 
238         // Test: delete
239         mContentResolver.delete(mChannelsUri, null, null);
240         try (Cursor cursor = mContentResolver.query(
241                 mChannelsUri, CHANNELS_PROJECTION, null, null, null)) {
242             assertEquals(0, cursor.getCount());
243         }
244     }
245 
verifyProgram(Uri programUri, ContentValues expectedValues, long programId)246     private void verifyProgram(Uri programUri, ContentValues expectedValues, long programId) {
247         try (Cursor cursor = mContentResolver.query(
248                 programUri, PROGRAMS_PROJECTION, null, null, null)) {
249             assertNotNull(cursor);
250             assertEquals(cursor.getCount(), 1);
251             assertTrue(cursor.moveToNext());
252             assertEquals(programId, cursor.getLong(cursor.getColumnIndex(TvContract.Programs._ID)));
253             verifyLongColumn(cursor, expectedValues, TvContract.Programs.COLUMN_CHANNEL_ID);
254             verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_TITLE);
255             verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_SEASON_NUMBER);
256             verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_EPISODE_NUMBER);
257             verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_EPISODE_TITLE);
258             verifyLongColumn(cursor, expectedValues,
259                     TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS);
260             verifyLongColumn(cursor, expectedValues,
261                     TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS);
262             verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_BROADCAST_GENRE);
263             verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_CANONICAL_GENRE);
264             verifyStringColumn(cursor, expectedValues,
265                     TvContract.Programs.COLUMN_SHORT_DESCRIPTION);
266             verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_LONG_DESCRIPTION);
267             verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VIDEO_WIDTH);
268             verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VIDEO_HEIGHT);
269             verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_AUDIO_LANGUAGE);
270             verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_CONTENT_RATING);
271             verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_POSTER_ART_URI);
272             verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_THUMBNAIL_URI);
273             verifyBlobColumn(cursor, expectedValues,
274                     TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA);
275             verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VERSION_NUMBER);
276         }
277     }
278 
verifyLogoIsReadable(Uri logoUri)279     private void verifyLogoIsReadable(Uri logoUri) throws Exception {
280         try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "r")) {
281             try (InputStream is = fd.createInputStream()) {
282                 // Assure that the stream is decodable as a Bitmap.
283                 BitmapFactory.decodeStream(is);
284             }
285         }
286     }
287 
testChannelLogo()288     public void testChannelLogo() throws Exception {
289         if (!Utils.hasTvInputFramework(getContext())) {
290             return;
291         }
292         // Set-up: add a channel.
293         ContentValues values = createDummyChannelValues(mInputId);
294         Uri channelUri = mContentResolver.insert(mChannelsUri, values);
295         Uri logoUri = TvContract.buildChannelLogoUri(channelUri);
296         Bitmap logo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.robot);
297 
298         // Write
299         try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "w")) {
300             try (OutputStream os = fd.createOutputStream()) {
301                 logo.compress(Bitmap.CompressFormat.PNG, 100, os);
302             }
303         }
304 
305         // Give some time for TvProvider to process the logo.
306         Thread.sleep(OPERATION_TIME);
307 
308         // Read and verify
309         verifyLogoIsReadable(logoUri);
310 
311         // Read and verify using alternative logo URI.
312         verifyLogoIsReadable(TvContract.buildChannelLogoUri(ContentUris.parseId(channelUri)));
313     }
314 
verifyProgramsTable(Uri programsUri, long channelId)315     public void verifyProgramsTable(Uri programsUri, long channelId) {
316         if (!Utils.hasTvInputFramework(getContext())) {
317             return;
318         }
319         // Test: insert
320         ContentValues values = createDummyProgramValues(channelId);
321 
322         Uri rowUri = mContentResolver.insert(programsUri, values);
323         long programId = ContentUris.parseId(rowUri);
324         Uri programUri = TvContract.buildProgramUri(programId);
325         verifyProgram(programUri, values, programId);
326 
327         // Test: update
328         values.put(TvContract.Programs.COLUMN_EPISODE_TITLE, "Sample title");
329         values.put(TvContract.Programs.COLUMN_SHORT_DESCRIPTION, "Short description");
330         values.put(TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes());
331 
332         mContentResolver.update(programUri, values, null, null);
333         verifyProgram(programUri, values, programId);
334 
335         // Test: delete
336         mContentResolver.delete(programsUri, null, null);
337         try (Cursor cursor = mContentResolver.query(
338                 programsUri, PROGRAMS_PROJECTION, null, null, null)) {
339             assertEquals(0, cursor.getCount());
340         }
341     }
342 
testProgramsTable()343     public void testProgramsTable() throws Exception {
344         if (!Utils.hasTvInputFramework(getContext())) {
345             return;
346         }
347         // Set-up: add a channel.
348         ContentValues values = createDummyChannelValues(mInputId);
349         Uri channelUri = mContentResolver.insert(mChannelsUri, values);
350         long channelId = ContentUris.parseId(channelUri);
351 
352         verifyProgramsTable(TvContract.buildProgramsUriForChannel(channelId), channelId);
353         verifyProgramsTable(TvContract.buildProgramsUriForChannel(channelUri), channelId);
354     }
355 
verifyOverlap(long startMillis, long endMillis, int expectedCount, long channelId, Uri channelUri)356     private void verifyOverlap(long startMillis, long endMillis, int expectedCount,
357             long channelId, Uri channelUri) {
358         try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(
359                 channelId, startMillis, endMillis), PROGRAMS_PROJECTION, null, null, null)) {
360             assertEquals(expectedCount, cursor.getCount());
361         }
362         try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(
363                 channelUri, startMillis, endMillis), PROGRAMS_PROJECTION, null, null, null)) {
364             assertEquals(expectedCount, cursor.getCount());
365         }
366     }
367 
testProgramsScheduleOverlap()368     public void testProgramsScheduleOverlap() throws Exception {
369         if (!Utils.hasTvInputFramework(getContext())) {
370             return;
371         }
372         final long programStartMillis = 1403712000000l;  // Jun 25 2014 16:00 UTC
373         final long programEndMillis = 1403719200000l;  // Jun 25 2014 18:00 UTC
374         final long hour = 3600000l;
375 
376         // Set-up: add a channel and program.
377         ContentValues values = createDummyChannelValues(mInputId);
378         Uri channelUri = mContentResolver.insert(mChannelsUri, values);
379         long channelId = ContentUris.parseId(channelUri);
380         Uri programsUri = TvContract.buildProgramsUriForChannel(channelId);
381         values = createDummyProgramValues(channelId);
382         values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartMillis);
383         values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, programEndMillis);
384         mContentResolver.insert(programsUri, values);
385 
386         // Overlap 1: starts early, ends early.
387         verifyOverlap(programStartMillis - hour, programEndMillis - hour, 1, channelId, channelUri);
388 
389         // Overlap 2: starts early, ends late.
390         verifyOverlap(programStartMillis - hour, programEndMillis + hour, 1, channelId, channelUri);
391 
392         // Overlap 3: starts early, ends late.
393         verifyOverlap(programStartMillis + hour / 2, programEndMillis - hour / 2, 1,
394                 channelId, channelUri);
395 
396         // Overlap 4: starts late, ends late.
397         verifyOverlap(programStartMillis + hour, programEndMillis + hour, 1, channelId, channelUri);
398 
399         // Non-overlap 1: ends too early.
400         verifyOverlap(programStartMillis - hour, programStartMillis - hour / 2, 0,
401                 channelId, channelUri);
402 
403         // Non-overlap 2: starts too late
404         verifyOverlap(programEndMillis + hour, programEndMillis + hour * 2, 0,
405                 channelId, channelUri);
406     }
407 
verifyQueryWithSortOrder(Uri uri, final String[] projection, String sortOrder)408     private void verifyQueryWithSortOrder(Uri uri, final String[] projection,
409             String sortOrder) throws Exception {
410         try {
411             getContext().getContentResolver().query(uri, projection, null, null, sortOrder);
412         } catch (SecurityException e) {
413             fail("Setting sort order shoud be allowed for " + uri);
414         }
415     }
416 
verifyQueryWithSelection(Uri uri, final String[] projection, String selection)417     private void verifyQueryWithSelection(Uri uri, final String[] projection,
418             String selection) throws Exception {
419         try {
420             getContext().getContentResolver().query(uri, projection, selection, null, null);
421             fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
422         } catch (SecurityException e) {
423             // Expected exception
424         }
425     }
426 
verifyUpdateWithSelection(Uri uri, String selection)427     private void verifyUpdateWithSelection(Uri uri, String selection) throws Exception {
428         try {
429             ContentValues values = new ContentValues();
430             getContext().getContentResolver().update(uri, values, selection, null);
431             fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
432         } catch (SecurityException e) {
433             // Expected exception
434         }
435     }
436 
verifyDeleteWithSelection(Uri uri, String selection)437     private void verifyDeleteWithSelection(Uri uri, String selection) throws Exception {
438         try {
439             getContext().getContentResolver().delete(uri, selection, null);
440             fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
441         } catch (SecurityException e) {
442             // Expected exception
443         }
444     }
445 
testAllEpgPermissionBlocksSortOrderOnQuery_Channels()446     public void testAllEpgPermissionBlocksSortOrderOnQuery_Channels() throws Exception {
447         if (!Utils.hasTvInputFramework(getContext())) {
448             return;
449         }
450         final String[] projection = { TvContract.Channels._ID };
451         verifyQueryWithSortOrder(TvContract.Channels.CONTENT_URI, projection,
452                 TvContract.Channels._ID + " ASC");
453     }
454 
testAllEpgPermissionBlocksSelectionOnQuery_Channels()455     public void testAllEpgPermissionBlocksSelectionOnQuery_Channels() throws Exception {
456         if (!Utils.hasTvInputFramework(getContext())) {
457             return;
458         }
459         final String[] projection = { TvContract.Channels._ID };
460         verifyQueryWithSelection(TvContract.Channels.CONTENT_URI, projection,
461                 TvContract.Channels._ID + ">0");
462     }
463 
testAllEpgPermissionBlocksSelectionOnUpdate_Channels()464     public void testAllEpgPermissionBlocksSelectionOnUpdate_Channels() throws Exception {
465         if (!Utils.hasTvInputFramework(getContext())) {
466             return;
467         }
468         verifyUpdateWithSelection(TvContract.Channels.CONTENT_URI,
469                 TvContract.Channels._ID + ">0");
470     }
471 
testAllEpgPermissionBlocksSelectionOnDelete_Channels()472     public void testAllEpgPermissionBlocksSelectionOnDelete_Channels() throws Exception {
473         if (!Utils.hasTvInputFramework(getContext())) {
474             return;
475         }
476         verifyDeleteWithSelection(TvContract.Channels.CONTENT_URI,
477                 TvContract.Channels._ID + ">0");
478     }
479 
testAllEpgPermissionBlocksSortOrderOnQuery_Programs()480     public void testAllEpgPermissionBlocksSortOrderOnQuery_Programs() throws Exception {
481         if (!Utils.hasTvInputFramework(getContext())) {
482             return;
483         }
484         final String[] projection = { TvContract.Programs._ID };
485         verifyQueryWithSortOrder(TvContract.Programs.CONTENT_URI, projection,
486                 TvContract.Programs._ID + " ASC");
487     }
488 
testAllEpgPermissionBlocksSelectionOnQuery_Programs()489     public void testAllEpgPermissionBlocksSelectionOnQuery_Programs() throws Exception {
490         if (!Utils.hasTvInputFramework(getContext())) {
491             return;
492         }
493         final String[] projection = { TvContract.Channels._ID };
494         verifyQueryWithSelection(TvContract.Programs.CONTENT_URI, projection,
495                 TvContract.Programs._ID + ">0");
496     }
497 
testAllEpgPermissionBlocksSelectionOnUpdate_Programs()498     public void testAllEpgPermissionBlocksSelectionOnUpdate_Programs() throws Exception {
499         if (!Utils.hasTvInputFramework(getContext())) {
500             return;
501         }
502         verifyUpdateWithSelection(TvContract.Programs.CONTENT_URI,
503                 TvContract.Programs._ID + ">0");
504     }
505 
testAllEpgPermissionBlocksSelectionOnDelete_Programs()506     public void testAllEpgPermissionBlocksSelectionOnDelete_Programs() throws Exception {
507         if (!Utils.hasTvInputFramework(getContext())) {
508             return;
509         }
510         verifyDeleteWithSelection(TvContract.Programs.CONTENT_URI,
511                 TvContract.Programs._ID + ">0");
512     }
513 
testDefaultValues()514     public void testDefaultValues() throws Exception {
515         if (!Utils.hasTvInputFramework(getContext())) {
516             return;
517         }
518         ContentValues values = new ContentValues();
519         values.put(TvContract.Channels.COLUMN_INPUT_ID, mInputId);
520         Uri channelUri = mContentResolver.insert(mChannelsUri, values);
521         assertNotNull(channelUri);
522         try (Cursor cursor = mContentResolver.query(
523                 channelUri, CHANNELS_PROJECTION, null, null, null)) {
524             cursor.moveToNext();
525             assertEquals(TvContract.Channels.TYPE_OTHER,
526                     cursor.getString(cursor.getColumnIndex(TvContract.Channels.COLUMN_TYPE)));
527             assertEquals(TvContract.Channels.SERVICE_TYPE_AUDIO_VIDEO,
528                     cursor.getString(cursor.getColumnIndex(
529                             TvContract.Channels.COLUMN_SERVICE_TYPE)));
530         }
531         values.clear();
532     }
533 
testChannelsGetVideoResolution()534     public void testChannelsGetVideoResolution() {
535         if (!Utils.hasTvInputFramework(getContext())) {
536             return;
537         }
538         assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution(
539                 Channels.VIDEO_FORMAT_480I));
540         assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution(
541                 Channels.VIDEO_FORMAT_480P));
542         assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution(
543                 Channels.VIDEO_FORMAT_576I));
544         assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution(
545                 Channels.VIDEO_FORMAT_576P));
546         assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution(
547                 Channels.VIDEO_FORMAT_720P));
548         assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution(
549                 Channels.VIDEO_FORMAT_1080I));
550         assertEquals(Channels.VIDEO_RESOLUTION_FHD, Channels.getVideoResolution(
551                 Channels.VIDEO_FORMAT_1080P));
552         assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution(
553                 Channels.VIDEO_FORMAT_2160P));
554         assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution(
555                 Channels.VIDEO_FORMAT_4320P));
556         assertEquals(null, Channels.getVideoResolution("Unknown format"));
557     }
558 
testProgramsGenresDecode()559     public void testProgramsGenresDecode() {
560         if (!Utils.hasTvInputFramework(getContext())) {
561             return;
562         }
563         List genres = Arrays.asList(Genres.decode(ENCODED_GENRE_STRING));
564         assertEquals(11, genres.size());
565         assertTrue(genres.contains(Genres.ANIMAL_WILDLIFE));
566         assertTrue(genres.contains(Genres.COMEDY));
567         assertTrue(genres.contains(Genres.DRAMA));
568         assertTrue(genres.contains(Genres.EDUCATION));
569         assertTrue(genres.contains(Genres.FAMILY_KIDS));
570         assertTrue(genres.contains(Genres.GAMING));
571         assertTrue(genres.contains(Genres.MOVIES));
572         assertTrue(genres.contains(Genres.NEWS));
573         assertTrue(genres.contains(Genres.SHOPPING));
574         assertTrue(genres.contains(Genres.SPORTS));
575         assertTrue(genres.contains(Genres.TRAVEL));
576         assertFalse(genres.contains(","));
577     }
578 
testProgramsGenresEncode()579     public void testProgramsGenresEncode() {
580         if (!Utils.hasTvInputFramework(getContext())) {
581             return;
582         }
583         assertEquals(ENCODED_GENRE_STRING, Genres.encode(Genres.ANIMAL_WILDLIFE,
584                 Genres.COMEDY, Genres.DRAMA, Genres.EDUCATION, Genres.FAMILY_KIDS, Genres.GAMING,
585                 Genres.MOVIES, Genres.NEWS, Genres.SHOPPING, Genres.SPORTS, Genres.TRAVEL));
586     }
587 }
588