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.PreviewPrograms; 31 import android.media.tv.TvContract.Programs; 32 import android.media.tv.TvContract.Programs.Genres; 33 import android.media.tv.TvContract.RecordedPrograms; 34 import android.media.tv.TvContract.WatchNextPrograms; 35 import android.net.Uri; 36 import android.test.AndroidTestCase; 37 import android.test.MoreAsserts; 38 import android.tv.cts.R; 39 40 import java.io.InputStream; 41 import java.io.OutputStream; 42 import java.util.Arrays; 43 import java.util.List; 44 45 /** 46 * Test for {@link android.media.tv.TvContract}. 47 */ 48 public class TvContractTest extends AndroidTestCase { 49 private static final String[] CHANNELS_PROJECTION = { 50 Channels._ID, 51 Channels.COLUMN_INPUT_ID, 52 Channels.COLUMN_TYPE, 53 Channels.COLUMN_SERVICE_TYPE, 54 Channels.COLUMN_ORIGINAL_NETWORK_ID, 55 Channels.COLUMN_TRANSPORT_STREAM_ID, 56 Channels.COLUMN_SERVICE_ID, 57 Channels.COLUMN_DISPLAY_NUMBER, 58 Channels.COLUMN_DISPLAY_NAME, 59 Channels.COLUMN_NETWORK_AFFILIATION, 60 Channels.COLUMN_DESCRIPTION, 61 Channels.COLUMN_VIDEO_FORMAT, 62 Channels.COLUMN_INTERNAL_PROVIDER_DATA, 63 Channels.COLUMN_VERSION_NUMBER, 64 Channels.COLUMN_INTERNAL_PROVIDER_ID, 65 }; 66 67 private static final String[] PROGRAMS_PROJECTION = { 68 Programs._ID, 69 Programs.COLUMN_CHANNEL_ID, 70 Programs.COLUMN_TITLE, 71 Programs.COLUMN_SEASON_DISPLAY_NUMBER, 72 Programs.COLUMN_SEASON_TITLE, 73 Programs.COLUMN_EPISODE_DISPLAY_NUMBER, 74 Programs.COLUMN_EPISODE_TITLE, 75 Programs.COLUMN_START_TIME_UTC_MILLIS, 76 Programs.COLUMN_END_TIME_UTC_MILLIS, 77 Programs.COLUMN_BROADCAST_GENRE, 78 Programs.COLUMN_CANONICAL_GENRE, 79 Programs.COLUMN_SHORT_DESCRIPTION, 80 Programs.COLUMN_LONG_DESCRIPTION, 81 Programs.COLUMN_VIDEO_WIDTH, 82 Programs.COLUMN_VIDEO_HEIGHT, 83 Programs.COLUMN_AUDIO_LANGUAGE, 84 Programs.COLUMN_CONTENT_RATING, 85 Programs.COLUMN_POSTER_ART_URI, 86 Programs.COLUMN_THUMBNAIL_URI, 87 Programs.COLUMN_INTERNAL_PROVIDER_DATA, 88 Programs.COLUMN_VERSION_NUMBER, 89 }; 90 91 private static long OPERATION_TIME = 1000l; 92 93 private static final String ENCODED_GENRE_STRING = Genres.ANIMAL_WILDLIFE + "," + Genres.COMEDY 94 + "," + Genres.DRAMA + "," + Genres.EDUCATION + "," + Genres.FAMILY_KIDS + "," 95 + Genres.GAMING + "," + Genres.MOVIES + "," + Genres.NEWS + "," + Genres.SHOPPING + "," 96 + Genres.SPORTS + "," + Genres.TRAVEL; 97 98 // Delimiter for genre. 99 private static final String DELIMITER = ","; 100 private static final String EMPTY_GENRE = ""; 101 private static final String COMMA = ","; 102 private static final String COMMA_ENCODED = "\","; 103 private static final String QUOTE = "\""; 104 private static final String QUOTE_ENCODED = "\"\""; 105 private static final String WHITE_SPACES = " \r \n \t \f "; 106 107 private static final String PARAM_CANONICAL_GENRE = "canonical_genre"; 108 private static final String NON_EXISTING_COLUMN_NAME = "non_existing_column"; 109 110 private String mInputId; 111 private ContentResolver mContentResolver; 112 private Uri mChannelsUri; 113 114 @Override 115 protected void setUp() throws Exception { 116 super.setUp(); 117 if (!Utils.hasTvInputFramework(getContext())) { 118 return; 119 } 120 mInputId = TvContract.buildInputId( 121 new ComponentName(getContext(), StubTunerTvInputService.class)); 122 mContentResolver = getContext().getContentResolver(); 123 mChannelsUri = TvContract.buildChannelsUriForInput(mInputId); 124 } 125 126 @Override 127 protected void tearDown() throws Exception { 128 if (!Utils.hasTvInputFramework(getContext())) { 129 super.tearDown(); 130 return; 131 } 132 mContentResolver.delete(Channels.CONTENT_URI, null, null); 133 mContentResolver.delete(RecordedPrograms.CONTENT_URI, null, null); 134 mContentResolver.delete(WatchNextPrograms.CONTENT_URI, null, null); 135 super.tearDown(); 136 } 137 138 private static ContentValues createDummyChannelValues(String inputId, boolean preview) { 139 ContentValues values = new ContentValues(); 140 values.put(Channels.COLUMN_INPUT_ID, inputId); 141 values.put(Channels.COLUMN_INTERNAL_PROVIDER_ID, "ID-4321"); 142 values.put(Channels.COLUMN_TYPE, preview ? Channels.TYPE_PREVIEW : Channels.TYPE_OTHER); 143 values.put(Channels.COLUMN_SERVICE_TYPE, Channels.SERVICE_TYPE_AUDIO_VIDEO); 144 values.put(Channels.COLUMN_DISPLAY_NUMBER, "1"); 145 values.put(Channels.COLUMN_VIDEO_FORMAT, Channels.VIDEO_FORMAT_480P); 146 147 return values; 148 } 149 150 private static ContentValues createDummyProgramValues(long channelId) { 151 ContentValues values = new ContentValues(); 152 values.put(Programs.COLUMN_CHANNEL_ID, channelId); 153 values.put(Programs.COLUMN_EPISODE_DISPLAY_NUMBER , "1A"); 154 values.put(Programs.COLUMN_EPISODE_TITLE, "episode_title"); 155 values.put(Programs.COLUMN_SEASON_DISPLAY_NUMBER , "2B"); 156 values.put(Programs.COLUMN_SEASON_TITLE, "season_title"); 157 values.put(Programs.COLUMN_CANONICAL_GENRE, Programs.Genres.encode( 158 Programs.Genres.MOVIES, Programs.Genres.DRAMA)); 159 TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG", 160 "US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V"); 161 values.put(Programs.COLUMN_CONTENT_RATING, rating.flattenToString()); 162 values.put(Programs.COLUMN_REVIEW_RATING_STYLE, 163 RecordedPrograms.REVIEW_RATING_STYLE_STARS); 164 values.put(Programs.COLUMN_REVIEW_RATING, "4.5"); 165 166 return values; 167 } 168 169 private static ContentValues createDummyPreviewProgramValues(long channelId) { 170 ContentValues values = createDummyBasePreviewProgramValues(); 171 values.put(PreviewPrograms.COLUMN_CHANNEL_ID, channelId); 172 values.put(PreviewPrograms.COLUMN_WEIGHT, 100); 173 return values; 174 } 175 176 private static ContentValues createDummyWatchNextProgramValues() { 177 ContentValues values = createDummyBasePreviewProgramValues(); 178 values.put(WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE, 179 WatchNextPrograms.WATCH_NEXT_TYPE_CONTINUE); 180 values.put(WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS, 181 System.currentTimeMillis()); 182 return values; 183 } 184 185 private static ContentValues createDummyBasePreviewProgramValues() { 186 ContentValues values = new ContentValues(); 187 values.put(PreviewPrograms.COLUMN_INTERNAL_PROVIDER_ID, "ID-4321"); 188 values.put(PreviewPrograms.COLUMN_PREVIEW_VIDEO_URI, "http://test.com/preview.mp4"); 189 values.put(PreviewPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS, 5000); 190 values.put(PreviewPrograms.COLUMN_DURATION_MILLIS, 60000); 191 values.put(PreviewPrograms.COLUMN_INTENT_URI, "intent_for_video"); 192 values.put(PreviewPrograms.COLUMN_TITLE, "program_title"); 193 values.put(PreviewPrograms.COLUMN_SHORT_DESCRIPTION, "short_description"); 194 values.put(PreviewPrograms.COLUMN_EPISODE_DISPLAY_NUMBER , "1A"); 195 values.put(PreviewPrograms.COLUMN_EPISODE_TITLE, "episode_title"); 196 values.put(PreviewPrograms.COLUMN_SEASON_DISPLAY_NUMBER , "2B"); 197 values.put(PreviewPrograms.COLUMN_SEASON_TITLE, "season_title"); 198 values.put(PreviewPrograms.COLUMN_CANONICAL_GENRE, Programs.Genres.encode( 199 Programs.Genres.SPORTS, Programs.Genres.DRAMA)); 200 TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG", 201 "US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V"); 202 values.put(PreviewPrograms.COLUMN_CONTENT_RATING, rating.flattenToString()); 203 values.put(PreviewPrograms.COLUMN_TYPE, PreviewPrograms.TYPE_MOVIE); 204 values.put(PreviewPrograms.COLUMN_POSTER_ART_URI, "http://foo.com/artwork.png"); 205 values.put(PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO, 206 PreviewPrograms.ASPECT_RATIO_2_3); 207 values.put(PreviewPrograms.COLUMN_THUMBNAIL_URI, "http://foo.com/thumbnail.jpg"); 208 values.put(PreviewPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO, 209 PreviewPrograms.ASPECT_RATIO_16_9); 210 values.put(PreviewPrograms.COLUMN_LOGO_URI, "http://foo.com/logo.jpg"); 211 values.put(PreviewPrograms.COLUMN_AVAILABILITY, PreviewPrograms.AVAILABILITY_AVAILABLE); 212 values.put(PreviewPrograms.COLUMN_STARTING_PRICE, "10.99 USD"); 213 values.put(PreviewPrograms.COLUMN_OFFER_PRICE, "3.99 USD"); 214 values.put(PreviewPrograms.COLUMN_RELEASE_DATE, "1985"); 215 values.put(PreviewPrograms.COLUMN_ITEM_COUNT, 1); 216 values.put(PreviewPrograms.COLUMN_LIVE, 0); 217 values.put(PreviewPrograms.COLUMN_INTERACTION_TYPE, PreviewPrograms.INTERACTION_TYPE_LIKES); 218 values.put(PreviewPrograms.COLUMN_INTERACTION_COUNT, 4000); 219 values.put(PreviewPrograms.COLUMN_AUTHOR, "author_name1"); 220 values.put(PreviewPrograms.COLUMN_REVIEW_RATING_STYLE, 221 PreviewPrograms.REVIEW_RATING_STYLE_STARS); 222 values.put(PreviewPrograms.COLUMN_REVIEW_RATING, "4.5"); 223 values.put(WatchNextPrograms.COLUMN_CONTENT_ID, "CID-125-6335"); 224 225 return values; 226 } 227 228 229 private static ContentValues createDummyRecordedProgramValues(String inputId, long channelId) { 230 ContentValues values = new ContentValues(); 231 values.put(RecordedPrograms.COLUMN_INPUT_ID, inputId); 232 values.put(RecordedPrograms.COLUMN_CHANNEL_ID, channelId); 233 values.put(RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER , "3B"); 234 values.put(RecordedPrograms.COLUMN_SEASON_TITLE, "season_title"); 235 values.put(RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER , "2A"); 236 values.put(RecordedPrograms.COLUMN_EPISODE_TITLE, "episode_title"); 237 values.put(RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS, 1000); 238 values.put(RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, 2000); 239 values.put(RecordedPrograms.COLUMN_CANONICAL_GENRE, 240 Programs.Genres.encode(Programs.Genres.MOVIES, Programs.Genres.DRAMA)); 241 values.put(RecordedPrograms.COLUMN_SHORT_DESCRIPTION, "short_description"); 242 values.put(RecordedPrograms.COLUMN_LONG_DESCRIPTION, "long_description"); 243 values.put(RecordedPrograms.COLUMN_VIDEO_WIDTH, 1920); 244 values.put(RecordedPrograms.COLUMN_VIDEO_HEIGHT, 1080); 245 values.put(RecordedPrograms.COLUMN_AUDIO_LANGUAGE, "en"); 246 TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG", 247 "US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V"); 248 values.put(RecordedPrograms.COLUMN_CONTENT_RATING, rating.flattenToString()); 249 values.put(RecordedPrograms.COLUMN_POSTER_ART_URI, "http://foo.com/artwork.png"); 250 values.put(RecordedPrograms.COLUMN_THUMBNAIL_URI, "http://foo.com/thumbnail.jpg"); 251 values.put(RecordedPrograms.COLUMN_SEARCHABLE, 1); 252 values.put(RecordedPrograms.COLUMN_RECORDING_DATA_URI, "file:///sdcard/foo/"); 253 values.put(RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, 1024 * 1024); 254 values.put(RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, 60 * 60 * 1000); 255 values.put(RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS, 1454480880L); 256 values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA, 257 "internal_provider_data".getBytes()); 258 values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1, 4); 259 values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2, 3); 260 values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3, 2); 261 values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4, 1); 262 values.put(RecordedPrograms.COLUMN_REVIEW_RATING_STYLE, 263 RecordedPrograms.REVIEW_RATING_STYLE_STARS); 264 values.put(RecordedPrograms.COLUMN_REVIEW_RATING, "4.5"); 265 266 return values; 267 } 268 269 private static void verifyStringColumn(Cursor cursor, ContentValues expectedValues, 270 String columnName) { 271 if (expectedValues.containsKey(columnName)) { 272 assertEquals(expectedValues.getAsString(columnName), 273 cursor.getString(cursor.getColumnIndex(columnName))); 274 } 275 } 276 277 private static void verifyIntegerColumn(Cursor cursor, ContentValues expectedValues, 278 String columnName) { 279 if (expectedValues.containsKey(columnName)) { 280 assertEquals(expectedValues.getAsInteger(columnName).intValue(), 281 cursor.getInt(cursor.getColumnIndex(columnName))); 282 } 283 } 284 285 private static void verifyLongColumn(Cursor cursor, ContentValues expectedValues, 286 String columnName) { 287 if (expectedValues.containsKey(columnName)) { 288 assertEquals(expectedValues.getAsLong(columnName).longValue(), 289 cursor.getLong(cursor.getColumnIndex(columnName))); 290 } 291 } 292 293 private static void verifyBlobColumn(Cursor cursor, ContentValues expectedValues, 294 String columnName) { 295 if (expectedValues.containsKey(columnName)) { 296 byte[] expected = expectedValues.getAsByteArray(columnName); 297 byte[] actual = cursor.getBlob(cursor.getColumnIndex(columnName)); 298 assertEquals(expected.length, actual.length); 299 for (int i = 0; i < expected.length; ++i) { 300 assertEquals(expected[i], actual[i]); 301 } 302 } 303 } 304 305 private void verifyChannel(Uri channelUri, ContentValues expectedValues, long channelId) { 306 verifyChannel(channelUri, expectedValues, channelId, true); 307 } 308 309 private void verifyChannel(Uri channelUri, ContentValues expectedValues, long channelId, 310 boolean verifyInputId) { 311 try (Cursor cursor = mContentResolver.query( 312 channelUri, CHANNELS_PROJECTION, null, null, null)) { 313 assertNotNull(cursor); 314 assertEquals(cursor.getCount(), 1); 315 assertTrue(cursor.moveToNext()); 316 assertEquals(channelId, cursor.getLong(cursor.getColumnIndex(Channels._ID))); 317 if (verifyInputId) { 318 verifyStringColumn(cursor, expectedValues, Channels.COLUMN_INPUT_ID); 319 } 320 verifyStringColumn(cursor, expectedValues, Channels.COLUMN_TYPE); 321 verifyStringColumn(cursor, expectedValues, Channels.COLUMN_SERVICE_TYPE); 322 verifyIntegerColumn(cursor, expectedValues, Channels.COLUMN_ORIGINAL_NETWORK_ID); 323 verifyIntegerColumn(cursor, expectedValues, Channels.COLUMN_TRANSPORT_STREAM_ID); 324 verifyIntegerColumn(cursor, expectedValues, Channels.COLUMN_SERVICE_ID); 325 verifyStringColumn(cursor, expectedValues, Channels.COLUMN_DISPLAY_NUMBER); 326 verifyStringColumn(cursor, expectedValues, Channels.COLUMN_DISPLAY_NAME); 327 verifyStringColumn(cursor, expectedValues, Channels.COLUMN_NETWORK_AFFILIATION); 328 verifyStringColumn(cursor, expectedValues, Channels.COLUMN_DESCRIPTION); 329 verifyStringColumn(cursor, expectedValues, Channels.COLUMN_VIDEO_FORMAT); 330 verifyStringColumn(cursor, expectedValues, Channels.COLUMN_INTERNAL_PROVIDER_ID); 331 verifyBlobColumn(cursor, expectedValues, Channels.COLUMN_INTERNAL_PROVIDER_DATA); 332 verifyIntegerColumn(cursor, expectedValues, Channels.COLUMN_VERSION_NUMBER); 333 } 334 } 335 336 private void verifyNonExistingColumn(Uri channelUri, long channelId) { 337 String[] projection = { 338 Channels._ID, 339 NON_EXISTING_COLUMN_NAME 340 }; 341 try (Cursor cursor = mContentResolver.query(channelUri, projection, null, null, null)) { 342 assertNotNull(cursor); 343 assertEquals(cursor.getCount(), 1); 344 assertTrue(cursor.moveToNext()); 345 assertEquals(channelId, cursor.getLong(0)); 346 assertNull(cursor.getString(1)); 347 assertEquals(0, cursor.getInt(1)); 348 } 349 } 350 351 public void testChannelsTable() throws Exception { 352 if (!Utils.hasTvInputFramework(getContext())) { 353 return; 354 } 355 // Test: insert 356 ContentValues values = createDummyChannelValues(mInputId, false); 357 358 Uri rowUri = mContentResolver.insert(mChannelsUri, values); 359 long channelId = ContentUris.parseId(rowUri); 360 Uri channelUri = TvContract.buildChannelUri(channelId); 361 verifyChannel(channelUri, values, channelId); 362 363 // Test: update 364 values.put(Channels.COLUMN_DISPLAY_NUMBER, "1-1"); 365 values.put(Channels.COLUMN_DISPLAY_NAME, "One dash one"); 366 values.put(Channels.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes()); 367 368 mContentResolver.update(channelUri, values, null, null); 369 verifyChannel(channelUri, values, channelId); 370 371 // Test: delete 372 mContentResolver.delete(mChannelsUri, null, null); 373 try (Cursor cursor = mContentResolver.query( 374 mChannelsUri, CHANNELS_PROJECTION, null, null, null)) { 375 assertEquals(0, cursor.getCount()); 376 } 377 } 378 379 public void testChannelsTableForModifyChannelType() throws Exception { 380 if (!Utils.hasTvInputFramework(getContext())) { 381 return; 382 } 383 ContentValues values = createDummyChannelValues(mInputId, true); 384 Uri channelUri = mContentResolver.insert(mChannelsUri, values); 385 long channelId = ContentUris.parseId(channelUri); 386 387 // Test: try to modify channel type should fail 388 values.put(Channels.COLUMN_TYPE, Channels.TYPE_OTHER); 389 values.put(Channels.COLUMN_DISPLAY_NAME, "One dash one"); 390 int result = mContentResolver.update(channelUri, values, null, null); 391 assertEquals(0, result); 392 393 // Test: update with same channel type should succeed 394 values.put(Channels.COLUMN_TYPE, Channels.TYPE_PREVIEW); 395 result = mContentResolver.update(channelUri, values, null, null); 396 assertEquals(1, result); 397 verifyChannel(channelUri, values, channelId); 398 399 // Test: update channel type for all channels should fail 400 result = mContentResolver.update(Channels.CONTENT_URI, values, null, null); 401 assertEquals(0, result); 402 } 403 404 public void testChannelsTableForInputId() throws Exception { 405 if (!Utils.hasTvInputFramework(getContext())) { 406 return; 407 } 408 // Non-preview channels should not be inserted without input ID 409 ContentValues values = createDummyChannelValues(mInputId, false); 410 values.remove(Channels.COLUMN_INPUT_ID); 411 Uri rowUri = mContentResolver.insert(Channels.CONTENT_URI, values); 412 assertNull(rowUri); 413 414 // Non-preview channels should not be inserted with null input ID 415 values.putNull(Channels.COLUMN_INPUT_ID); 416 rowUri = mContentResolver.insert(Channels.CONTENT_URI, values); 417 assertNull(rowUri); 418 419 // Preview channels can be inserted with null input ID 420 values.put(Channels.COLUMN_TYPE, Channels.TYPE_PREVIEW); 421 rowUri = mContentResolver.insert(Channels.CONTENT_URI, values); 422 long channelId = ContentUris.parseId(rowUri); 423 Uri channelUri = TvContract.buildChannelUri(channelId); 424 verifyChannel(channelUri, values, channelId, false); 425 426 // Preview channels can be inserted without input ID 427 values.remove(Channels.COLUMN_INPUT_ID); 428 rowUri = mContentResolver.insert(Channels.CONTENT_URI, values); 429 channelId = ContentUris.parseId(rowUri); 430 channelUri = TvContract.buildChannelUri(channelId); 431 verifyChannel(channelUri, values, channelId, false); 432 } 433 434 public void testChannelsTableForModifyIdAndPackageName() throws Exception { 435 if (!Utils.hasTvInputFramework(getContext())) { 436 return; 437 } 438 ContentValues baseValues = createDummyChannelValues(mInputId, false); 439 Uri channelUri = mContentResolver.insert(mChannelsUri, baseValues); 440 long channelId = ContentUris.parseId(channelUri); 441 442 ContentValues values = new ContentValues(baseValues); 443 values.put(Channels._ID, channelId); 444 int result = mContentResolver.update(channelUri, values, null, null); 445 assertEquals(1, result); 446 values.put(Channels._ID, channelId + 1); 447 try { 448 mContentResolver.update(channelUri, values, null, null); 449 fail("Channels._ID should not be changed."); 450 } catch (Exception e) { 451 // Expected. 452 } 453 454 values = new ContentValues(baseValues); 455 values.put(Channels.COLUMN_PACKAGE_NAME, getContext().getPackageName()); 456 result = mContentResolver.update(channelUri, values, null, null); 457 assertEquals(1, result); 458 values.put(Channels.COLUMN_PACKAGE_NAME, ""); 459 try { 460 mContentResolver.update(channelUri, values, null, null); 461 fail("Channels.COLUMN_PACKAGE_NAME should not be changed."); 462 } catch (Exception e) { 463 // Expected. 464 } 465 } 466 467 public void testChannelsTableForIllegalAccess() throws Exception { 468 if (!Utils.hasTvInputFramework(getContext())) { 469 return; 470 } 471 ContentValues baseValues = createDummyChannelValues(mInputId, false); 472 Uri channelUri = mContentResolver.insert(mChannelsUri, baseValues); 473 474 // Test: insert 475 ContentValues values = new ContentValues(baseValues); 476 values.put(Channels.COLUMN_BROWSABLE, 1); 477 try { 478 mContentResolver.insert(mChannelsUri, values); 479 fail("Channels.COLUMN_BROWSABLE should be read-only."); 480 } catch (Exception e) { 481 // Expected. 482 } 483 values = new ContentValues(baseValues); 484 values.put(Channels.COLUMN_LOCKED, 1); 485 try { 486 mContentResolver.insert(mChannelsUri, values); 487 fail("Channels.COLUMN_LOCKED should be read-only."); 488 } catch (Exception e) { 489 // Expected. 490 } 491 492 // Test: update 493 values = new ContentValues(baseValues); 494 values.put(Channels.COLUMN_BROWSABLE, 1); 495 try { 496 mContentResolver.update(channelUri, values, null, null); 497 fail("Channels.COLUMN_BROWSABLE should be read-only."); 498 } catch (Exception e) { 499 // Expected. 500 } 501 values = new ContentValues(baseValues); 502 values.put(Channels.COLUMN_LOCKED, 1); 503 try { 504 mContentResolver.update(channelUri, values, null, null); 505 fail("Channels.COLUMN_LOCKED should be read-only."); 506 } catch (Exception e) { 507 // Expected. 508 } 509 510 // Test: query 511 try (Cursor cursor = mContentResolver.query( 512 channelUri, 513 new String[]{ Channels.COLUMN_BROWSABLE }, null, null, null)) { 514 // Channels.COLUMN_BROWSABLE should be readable from application. 515 assertEquals(1, cursor.getCount()); 516 } 517 try (Cursor cursor = mContentResolver.query( 518 channelUri, 519 new String[]{ Channels.COLUMN_LOCKED }, null, null, null)) { 520 // Channels.COLUMN_LOCKED should be readable from application. 521 assertEquals(1, cursor.getCount()); 522 } 523 524 mContentResolver.delete(mChannelsUri, null, null); 525 try (Cursor cursor = mContentResolver.query( 526 mChannelsUri, CHANNELS_PROJECTION, null, null, null)) { 527 assertEquals(0, cursor.getCount()); 528 } 529 } 530 531 public void testChannelsTableForNonExistingColumns() throws Exception { 532 if (!Utils.hasTvInputFramework(getContext())) { 533 return; 534 } 535 ContentValues values = createDummyChannelValues(mInputId, false); 536 values.put(NON_EXISTING_COLUMN_NAME, "dummy value"); 537 Uri rowUri = mContentResolver.insert(mChannelsUri, values); 538 long channelId = ContentUris.parseId(rowUri); 539 Uri channelUri = TvContract.buildChannelUri(channelId); 540 verifyChannel(channelUri, values, channelId); 541 verifyNonExistingColumn(channelUri, channelId); 542 543 // Test: update 544 mContentResolver.update(channelUri, values, null, null); 545 verifyChannel(channelUri, values, channelId); 546 verifyNonExistingColumn(channelUri, channelId); 547 548 // Test: delete 549 mContentResolver.delete(mChannelsUri, null, null); 550 try (Cursor cursor = mContentResolver.query( 551 mChannelsUri, CHANNELS_PROJECTION, null, null, null)) { 552 assertEquals(0, cursor.getCount()); 553 } 554 } 555 556 private void verifyProgram(Uri programUri, ContentValues expectedValues, long programId) { 557 try (Cursor cursor = mContentResolver.query( 558 programUri, null, null, null, null)) { 559 assertNotNull(cursor); 560 assertEquals(cursor.getCount(), 1); 561 assertTrue(cursor.moveToNext()); 562 assertEquals(programId, cursor.getLong(cursor.getColumnIndex(Programs._ID))); 563 verifyLongColumn(cursor, expectedValues, Programs.COLUMN_CHANNEL_ID); 564 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_TITLE); 565 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_SEASON_DISPLAY_NUMBER); 566 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_SEASON_TITLE); 567 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_EPISODE_DISPLAY_NUMBER); 568 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_EPISODE_TITLE); 569 verifyLongColumn(cursor, expectedValues, Programs.COLUMN_START_TIME_UTC_MILLIS); 570 verifyLongColumn(cursor, expectedValues, Programs.COLUMN_END_TIME_UTC_MILLIS); 571 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_BROADCAST_GENRE); 572 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_CANONICAL_GENRE); 573 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_SHORT_DESCRIPTION); 574 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_LONG_DESCRIPTION); 575 verifyIntegerColumn(cursor, expectedValues, Programs.COLUMN_VIDEO_WIDTH); 576 verifyIntegerColumn(cursor, expectedValues, Programs.COLUMN_VIDEO_HEIGHT); 577 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_AUDIO_LANGUAGE); 578 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_CONTENT_RATING); 579 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_POSTER_ART_URI); 580 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_THUMBNAIL_URI); 581 verifyBlobColumn(cursor, expectedValues, Programs.COLUMN_INTERNAL_PROVIDER_DATA); 582 verifyIntegerColumn(cursor, expectedValues, Programs.COLUMN_VERSION_NUMBER); 583 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_REVIEW_RATING_STYLE); 584 verifyStringColumn(cursor, expectedValues, Programs.COLUMN_REVIEW_RATING); 585 } 586 } 587 588 private void verifyPreviewProgram(Uri programUri, ContentValues expectedValues, 589 long programId) { 590 try (Cursor cursor = mContentResolver.query( 591 programUri, null, null, null, null)) { 592 assertNotNull(cursor); 593 assertEquals(cursor.getCount(), 1); 594 assertTrue(cursor.moveToNext()); 595 assertEquals(programId, cursor.getLong(cursor.getColumnIndex(PreviewPrograms._ID))); 596 verifyLongColumn(cursor, expectedValues, PreviewPrograms.COLUMN_CHANNEL_ID); 597 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_TITLE); 598 verifyStringColumn(cursor, expectedValues, 599 PreviewPrograms.COLUMN_SEASON_DISPLAY_NUMBER); 600 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_SEASON_TITLE); 601 verifyStringColumn(cursor, expectedValues, 602 PreviewPrograms.COLUMN_EPISODE_DISPLAY_NUMBER); 603 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_EPISODE_TITLE); 604 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_CANONICAL_GENRE); 605 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_SHORT_DESCRIPTION); 606 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_LONG_DESCRIPTION); 607 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_VIDEO_WIDTH); 608 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_VIDEO_HEIGHT); 609 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_AUDIO_LANGUAGE); 610 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_CONTENT_RATING); 611 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_POSTER_ART_URI); 612 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_THUMBNAIL_URI); 613 verifyBlobColumn(cursor, expectedValues, PreviewPrograms.COLUMN_INTERNAL_PROVIDER_DATA); 614 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_VERSION_NUMBER); 615 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_INTERNAL_PROVIDER_ID); 616 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_PREVIEW_VIDEO_URI); 617 verifyIntegerColumn(cursor, expectedValues, 618 PreviewPrograms.COLUMN_LAST_PLAYBACK_POSITION_MILLIS); 619 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_DURATION_MILLIS); 620 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_INTENT_URI); 621 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_WEIGHT); 622 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_TYPE); 623 verifyIntegerColumn(cursor, expectedValues, 624 PreviewPrograms.COLUMN_POSTER_ART_ASPECT_RATIO); 625 verifyIntegerColumn(cursor, expectedValues, 626 PreviewPrograms.COLUMN_THUMBNAIL_ASPECT_RATIO); 627 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_LOGO_URI); 628 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_AVAILABILITY); 629 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_STARTING_PRICE); 630 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_OFFER_PRICE); 631 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_RELEASE_DATE); 632 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_ITEM_COUNT); 633 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_LIVE); 634 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_INTERACTION_TYPE); 635 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_INTERACTION_COUNT); 636 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_AUTHOR); 637 verifyIntegerColumn(cursor, expectedValues, PreviewPrograms.COLUMN_REVIEW_RATING_STYLE); 638 verifyStringColumn(cursor, expectedValues, PreviewPrograms.COLUMN_REVIEW_RATING); 639 } 640 } 641 642 private void verifyWatchNextProgram(Uri programUri, ContentValues expectedValues, 643 long programId) { 644 verifyPreviewProgram(programUri, expectedValues, programId); 645 try (Cursor cursor = mContentResolver.query(programUri, null, null, null, null)) { 646 assertNotNull(cursor); 647 assertEquals(cursor.getCount(), 1); 648 assertTrue(cursor.moveToNext()); 649 650 verifyIntegerColumn(cursor, expectedValues, WatchNextPrograms.COLUMN_WATCH_NEXT_TYPE); 651 verifyLongColumn(cursor, expectedValues, 652 WatchNextPrograms.COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS); 653 } 654 } 655 656 private void verifyDeprecatedColumsInProgram(Uri programUri, ContentValues expectedValues) { 657 final String[] DEPRECATED_COLUMNS_PROJECTION = { 658 Programs.COLUMN_SEASON_NUMBER, 659 Programs.COLUMN_EPISODE_NUMBER, 660 }; 661 try (Cursor cursor = mContentResolver.query( 662 programUri, DEPRECATED_COLUMNS_PROJECTION, null, null, null)) { 663 assertNotNull(cursor); 664 assertEquals(cursor.getCount(), 1); 665 assertTrue(cursor.moveToNext()); 666 verifyIntegerColumn(cursor, expectedValues, Programs.COLUMN_SEASON_NUMBER); 667 verifyIntegerColumn(cursor, expectedValues, Programs.COLUMN_EPISODE_NUMBER); 668 } 669 } 670 671 private void verifyLogoIsReadable(Uri logoUri) throws Exception { 672 try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "r")) { 673 try (InputStream is = fd.createInputStream()) { 674 // Assure that the stream is decodable as a Bitmap. 675 BitmapFactory.decodeStream(is); 676 } 677 } 678 } 679 680 public void testChannelLogo() throws Exception { 681 if (!Utils.hasTvInputFramework(getContext())) { 682 return; 683 } 684 // Set-up: add a channel. 685 ContentValues values = createDummyChannelValues(mInputId, false); 686 Uri channelUri = mContentResolver.insert(mChannelsUri, values); 687 Uri logoUri = TvContract.buildChannelLogoUri(channelUri); 688 Bitmap logo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.robot); 689 690 // Write 691 try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "w")) { 692 try (OutputStream os = fd.createOutputStream()) { 693 logo.compress(Bitmap.CompressFormat.PNG, 100, os); 694 } 695 } 696 697 // Give some time for TvProvider to process the logo. 698 Thread.sleep(OPERATION_TIME); 699 700 // Read and verify 701 verifyLogoIsReadable(logoUri); 702 703 // Read and verify using alternative logo URI. 704 verifyLogoIsReadable(TvContract.buildChannelLogoUri(ContentUris.parseId(channelUri))); 705 } 706 707 public void verifyProgramsTable(Uri programsUri, long channelId) { 708 if (!Utils.hasTvInputFramework(getContext())) { 709 return; 710 } 711 // Test: insert 712 ContentValues values = createDummyProgramValues(channelId); 713 714 Uri rowUri = mContentResolver.insert(programsUri, values); 715 long programId = ContentUris.parseId(rowUri); 716 Uri programUri = TvContract.buildProgramUri(programId); 717 verifyProgram(programUri, values, programId); 718 719 // Test: update 720 values.put(Programs.COLUMN_TITLE, "new_program_title"); 721 values.put(Programs.COLUMN_SHORT_DESCRIPTION, ""); 722 values.put(Programs.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes()); 723 724 mContentResolver.update(programUri, values, null, null); 725 verifyProgram(programUri, values, programId); 726 727 // Test: delete 728 mContentResolver.delete(programsUri, null, null); 729 try (Cursor cursor = mContentResolver.query(programsUri, null, null, null, null)) { 730 assertEquals(0, cursor.getCount()); 731 } 732 } 733 734 public void verifyPreviewProgramsTable(Uri previewProgramsUri, long channelId) { 735 if (!Utils.hasTvInputFramework(getContext())) { 736 return; 737 } 738 // Test: insert 739 ContentValues values = createDummyPreviewProgramValues(channelId); 740 741 Uri rowUri = mContentResolver.insert(previewProgramsUri, values); 742 long programId = ContentUris.parseId(rowUri); 743 Uri programUri = TvContract.buildPreviewProgramUri(programId); 744 verifyPreviewProgram(programUri, values, programId); 745 746 values.remove(PreviewPrograms.COLUMN_TYPE); 747 try { 748 mContentResolver.insert(previewProgramsUri, values); 749 fail("Type should be a required column."); 750 } catch (IllegalArgumentException e) { 751 // Expected. 752 } 753 754 // Test: update 755 values.put(PreviewPrograms.COLUMN_EPISODE_TITLE, "Sample title"); 756 values.put(PreviewPrograms.COLUMN_SHORT_DESCRIPTION, "Short description"); 757 values.put(PreviewPrograms.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes()); 758 759 mContentResolver.update(programUri, values, null, null); 760 verifyPreviewProgram(programUri, values, programId); 761 762 // Test: delete 763 mContentResolver.delete(previewProgramsUri, null, null); 764 try (Cursor cursor = mContentResolver.query(previewProgramsUri, null, null, null, null)) { 765 assertEquals(0, cursor.getCount()); 766 } 767 } 768 769 public void verifyWatchNextProgramsTable(Uri watchNextProgramsUri) { 770 if (!Utils.hasTvInputFramework(getContext())) { 771 return; 772 } 773 // Test: insert 774 ContentValues values = createDummyWatchNextProgramValues(); 775 776 Uri rowUri = mContentResolver.insert(watchNextProgramsUri, values); 777 long programId = ContentUris.parseId(rowUri); 778 Uri programUri = TvContract.buildWatchNextProgramUri(programId); 779 verifyWatchNextProgram(programUri, values, programId); 780 781 values.remove(WatchNextPrograms.COLUMN_TYPE); 782 try { 783 mContentResolver.insert(watchNextProgramsUri, values); 784 fail("Type should be a required column."); 785 } catch (IllegalArgumentException e) { 786 // Expected. 787 } 788 789 // Test: update 790 values.put(WatchNextPrograms.COLUMN_EPISODE_TITLE, "Sample title"); 791 values.put(WatchNextPrograms.COLUMN_SHORT_DESCRIPTION, "Short description"); 792 values.put(WatchNextPrograms.COLUMN_CONTENT_ID, "CID-4328-2548"); 793 794 mContentResolver.update(programUri, values, null, null); 795 verifyWatchNextProgram(programUri, values, programId); 796 797 // Test: delete 798 mContentResolver.delete(watchNextProgramsUri, null, null); 799 try (Cursor cursor = mContentResolver.query(watchNextProgramsUri, null, null, null, null)) { 800 assertEquals(0, cursor.getCount()); 801 } 802 } 803 804 public void verifyProgramsTableWithDeprecatedColumns(Uri programsUri, long channelId) { 805 if (!Utils.hasTvInputFramework(getContext())) { 806 return; 807 } 808 // Test: insert 809 ContentValues expected = createDummyProgramValues(channelId); 810 expected.put(Programs.COLUMN_SEASON_DISPLAY_NUMBER, "3"); 811 expected.put(Programs.COLUMN_EPISODE_DISPLAY_NUMBER, "9"); 812 813 ContentValues input = new ContentValues(expected); 814 input.remove(Programs.COLUMN_SEASON_DISPLAY_NUMBER); 815 input.remove(Programs.COLUMN_EPISODE_DISPLAY_NUMBER); 816 input.put(Programs.COLUMN_SEASON_NUMBER, 3); 817 input.put(Programs.COLUMN_EPISODE_NUMBER, 9); 818 819 Uri rowUri = mContentResolver.insert(programsUri, input); 820 long programId = ContentUris.parseId(rowUri); 821 Uri programUri = TvContract.buildProgramUri(programId); 822 verifyProgram(programUri, expected, programId); 823 verifyDeprecatedColumsInProgram(programUri, input); 824 825 // Test: update 826 expected.put(Programs.COLUMN_SEASON_DISPLAY_NUMBER, "4"); 827 expected.put(Programs.COLUMN_EPISODE_DISPLAY_NUMBER, "10"); 828 input.put(Programs.COLUMN_SEASON_NUMBER, 4); 829 input.put(Programs.COLUMN_EPISODE_NUMBER, 10); 830 831 mContentResolver.update(programUri, input, null, null); 832 verifyProgram(programUri, expected, programId); 833 verifyDeprecatedColumsInProgram(programUri, input); 834 835 // Test: delete 836 mContentResolver.delete(programsUri, null, null); 837 try (Cursor cursor = mContentResolver.query( 838 programsUri, PROGRAMS_PROJECTION, null, null, null)) { 839 assertEquals(0, cursor.getCount()); 840 } 841 } 842 843 public void testProgramsTable() throws Exception { 844 if (!Utils.hasTvInputFramework(getContext())) { 845 return; 846 } 847 // Set-up: add a channel. 848 ContentValues values = createDummyChannelValues(mInputId, false); 849 Uri channelUri = mContentResolver.insert(mChannelsUri, values); 850 long channelId = ContentUris.parseId(channelUri); 851 852 verifyProgramsTable(TvContract.buildProgramsUriForChannel(channelId), channelId); 853 verifyProgramsTable(TvContract.buildProgramsUriForChannel(channelUri), channelId); 854 verifyProgramsTableWithDeprecatedColumns(TvContract.buildProgramsUriForChannel(channelId), 855 channelId); 856 verifyProgramsTableWithDeprecatedColumns(TvContract.buildProgramsUriForChannel(channelUri), 857 channelId); 858 } 859 860 public void testPreviewProgramsTable() throws Exception { 861 if (!Utils.hasTvInputFramework(getContext())) { 862 return; 863 } 864 // Set-up: add a preview type channel. 865 ContentValues values = createDummyChannelValues(mInputId, true); 866 Uri channelUri = mContentResolver.insert(mChannelsUri, values); 867 long channelId = ContentUris.parseId(channelUri); 868 869 verifyPreviewProgramsTable(PreviewPrograms.CONTENT_URI, channelId); 870 } 871 872 public void testWatchNextProgramsTable() throws Exception { 873 if (!Utils.hasTvInputFramework(getContext())) { 874 return; 875 } 876 verifyWatchNextProgramsTable(WatchNextPrograms.CONTENT_URI); 877 } 878 879 public void testPreviewProgramsTableForIllegalAccess() throws Exception { 880 if (!Utils.hasTvInputFramework(getContext())) { 881 return; 882 } 883 // Set-up: add a channel and preview program. 884 ContentValues values = createDummyChannelValues(mInputId, true); 885 Uri channelUri = mContentResolver.insert(mChannelsUri, values); 886 long channelId = ContentUris.parseId(channelUri); 887 Uri previewProgramsUri = TvContract.buildPreviewProgramsUriForChannel(channelId); 888 values = createDummyPreviewProgramValues(channelId); 889 Uri previewProgramUri = mContentResolver.insert(previewProgramsUri, values); 890 891 values.put(PreviewPrograms.COLUMN_BROWSABLE, 1); 892 try { 893 mContentResolver.insert(previewProgramUri, values); 894 fail("PreviewPrograms.COLUMN_BROWSABLE should not be accessible."); 895 } catch (Exception e) { 896 // Expected. 897 } 898 899 mContentResolver.delete(previewProgramUri, null, null); 900 try (Cursor cursor = mContentResolver.query( 901 previewProgramUri, PROGRAMS_PROJECTION, null, null, null)) { 902 assertEquals(0, cursor.getCount()); 903 } 904 } 905 906 public void testPreviewProgramsTableForModifyChannelId() throws Exception { 907 if (!Utils.hasTvInputFramework(getContext())) { 908 return; 909 } 910 // Set-up: add a channel and preview program. 911 ContentValues values = createDummyChannelValues(mInputId, true); 912 Uri channelUri = mContentResolver.insert(mChannelsUri, values); 913 long channelId = ContentUris.parseId(channelUri); 914 Uri previewProgramsUri = TvContract.buildPreviewProgramsUriForChannel(channelId); 915 values = createDummyPreviewProgramValues(channelId); 916 Uri previewProgramUri = mContentResolver.insert(previewProgramsUri, values); 917 918 // Channel ID cannot be changed 919 values.put(PreviewPrograms.COLUMN_ITEM_COUNT, 1); 920 values.put(PreviewPrograms.COLUMN_CHANNEL_ID, channelId + 1); 921 int result = mContentResolver.update(previewProgramUri, values, null, null); 922 assertEquals(0, result); 923 924 // Same Channel ID should not fail updating 925 values.put(PreviewPrograms.COLUMN_CHANNEL_ID, channelId); 926 result = mContentResolver.update(previewProgramUri, values, null, null); 927 assertEquals(1, result); 928 929 // Update without Channel ID should not fail 930 values.put(PreviewPrograms.COLUMN_ITEM_COUNT, 2); 931 values.remove(PreviewPrograms.COLUMN_CHANNEL_ID); 932 result = mContentResolver.update(previewProgramUri, values, null, null); 933 assertEquals(1, result); 934 935 // Update channel ID with CONTENT_URI should fail 936 values.put(PreviewPrograms.COLUMN_CHANNEL_ID, channelId); 937 result = mContentResolver.update(PreviewPrograms.CONTENT_URI, values, null, null); 938 assertEquals(0, result); 939 940 mContentResolver.delete(previewProgramUri, null, null); 941 try (Cursor cursor = mContentResolver.query( 942 previewProgramUri, PROGRAMS_PROJECTION, null, null, null)) { 943 assertEquals(0, cursor.getCount()); 944 } 945 } 946 947 private void verifyOverlap(long startMillis, long endMillis, int expectedCount, 948 long channelId, Uri channelUri) { 949 try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel( 950 channelId, startMillis, endMillis), PROGRAMS_PROJECTION, null, null, null)) { 951 assertEquals(expectedCount, cursor.getCount()); 952 } 953 try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel( 954 channelUri, startMillis, endMillis), PROGRAMS_PROJECTION, null, null, null)) { 955 assertEquals(expectedCount, cursor.getCount()); 956 } 957 } 958 959 public void testProgramsScheduleOverlap() throws Exception { 960 if (!Utils.hasTvInputFramework(getContext())) { 961 return; 962 } 963 final long programStartMillis = 1403712000000l; // Jun 25 2014 16:00 UTC 964 final long programEndMillis = 1403719200000l; // Jun 25 2014 18:00 UTC 965 final long hour = 3600000l; 966 967 // Set-up: add a channel and program. 968 ContentValues values = createDummyChannelValues(mInputId, false); 969 Uri channelUri = mContentResolver.insert(mChannelsUri, values); 970 long channelId = ContentUris.parseId(channelUri); 971 Uri programsUri = TvContract.buildProgramsUriForChannel(channelId); 972 values = createDummyProgramValues(channelId); 973 values.put(Programs.COLUMN_START_TIME_UTC_MILLIS, programStartMillis); 974 values.put(Programs.COLUMN_END_TIME_UTC_MILLIS, programEndMillis); 975 mContentResolver.insert(programsUri, values); 976 977 // Overlap 1: starts early, ends early. 978 verifyOverlap(programStartMillis - hour, programEndMillis - hour, 1, channelId, channelUri); 979 980 // Overlap 2: starts early, ends late. 981 verifyOverlap(programStartMillis - hour, programEndMillis + hour, 1, channelId, channelUri); 982 983 // Overlap 3: starts early, ends late. 984 verifyOverlap(programStartMillis + hour / 2, programEndMillis - hour / 2, 1, 985 channelId, channelUri); 986 987 // Overlap 4: starts late, ends late. 988 verifyOverlap(programStartMillis + hour, programEndMillis + hour, 1, channelId, channelUri); 989 990 // Non-overlap 1: ends too early. 991 verifyOverlap(programStartMillis - hour, programStartMillis - hour / 2, 0, 992 channelId, channelUri); 993 994 // Non-overlap 2: starts too late. 995 verifyOverlap(programEndMillis + hour, programEndMillis + hour * 2, 0, 996 channelId, channelUri); 997 998 // Non-overlap 3: invalid start and end times. 999 verifyOverlap(programEndMillis, programStartMillis, 0, channelId, channelUri); 1000 } 1001 1002 private void verifyRecordedProgram(Uri recordedProgramUri, ContentValues expectedValues, 1003 long recordedProgramId) { 1004 try (Cursor cursor = mContentResolver.query(recordedProgramUri, null, null, null, null)) { 1005 assertNotNull(cursor); 1006 assertEquals(cursor.getCount(), 1); 1007 assertTrue(cursor.moveToNext()); 1008 assertEquals(recordedProgramId, cursor.getLong(cursor.getColumnIndex( 1009 RecordedPrograms._ID))); 1010 verifyLongColumn(cursor, expectedValues, RecordedPrograms.COLUMN_CHANNEL_ID); 1011 verifyStringColumn(cursor, expectedValues, 1012 RecordedPrograms.COLUMN_SEASON_DISPLAY_NUMBER); 1013 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_SEASON_TITLE); 1014 verifyStringColumn(cursor, expectedValues, 1015 RecordedPrograms.COLUMN_EPISODE_DISPLAY_NUMBER); 1016 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_EPISODE_TITLE); 1017 verifyLongColumn(cursor, expectedValues, RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS); 1018 verifyLongColumn(cursor, expectedValues, RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS); 1019 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_BROADCAST_GENRE); 1020 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_CANONICAL_GENRE); 1021 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_SHORT_DESCRIPTION); 1022 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_LONG_DESCRIPTION); 1023 verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_VIDEO_WIDTH); 1024 verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_VIDEO_HEIGHT); 1025 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_AUDIO_LANGUAGE); 1026 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_CONTENT_RATING); 1027 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_POSTER_ART_URI); 1028 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_THUMBNAIL_URI); 1029 verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_SEARCHABLE); 1030 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_RECORDING_DATA_URI); 1031 verifyIntegerColumn(cursor, expectedValues, 1032 RecordedPrograms.COLUMN_RECORDING_DATA_BYTES); 1033 verifyIntegerColumn(cursor, expectedValues, 1034 RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS); 1035 verifyIntegerColumn(cursor, expectedValues, 1036 RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS); 1037 verifyBlobColumn(cursor, expectedValues, 1038 RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA); 1039 verifyIntegerColumn(cursor, expectedValues, 1040 RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1); 1041 verifyIntegerColumn(cursor, expectedValues, 1042 RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2); 1043 verifyIntegerColumn(cursor, expectedValues, 1044 RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3); 1045 verifyIntegerColumn(cursor, expectedValues, 1046 RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4); 1047 verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_VERSION_NUMBER); 1048 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_REVIEW_RATING_STYLE); 1049 verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_REVIEW_RATING); 1050 } 1051 } 1052 1053 private void verifyRecordedProgramsTable(Uri recordedProgramsUri, long channelId) { 1054 // Test: insert 1055 ContentValues values = createDummyRecordedProgramValues(mInputId, channelId); 1056 1057 Uri rowUri = mContentResolver.insert(recordedProgramsUri, values); 1058 long recordedProgramId = ContentUris.parseId(rowUri); 1059 Uri recordedProgramUri = TvContract.buildRecordedProgramUri(recordedProgramId); 1060 verifyRecordedProgram(recordedProgramUri, values, recordedProgramId); 1061 1062 // Test: update 1063 values.put(RecordedPrograms.COLUMN_EPISODE_TITLE, "episode_title1"); 1064 values.put(RecordedPrograms.COLUMN_SHORT_DESCRIPTION, "short_description1"); 1065 values.put(RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA, 1066 "internal_provider_data1".getBytes()); 1067 1068 mContentResolver.update(recordedProgramUri, values, null, null); 1069 verifyRecordedProgram(recordedProgramUri, values, recordedProgramId); 1070 1071 // Test: delete 1072 mContentResolver.delete(recordedProgramUri, null, null); 1073 try (Cursor cursor = mContentResolver.query(recordedProgramsUri, null, null, null, null)) { 1074 assertEquals(0, cursor.getCount()); 1075 } 1076 } 1077 1078 public void testRecordedProgramsTable() throws Exception { 1079 if (!Utils.hasTvInputFramework(getContext())) { 1080 return; 1081 } 1082 // Set-up: add a channel. 1083 ContentValues values = createDummyChannelValues(mInputId, false); 1084 Uri channelUri = mContentResolver.insert(mChannelsUri, values); 1085 long channelId = ContentUris.parseId(channelUri); 1086 1087 verifyRecordedProgramsTable(RecordedPrograms.CONTENT_URI, channelId); 1088 } 1089 1090 private void verifyQueryWithSortOrder(Uri uri, final String[] projection, 1091 String sortOrder) throws Exception { 1092 try { 1093 getContext().getContentResolver().query(uri, projection, null, null, sortOrder); 1094 } catch (SecurityException e) { 1095 fail("Setting sort order shoud be allowed for " + uri); 1096 } 1097 } 1098 1099 private void verifyQueryWithSelection(Uri uri, final String[] projection, 1100 String selection) throws Exception { 1101 try { 1102 getContext().getContentResolver().query(uri, projection, selection, null, null); 1103 fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri); 1104 } catch (SecurityException e) { 1105 // Expected exception 1106 } 1107 } 1108 1109 private void verifyUpdateWithSelection(Uri uri, String selection) throws Exception { 1110 try { 1111 ContentValues values = new ContentValues(); 1112 getContext().getContentResolver().update(uri, values, selection, null); 1113 fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri); 1114 } catch (SecurityException e) { 1115 // Expected exception 1116 } 1117 } 1118 1119 private void verifyDeleteWithSelection(Uri uri, String selection) throws Exception { 1120 try { 1121 getContext().getContentResolver().delete(uri, selection, null); 1122 fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri); 1123 } catch (SecurityException e) { 1124 // Expected exception 1125 } 1126 } 1127 1128 public void testAllEpgPermissionBlocksSortOrderOnQuery_Channels() throws Exception { 1129 if (!Utils.hasTvInputFramework(getContext())) { 1130 return; 1131 } 1132 final String[] projection = { Channels._ID }; 1133 verifyQueryWithSortOrder(Channels.CONTENT_URI, projection, Channels._ID + " ASC"); 1134 } 1135 1136 public void testAllEpgPermissionBlocksSelectionOnQuery_Channels() throws Exception { 1137 if (!Utils.hasTvInputFramework(getContext())) { 1138 return; 1139 } 1140 final String[] projection = { Channels._ID }; 1141 verifyQueryWithSelection(Channels.CONTENT_URI, projection, Channels._ID + ">0"); 1142 } 1143 1144 public void testAllEpgPermissionBlocksSelectionOnUpdate_Channels() throws Exception { 1145 if (!Utils.hasTvInputFramework(getContext())) { 1146 return; 1147 } 1148 verifyUpdateWithSelection(Channels.CONTENT_URI, Channels._ID + ">0"); 1149 } 1150 1151 public void testAllEpgPermissionBlocksSelectionOnDelete_Channels() throws Exception { 1152 if (!Utils.hasTvInputFramework(getContext())) { 1153 return; 1154 } 1155 verifyDeleteWithSelection(Channels.CONTENT_URI, Channels._ID + ">0"); 1156 } 1157 1158 public void testAllEpgPermissionBlocksSortOrderOnQuery_Programs() throws Exception { 1159 if (!Utils.hasTvInputFramework(getContext())) { 1160 return; 1161 } 1162 final String[] projection = { Programs._ID }; 1163 verifyQueryWithSortOrder(Programs.CONTENT_URI, projection, Programs._ID + " ASC"); 1164 } 1165 1166 public void testAllEpgPermissionBlocksSelectionOnQuery_Programs() throws Exception { 1167 if (!Utils.hasTvInputFramework(getContext())) { 1168 return; 1169 } 1170 final String[] projection = { Channels._ID }; 1171 verifyQueryWithSelection(Programs.CONTENT_URI, projection, Programs._ID + ">0"); 1172 } 1173 1174 public void testAllEpgPermissionBlocksSelectionOnUpdate_Programs() throws Exception { 1175 if (!Utils.hasTvInputFramework(getContext())) { 1176 return; 1177 } 1178 verifyUpdateWithSelection(Programs.CONTENT_URI, Programs._ID + ">0"); 1179 } 1180 1181 public void testAllEpgPermissionBlocksSelectionOnDelete_Programs() throws Exception { 1182 if (!Utils.hasTvInputFramework(getContext())) { 1183 return; 1184 } 1185 verifyDeleteWithSelection(Programs.CONTENT_URI, Programs._ID + ">0"); 1186 } 1187 1188 public void testDefaultValues() throws Exception { 1189 if (!Utils.hasTvInputFramework(getContext())) { 1190 return; 1191 } 1192 ContentValues values = new ContentValues(); 1193 values.put(Channels.COLUMN_INPUT_ID, mInputId); 1194 Uri channelUri = mContentResolver.insert(mChannelsUri, values); 1195 assertNotNull(channelUri); 1196 try (Cursor cursor = mContentResolver.query( 1197 channelUri, CHANNELS_PROJECTION, null, null, null)) { 1198 cursor.moveToNext(); 1199 assertEquals(Channels.TYPE_OTHER, 1200 cursor.getString(cursor.getColumnIndex(Channels.COLUMN_TYPE))); 1201 assertEquals(Channels.SERVICE_TYPE_AUDIO_VIDEO, 1202 cursor.getString(cursor.getColumnIndex(Channels.COLUMN_SERVICE_TYPE))); 1203 } 1204 values.clear(); 1205 } 1206 1207 public void testChannelsGetVideoResolution() { 1208 if (!Utils.hasTvInputFramework(getContext())) { 1209 return; 1210 } 1211 assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution( 1212 Channels.VIDEO_FORMAT_480I)); 1213 assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution( 1214 Channels.VIDEO_FORMAT_480P)); 1215 assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution( 1216 Channels.VIDEO_FORMAT_576I)); 1217 assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution( 1218 Channels.VIDEO_FORMAT_576P)); 1219 assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution( 1220 Channels.VIDEO_FORMAT_720P)); 1221 assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution( 1222 Channels.VIDEO_FORMAT_1080I)); 1223 assertEquals(Channels.VIDEO_RESOLUTION_FHD, Channels.getVideoResolution( 1224 Channels.VIDEO_FORMAT_1080P)); 1225 assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution( 1226 Channels.VIDEO_FORMAT_2160P)); 1227 assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution( 1228 Channels.VIDEO_FORMAT_4320P)); 1229 assertEquals(null, Channels.getVideoResolution("Unknown format")); 1230 } 1231 1232 public void testProgramsGenresDecode() { 1233 if (!Utils.hasTvInputFramework(getContext())) { 1234 return; 1235 } 1236 List genres = Arrays.asList(Genres.decode(ENCODED_GENRE_STRING)); 1237 assertEquals(11, genres.size()); 1238 assertTrue(genres.contains(Genres.ANIMAL_WILDLIFE)); 1239 assertTrue(genres.contains(Genres.COMEDY)); 1240 assertTrue(genres.contains(Genres.DRAMA)); 1241 assertTrue(genres.contains(Genres.EDUCATION)); 1242 assertTrue(genres.contains(Genres.FAMILY_KIDS)); 1243 assertTrue(genres.contains(Genres.GAMING)); 1244 assertTrue(genres.contains(Genres.MOVIES)); 1245 assertTrue(genres.contains(Genres.NEWS)); 1246 assertTrue(genres.contains(Genres.SHOPPING)); 1247 assertTrue(genres.contains(Genres.SPORTS)); 1248 assertTrue(genres.contains(Genres.TRAVEL)); 1249 assertFalse(genres.contains(",")); 1250 } 1251 1252 public void testProgramsGenresEncode() { 1253 if (!Utils.hasTvInputFramework(getContext())) { 1254 return; 1255 } 1256 assertEquals(ENCODED_GENRE_STRING, Genres.encode(Genres.ANIMAL_WILDLIFE, 1257 Genres.COMEDY, Genres.DRAMA, Genres.EDUCATION, Genres.FAMILY_KIDS, Genres.GAMING, 1258 Genres.MOVIES, Genres.NEWS, Genres.SHOPPING, Genres.SPORTS, Genres.TRAVEL)); 1259 } 1260 1261 public void testProgramsGenresEncodeDecode_empty() { 1262 if (!Utils.hasTvInputFramework(getContext())) { 1263 return; 1264 } 1265 String[] genres = new String[] {EMPTY_GENRE}; 1266 String expectedEncoded = EMPTY_GENRE; 1267 checkGenreEncodeDecode(genres, expectedEncoded, 0); 1268 1269 genres = new String[] {EMPTY_GENRE, EMPTY_GENRE, EMPTY_GENRE}; 1270 expectedEncoded = DELIMITER + DELIMITER; 1271 checkGenreEncodeDecode(genres, expectedEncoded, 0); 1272 } 1273 1274 public void testProgramsGenresEncodeDecode_simpleDelimiter() { 1275 if (!Utils.hasTvInputFramework(getContext())) { 1276 return; 1277 } 1278 String[] genres = new String[] {EMPTY_GENRE, 1279 COMMA, 1280 QUOTE, 1281 COMMA + QUOTE, 1282 QUOTE + COMMA, 1283 COMMA + COMMA, 1284 QUOTE + QUOTE, 1285 COMMA + QUOTE + COMMA, 1286 QUOTE + COMMA + QUOTE}; 1287 String expectedEncoded = 1288 DELIMITER + COMMA_ENCODED 1289 + DELIMITER + QUOTE_ENCODED 1290 + DELIMITER + COMMA_ENCODED + QUOTE_ENCODED 1291 + DELIMITER + QUOTE_ENCODED + COMMA_ENCODED 1292 + DELIMITER + COMMA_ENCODED + COMMA_ENCODED 1293 + DELIMITER + QUOTE_ENCODED + QUOTE_ENCODED 1294 + DELIMITER + COMMA_ENCODED + QUOTE_ENCODED + COMMA_ENCODED 1295 + DELIMITER + QUOTE_ENCODED + COMMA_ENCODED + QUOTE_ENCODED; 1296 checkGenreEncodeDecode(genres, expectedEncoded, genres.length - 1); 1297 } 1298 1299 public void testProgramsGenresEncodeDecode_delimiterWithWhiteSpace() { 1300 if (!Utils.hasTvInputFramework(getContext())) { 1301 return; 1302 } 1303 String[] genres = new String[] {EMPTY_GENRE, 1304 COMMA + WHITE_SPACES, 1305 QUOTE + WHITE_SPACES, 1306 WHITE_SPACES + COMMA, 1307 WHITE_SPACES + QUOTE, 1308 WHITE_SPACES + COMMA + WHITE_SPACES, 1309 WHITE_SPACES + QUOTE + WHITE_SPACES}; 1310 String expectedEncoded = 1311 DELIMITER + COMMA_ENCODED + WHITE_SPACES 1312 + DELIMITER + QUOTE_ENCODED + WHITE_SPACES 1313 + DELIMITER + WHITE_SPACES + COMMA_ENCODED 1314 + DELIMITER + WHITE_SPACES + QUOTE_ENCODED 1315 + DELIMITER + WHITE_SPACES + COMMA_ENCODED + WHITE_SPACES 1316 + DELIMITER + WHITE_SPACES + QUOTE_ENCODED + WHITE_SPACES; 1317 checkGenreEncodeDecode(genres, expectedEncoded, genres.length - 1); 1318 } 1319 1320 public void testProgramsGenresEncodeDecode_all() { 1321 if (!Utils.hasTvInputFramework(getContext())) { 1322 return; 1323 } 1324 String[] genres = new String[] {EMPTY_GENRE, 1325 Genres.COMEDY, 1326 Genres.COMEDY + COMMA, 1327 Genres.COMEDY + COMMA + Genres.COMEDY, 1328 COMMA + Genres.COMEDY + COMMA, 1329 QUOTE + Genres.COMEDY + QUOTE, 1330 Genres.COMEDY + COMMA + WHITE_SPACES, 1331 Genres.COMEDY + COMMA + Genres.COMEDY + WHITE_SPACES, 1332 COMMA + Genres.COMEDY + COMMA + WHITE_SPACES, 1333 QUOTE + Genres.COMEDY + QUOTE + WHITE_SPACES 1334 }; 1335 String expectedEncoded = 1336 DELIMITER + Genres.COMEDY 1337 + DELIMITER + Genres.COMEDY + COMMA_ENCODED 1338 + DELIMITER + Genres.COMEDY + COMMA_ENCODED + Genres.COMEDY 1339 + DELIMITER + COMMA_ENCODED + Genres.COMEDY + COMMA_ENCODED 1340 + DELIMITER + QUOTE_ENCODED + Genres.COMEDY + QUOTE_ENCODED 1341 + DELIMITER + Genres.COMEDY + COMMA_ENCODED + WHITE_SPACES 1342 + DELIMITER + Genres.COMEDY + COMMA_ENCODED + Genres.COMEDY + WHITE_SPACES 1343 + DELIMITER + COMMA_ENCODED + Genres.COMEDY + COMMA_ENCODED + WHITE_SPACES 1344 + DELIMITER + QUOTE_ENCODED + Genres.COMEDY + QUOTE_ENCODED + WHITE_SPACES; 1345 checkGenreEncodeDecode(genres, expectedEncoded, genres.length - 1); 1346 } 1347 1348 private void checkGenreEncodeDecode(String[] genres, String expectedEncoded, 1349 int expectedDecodedLength) { 1350 String encoded = Genres.encode(genres); 1351 assertEquals(expectedEncoded, encoded); 1352 String[] decoded = Genres.decode(encoded); 1353 assertEquals(expectedDecodedLength, decoded.length); 1354 int decodedIndex = 0; 1355 for (int i = 0; i < genres.length; ++i) { 1356 String original = genres[i].trim(); 1357 if (!original.isEmpty()) { 1358 assertEquals(original, decoded[decodedIndex++]); 1359 } 1360 } 1361 } 1362 1363 private Uri insertProgramWithBroadcastGenre(String[] broadcastGenre) { 1364 ContentValues values = createDummyChannelValues(mInputId, false); 1365 Uri channelUri = mContentResolver.insert(Channels.CONTENT_URI, values); 1366 long channelId = ContentUris.parseId(channelUri); 1367 long curTime = System.currentTimeMillis(); 1368 values = new ContentValues(); 1369 values.put(Programs.COLUMN_CHANNEL_ID, channelId); 1370 values.put(Programs.COLUMN_BROADCAST_GENRE, Genres.encode(broadcastGenre)); 1371 values.put(Programs.COLUMN_START_TIME_UTC_MILLIS, curTime - 60000); 1372 values.put(Programs.COLUMN_END_TIME_UTC_MILLIS, curTime + 60000); 1373 Uri programUri = mContentResolver.insert(Programs.CONTENT_URI, values); 1374 assertNotNull(programUri); 1375 return programUri; 1376 } 1377 1378 private void verifyChannelCountWithCanonicalGenre(String canonicalGenre, int expectedCount) { 1379 Uri channelUri = TvContract.buildChannelsUriForInput(mInputId).buildUpon() 1380 .appendQueryParameter(PARAM_CANONICAL_GENRE, canonicalGenre).build(); 1381 try (Cursor c = mContentResolver.query(channelUri, new String[] {Channels._ID}, null, null, 1382 null)) { 1383 assertNotNull(c); 1384 assertEquals("Query:{Uri=" + channelUri + "}", expectedCount, c.getCount()); 1385 } 1386 } 1387 1388 public void testBroadcastGenreEncodeDecode() { 1389 if (!Utils.hasTvInputFramework(getContext())) { 1390 return; 1391 } 1392 String[] broadcastGenre = new String[] {"Animation", "Classic, opera"}; 1393 insertProgramWithBroadcastGenre(broadcastGenre); 1394 try (Cursor c = mContentResolver.query(Programs.CONTENT_URI, 1395 new String[] {Programs.COLUMN_BROADCAST_GENRE}, null, null, null)) { 1396 assertNotNull(c); 1397 assertEquals(1, c.getCount()); 1398 c.moveToNext(); 1399 MoreAsserts.assertEquals(broadcastGenre, Genres.decode(c.getString(0))); 1400 } 1401 } 1402 1403 public void testBroadcastGenreQueryChannel() { 1404 if (!Utils.hasTvInputFramework(getContext())) { 1405 return; 1406 } 1407 // "Animation" is mapped to Genres.MOVIES 1408 // "Classic, opera" is mapped to Genres.MUSIC 1409 insertProgramWithBroadcastGenre(new String[]{"Animation"}); 1410 insertProgramWithBroadcastGenre(new String[] {"Classic, opera"}); 1411 insertProgramWithBroadcastGenre(new String[]{"Animation", "Classic, opera"}); 1412 // There are two channels which belong to MOVIES genre - channel 1 and 3. 1413 verifyChannelCountWithCanonicalGenre(Genres.MOVIES, 2); 1414 // There are two channels which belong to MUSIC genre - channel 2 and 3. 1415 verifyChannelCountWithCanonicalGenre(Genres.MUSIC, 2); 1416 } 1417 1418 public void testGenresIsCanonical() { 1419 if (!Utils.hasTvInputFramework(getContext())) { 1420 return; 1421 } 1422 assertTrue(Genres.isCanonical(Genres.DRAMA)); 1423 assertFalse(Genres.isCanonical("Not a genre")); 1424 } 1425 1426 public void testUriUtils() { 1427 if (!Utils.hasTvInputFramework(getContext())) { 1428 return; 1429 } 1430 final Uri CHANNEL_URI_FOR_TUNER = TvContract.buildChannelUri(0); 1431 final Uri CHANNEL_URI_FOR_PASSTHROUGH_INPUT = 1432 TvContract.buildChannelUriForPassthroughInput("inputId"); 1433 final Uri PROGRAM_URI = TvContract.buildProgramUri(0); 1434 final Uri RECORDED_PROGRAM_URI = TvContract.buildRecordedProgramUri(0); 1435 1436 // Test isChannelUri 1437 assertTrue(TvContract.isChannelUri(CHANNEL_URI_FOR_TUNER)); 1438 assertTrue(TvContract.isChannelUri(CHANNEL_URI_FOR_PASSTHROUGH_INPUT)); 1439 assertFalse(TvContract.isChannelUri(PROGRAM_URI)); 1440 assertFalse(TvContract.isChannelUri(RECORDED_PROGRAM_URI)); 1441 assertFalse(TvContract.isChannelUri(null)); 1442 1443 // Test isChannelUriForPassthroughInput 1444 assertFalse(TvContract.isChannelUriForPassthroughInput(CHANNEL_URI_FOR_TUNER)); 1445 assertTrue(TvContract.isChannelUriForPassthroughInput(CHANNEL_URI_FOR_PASSTHROUGH_INPUT)); 1446 assertFalse(TvContract.isChannelUriForPassthroughInput(PROGRAM_URI)); 1447 assertFalse(TvContract.isChannelUriForPassthroughInput(RECORDED_PROGRAM_URI)); 1448 assertFalse(TvContract.isChannelUriForPassthroughInput(null)); 1449 1450 // Test isChannelUriForTunerInput 1451 assertTrue(TvContract.isChannelUriForTunerInput(CHANNEL_URI_FOR_TUNER)); 1452 assertFalse(TvContract.isChannelUriForTunerInput(CHANNEL_URI_FOR_PASSTHROUGH_INPUT)); 1453 assertFalse(TvContract.isChannelUriForTunerInput(PROGRAM_URI)); 1454 assertFalse(TvContract.isChannelUriForTunerInput(RECORDED_PROGRAM_URI)); 1455 assertFalse(TvContract.isChannelUriForTunerInput(null)); 1456 1457 // Test isProgramUri 1458 assertFalse(TvContract.isProgramUri(CHANNEL_URI_FOR_TUNER)); 1459 assertFalse(TvContract.isProgramUri(CHANNEL_URI_FOR_PASSTHROUGH_INPUT)); 1460 assertTrue(TvContract.isProgramUri(PROGRAM_URI)); 1461 assertFalse(TvContract.isProgramUri(RECORDED_PROGRAM_URI)); 1462 assertFalse(TvContract.isProgramUri(null)); 1463 1464 // Test isRecordedProgramUri 1465 assertFalse(TvContract.isRecordedProgramUri(CHANNEL_URI_FOR_TUNER)); 1466 assertFalse(TvContract.isRecordedProgramUri(CHANNEL_URI_FOR_PASSTHROUGH_INPUT)); 1467 assertFalse(TvContract.isRecordedProgramUri(PROGRAM_URI)); 1468 assertTrue(TvContract.isRecordedProgramUri(RECORDED_PROGRAM_URI)); 1469 assertFalse(TvContract.isRecordedProgramUri(null)); 1470 } 1471 } 1472