blob: 596f5fa28d4bab8b2b9ff441d5351fa90500d33c [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.tv.cts;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
import android.media.tv.TvContract.Channels;
import android.media.tv.TvContract.Programs.Genres;
import android.media.tv.TvContract.RecordedPrograms;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
import android.tv.cts.R;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
/**
* Test for {@link android.media.tv.TvContract}.
*/
public class TvContractTest extends AndroidTestCase {
private static final String[] CHANNELS_PROJECTION = {
TvContract.Channels._ID,
TvContract.Channels.COLUMN_INPUT_ID,
TvContract.Channels.COLUMN_TYPE,
TvContract.Channels.COLUMN_SERVICE_TYPE,
TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID,
TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID,
TvContract.Channels.COLUMN_SERVICE_ID,
TvContract.Channels.COLUMN_DISPLAY_NUMBER,
TvContract.Channels.COLUMN_DISPLAY_NAME,
TvContract.Channels.COLUMN_NETWORK_AFFILIATION,
TvContract.Channels.COLUMN_DESCRIPTION,
TvContract.Channels.COLUMN_VIDEO_FORMAT,
TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA,
TvContract.Channels.COLUMN_VERSION_NUMBER,
};
private static final String[] PROGRAMS_PROJECTION = {
TvContract.Programs._ID,
TvContract.Programs.COLUMN_CHANNEL_ID,
TvContract.Programs.COLUMN_TITLE,
TvContract.Programs.COLUMN_SEASON_NUMBER,
TvContract.Programs.COLUMN_EPISODE_NUMBER,
TvContract.Programs.COLUMN_EPISODE_TITLE,
TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
TvContract.Programs.COLUMN_BROADCAST_GENRE,
TvContract.Programs.COLUMN_CANONICAL_GENRE,
TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
TvContract.Programs.COLUMN_LONG_DESCRIPTION,
TvContract.Programs.COLUMN_VIDEO_WIDTH,
TvContract.Programs.COLUMN_VIDEO_HEIGHT,
TvContract.Programs.COLUMN_AUDIO_LANGUAGE,
TvContract.Programs.COLUMN_CONTENT_RATING,
TvContract.Programs.COLUMN_POSTER_ART_URI,
TvContract.Programs.COLUMN_THUMBNAIL_URI,
TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA,
TvContract.Programs.COLUMN_VERSION_NUMBER,
};
private static long OPERATION_TIME = 1000l;
private static final String ENCODED_GENRE_STRING = Genres.ANIMAL_WILDLIFE + "," + Genres.COMEDY
+ "," + Genres.DRAMA + "," + Genres.EDUCATION + "," + Genres.FAMILY_KIDS + ","
+ Genres.GAMING + "," + Genres.MOVIES + "," + Genres.NEWS + "," + Genres.SHOPPING + ","
+ Genres.SPORTS + "," + Genres.TRAVEL;
// Delimiter for genre.
private static final String DELIMITER = ",";
private static final String EMPTY_GENRE = "";
private static final String COMMA = ",";
private static final String COMMA_ENCODED = "\",";
private static final String QUOTE = "\"";
private static final String QUOTE_ENCODED = "\"\"";
private static final String WHITE_SPACES = " \r \n \t \f ";
private static final String PARAM_CANONICAL_GENRE = "canonical_genre";
private String mInputId;
private ContentResolver mContentResolver;
private Uri mChannelsUri;
@Override
protected void setUp() throws Exception {
super.setUp();
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
mInputId = TvContract.buildInputId(
new ComponentName(getContext(), StubTunerTvInputService.class));
mContentResolver = getContext().getContentResolver();
mChannelsUri = TvContract.buildChannelsUriForInput(mInputId);
}
@Override
protected void tearDown() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
super.tearDown();
return;
}
// Clean up, just in case we failed to delete the entry when a test failed.
// The cotentUris are specific to this package, so this will delete only the
// entries inserted by this package.
String[] projection = { TvContract.Channels._ID };
try (Cursor cursor = mContentResolver.query(mChannelsUri, projection, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
long channelId = cursor.getLong(0);
mContentResolver.delete(
TvContract.buildProgramsUriForChannel(channelId), null, null);
}
}
mContentResolver.delete(mChannelsUri, null, null);
mContentResolver.delete(RecordedPrograms.CONTENT_URI, null, null);
super.tearDown();
}
private static ContentValues createDummyChannelValues(String inputId) {
ContentValues values = new ContentValues();
values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
values.put(TvContract.Channels.COLUMN_TYPE, TvContract.Channels.TYPE_OTHER);
values.put(TvContract.Channels.COLUMN_SERVICE_TYPE,
TvContract.Channels.SERVICE_TYPE_AUDIO_VIDEO);
values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "1");
values.put(TvContract.Channels.COLUMN_VIDEO_FORMAT, TvContract.Channels.VIDEO_FORMAT_480P);
return values;
}
private static ContentValues createDummyProgramValues(long channelId) {
ContentValues values = new ContentValues();
values.put(TvContract.Programs.COLUMN_CHANNEL_ID, channelId);
values.put(TvContract.Programs.COLUMN_EPISODE_TITLE, "Title");
values.put(TvContract.Programs.COLUMN_CANONICAL_GENRE, TvContract.Programs.Genres.encode(
TvContract.Programs.Genres.MOVIES, TvContract.Programs.Genres.DRAMA));
TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
"US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
values.put(TvContract.Programs.COLUMN_CONTENT_RATING, rating.flattenToString());
return values;
}
private static ContentValues createDummyRecordedProgramValues(long channelId) {
ContentValues values = new ContentValues();
values.put(TvContract.RecordedPrograms.COLUMN_CHANNEL_ID, channelId);
values.put(TvContract.RecordedPrograms.COLUMN_SEASON_NUMBER, 6);
values.put(TvContract.RecordedPrograms.COLUMN_EPISODE_NUMBER, 3);
values.put(TvContract.RecordedPrograms.COLUMN_EPISODE_TITLE, "episode_title");
values.put(TvContract.RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS, 1000);
values.put(TvContract.RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS, 2000);
values.put(TvContract.RecordedPrograms.COLUMN_CANONICAL_GENRE,
TvContract.Programs.Genres.encode(
TvContract.Programs.Genres.MOVIES, TvContract.Programs.Genres.DRAMA));
values.put(TvContract.RecordedPrograms.COLUMN_SHORT_DESCRIPTION, "short_description");
values.put(TvContract.RecordedPrograms.COLUMN_LONG_DESCRIPTION, "long_description");
values.put(TvContract.RecordedPrograms.COLUMN_VIDEO_WIDTH, 1920);
values.put(TvContract.RecordedPrograms.COLUMN_VIDEO_HEIGHT, 1080);
values.put(TvContract.RecordedPrograms.COLUMN_AUDIO_LANGUAGE, "en");
TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
"US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
values.put(TvContract.RecordedPrograms.COLUMN_CONTENT_RATING, rating.flattenToString());
values.put(TvContract.RecordedPrograms.COLUMN_POSTER_ART_URI, "http://foo.com/artwork.png");
values.put(TvContract.RecordedPrograms.COLUMN_THUMBNAIL_URI, "http://foo.com/thumbnail.jpg");
values.put(TvContract.RecordedPrograms.COLUMN_SEARCHABLE, 1);
values.put(TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_URI, "file:///sdcard/foo/");
values.put(TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_BYTES, 1024 * 1024);
values.put(TvContract.RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS, 60 * 60 * 1000);
values.put(TvContract.RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS, 1454480880L);
values.put(TvContract.RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
"internal_provider_data".getBytes());
values.put(TvContract.RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1, 4);
values.put(TvContract.RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2, 3);
values.put(TvContract.RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3, 2);
values.put(TvContract.RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4, 1);
return values;
}
private static void verifyStringColumn(Cursor cursor, ContentValues expectedValues,
String columnName) {
if (expectedValues.containsKey(columnName)) {
assertEquals(expectedValues.getAsString(columnName),
cursor.getString(cursor.getColumnIndex(columnName)));
}
}
private static void verifyIntegerColumn(Cursor cursor, ContentValues expectedValues,
String columnName) {
if (expectedValues.containsKey(columnName)) {
assertEquals(expectedValues.getAsInteger(columnName).intValue(),
cursor.getInt(cursor.getColumnIndex(columnName)));
}
}
private static void verifyLongColumn(Cursor cursor, ContentValues expectedValues,
String columnName) {
if (expectedValues.containsKey(columnName)) {
assertEquals(expectedValues.getAsLong(columnName).longValue(),
cursor.getLong(cursor.getColumnIndex(columnName)));
}
}
private static void verifyBlobColumn(Cursor cursor, ContentValues expectedValues,
String columnName) {
if (expectedValues.containsKey(columnName)) {
byte[] expected = expectedValues.getAsByteArray(columnName);
byte[] actual = cursor.getBlob(cursor.getColumnIndex(columnName));
assertEquals(expected.length, actual.length);
for (int i = 0; i < expected.length; ++i) {
assertEquals(expected[i], actual[i]);
}
}
}
private void verifyChannel(Uri channelUri, ContentValues expectedValues, long channelId) {
try (Cursor cursor = mContentResolver.query(
channelUri, CHANNELS_PROJECTION, null, null, null)) {
assertNotNull(cursor);
assertEquals(cursor.getCount(), 1);
assertTrue(cursor.moveToNext());
assertEquals(channelId, cursor.getLong(cursor.getColumnIndex(TvContract.Channels._ID)));
verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_INPUT_ID);
verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_TYPE);
verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_SERVICE_TYPE);
verifyIntegerColumn(cursor, expectedValues,
TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID);
verifyIntegerColumn(cursor, expectedValues,
TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID);
verifyIntegerColumn(cursor, expectedValues,
TvContract.Channels.COLUMN_SERVICE_ID);
verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_DISPLAY_NUMBER);
verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_DISPLAY_NAME);
verifyStringColumn(cursor, expectedValues,
TvContract.Channels.COLUMN_NETWORK_AFFILIATION);
verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_DESCRIPTION);
verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_VIDEO_FORMAT);
verifyBlobColumn(cursor, expectedValues,
TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA);
verifyIntegerColumn(cursor, expectedValues, TvContract.Channels.COLUMN_VERSION_NUMBER);
}
}
public void testChannelsTable() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
// Test: insert
ContentValues values = createDummyChannelValues(mInputId);
Uri rowUri = mContentResolver.insert(mChannelsUri, values);
long channelId = ContentUris.parseId(rowUri);
Uri channelUri = TvContract.buildChannelUri(channelId);
verifyChannel(channelUri, values, channelId);
// Test: update
values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "1-1");
values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "One dash one");
values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes());
mContentResolver.update(channelUri, values, null, null);
verifyChannel(channelUri, values, channelId);
// Test: delete
mContentResolver.delete(mChannelsUri, null, null);
try (Cursor cursor = mContentResolver.query(
mChannelsUri, CHANNELS_PROJECTION, null, null, null)) {
assertEquals(0, cursor.getCount());
}
}
private void verifyProgram(Uri programUri, ContentValues expectedValues, long programId) {
try (Cursor cursor = mContentResolver.query(
programUri, PROGRAMS_PROJECTION, null, null, null)) {
assertNotNull(cursor);
assertEquals(cursor.getCount(), 1);
assertTrue(cursor.moveToNext());
assertEquals(programId, cursor.getLong(cursor.getColumnIndex(TvContract.Programs._ID)));
verifyLongColumn(cursor, expectedValues, TvContract.Programs.COLUMN_CHANNEL_ID);
verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_TITLE);
verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_SEASON_NUMBER);
verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_EPISODE_NUMBER);
verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_EPISODE_TITLE);
verifyLongColumn(cursor, expectedValues,
TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS);
verifyLongColumn(cursor, expectedValues,
TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS);
verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_BROADCAST_GENRE);
verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_CANONICAL_GENRE);
verifyStringColumn(cursor, expectedValues,
TvContract.Programs.COLUMN_SHORT_DESCRIPTION);
verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_LONG_DESCRIPTION);
verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VIDEO_WIDTH);
verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VIDEO_HEIGHT);
verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_AUDIO_LANGUAGE);
verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_CONTENT_RATING);
verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_POSTER_ART_URI);
verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_THUMBNAIL_URI);
verifyBlobColumn(cursor, expectedValues,
TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA);
verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VERSION_NUMBER);
}
}
private void verifyLogoIsReadable(Uri logoUri) throws Exception {
try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "r")) {
try (InputStream is = fd.createInputStream()) {
// Assure that the stream is decodable as a Bitmap.
BitmapFactory.decodeStream(is);
}
}
}
public void testChannelLogo() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
// Set-up: add a channel.
ContentValues values = createDummyChannelValues(mInputId);
Uri channelUri = mContentResolver.insert(mChannelsUri, values);
Uri logoUri = TvContract.buildChannelLogoUri(channelUri);
Bitmap logo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.robot);
// Write
try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "w")) {
try (OutputStream os = fd.createOutputStream()) {
logo.compress(Bitmap.CompressFormat.PNG, 100, os);
}
}
// Give some time for TvProvider to process the logo.
Thread.sleep(OPERATION_TIME);
// Read and verify
verifyLogoIsReadable(logoUri);
// Read and verify using alternative logo URI.
verifyLogoIsReadable(TvContract.buildChannelLogoUri(ContentUris.parseId(channelUri)));
}
public void verifyProgramsTable(Uri programsUri, long channelId) {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
// Test: insert
ContentValues values = createDummyProgramValues(channelId);
Uri rowUri = mContentResolver.insert(programsUri, values);
long programId = ContentUris.parseId(rowUri);
Uri programUri = TvContract.buildProgramUri(programId);
verifyProgram(programUri, values, programId);
// Test: update
values.put(TvContract.Programs.COLUMN_EPISODE_TITLE, "Sample title");
values.put(TvContract.Programs.COLUMN_SHORT_DESCRIPTION, "Short description");
values.put(TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes());
mContentResolver.update(programUri, values, null, null);
verifyProgram(programUri, values, programId);
// Test: delete
mContentResolver.delete(programsUri, null, null);
try (Cursor cursor = mContentResolver.query(
programsUri, PROGRAMS_PROJECTION, null, null, null)) {
assertEquals(0, cursor.getCount());
}
}
public void testProgramsTable() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
// Set-up: add a channel.
ContentValues values = createDummyChannelValues(mInputId);
Uri channelUri = mContentResolver.insert(mChannelsUri, values);
long channelId = ContentUris.parseId(channelUri);
verifyProgramsTable(TvContract.buildProgramsUriForChannel(channelId), channelId);
verifyProgramsTable(TvContract.buildProgramsUriForChannel(channelUri), channelId);
}
private void verifyOverlap(long startMillis, long endMillis, int expectedCount,
long channelId, Uri channelUri) {
try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(
channelId, startMillis, endMillis), PROGRAMS_PROJECTION, null, null, null)) {
assertEquals(expectedCount, cursor.getCount());
}
try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(
channelUri, startMillis, endMillis), PROGRAMS_PROJECTION, null, null, null)) {
assertEquals(expectedCount, cursor.getCount());
}
}
public void testProgramsScheduleOverlap() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
final long programStartMillis = 1403712000000l; // Jun 25 2014 16:00 UTC
final long programEndMillis = 1403719200000l; // Jun 25 2014 18:00 UTC
final long hour = 3600000l;
// Set-up: add a channel and program.
ContentValues values = createDummyChannelValues(mInputId);
Uri channelUri = mContentResolver.insert(mChannelsUri, values);
long channelId = ContentUris.parseId(channelUri);
Uri programsUri = TvContract.buildProgramsUriForChannel(channelId);
values = createDummyProgramValues(channelId);
values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartMillis);
values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, programEndMillis);
mContentResolver.insert(programsUri, values);
// Overlap 1: starts early, ends early.
verifyOverlap(programStartMillis - hour, programEndMillis - hour, 1, channelId, channelUri);
// Overlap 2: starts early, ends late.
verifyOverlap(programStartMillis - hour, programEndMillis + hour, 1, channelId, channelUri);
// Overlap 3: starts early, ends late.
verifyOverlap(programStartMillis + hour / 2, programEndMillis - hour / 2, 1,
channelId, channelUri);
// Overlap 4: starts late, ends late.
verifyOverlap(programStartMillis + hour, programEndMillis + hour, 1, channelId, channelUri);
// Non-overlap 1: ends too early.
verifyOverlap(programStartMillis - hour, programStartMillis - hour / 2, 0,
channelId, channelUri);
// Non-overlap 2: starts too late
verifyOverlap(programEndMillis + hour, programEndMillis + hour * 2, 0,
channelId, channelUri);
}
private void verifyRecordedProgram(Uri recordedProgramUri, ContentValues expectedValues,
long recordedProgramId) {
try (Cursor cursor = mContentResolver.query(recordedProgramUri, null, null, null, null)) {
assertNotNull(cursor);
assertEquals(cursor.getCount(), 1);
assertTrue(cursor.moveToNext());
assertEquals(recordedProgramId, cursor.getLong(cursor.getColumnIndex(
RecordedPrograms._ID)));
verifyLongColumn(cursor, expectedValues, RecordedPrograms.COLUMN_CHANNEL_ID);
verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_SEASON_NUMBER);
verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_EPISODE_NUMBER);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_EPISODE_TITLE);
verifyLongColumn(cursor, expectedValues, RecordedPrograms.COLUMN_START_TIME_UTC_MILLIS);
verifyLongColumn(cursor, expectedValues, RecordedPrograms.COLUMN_END_TIME_UTC_MILLIS);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_BROADCAST_GENRE);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_CANONICAL_GENRE);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_SHORT_DESCRIPTION);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_LONG_DESCRIPTION);
verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_VIDEO_WIDTH);
verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_VIDEO_HEIGHT);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_AUDIO_LANGUAGE);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_CONTENT_RATING);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_POSTER_ART_URI);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_THUMBNAIL_URI);
verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_SEARCHABLE);
verifyStringColumn(cursor, expectedValues, RecordedPrograms.COLUMN_RECORDING_DATA_URI);
verifyIntegerColumn(cursor, expectedValues,
RecordedPrograms.COLUMN_RECORDING_DATA_BYTES);
verifyIntegerColumn(cursor, expectedValues,
RecordedPrograms.COLUMN_RECORDING_DURATION_MILLIS);
verifyIntegerColumn(cursor, expectedValues,
RecordedPrograms.COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS);
verifyBlobColumn(cursor, expectedValues,
RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA);
verifyIntegerColumn(cursor, expectedValues,
RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG1);
verifyIntegerColumn(cursor, expectedValues,
RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG2);
verifyIntegerColumn(cursor, expectedValues,
RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG3);
verifyIntegerColumn(cursor, expectedValues,
RecordedPrograms.COLUMN_INTERNAL_PROVIDER_FLAG4);
verifyIntegerColumn(cursor, expectedValues, RecordedPrograms.COLUMN_VERSION_NUMBER);
}
}
private void verifyRecordedProgramsTable(Uri recordedProgramsUri, long channelId) {
// Test: insert
ContentValues values = createDummyRecordedProgramValues(channelId);
Uri rowUri = mContentResolver.insert(recordedProgramsUri, values);
long recordedProgramId = ContentUris.parseId(rowUri);
Uri recordedProgramUri = TvContract.buildRecordedProgramUri(recordedProgramId);
verifyRecordedProgram(recordedProgramUri, values, recordedProgramId);
// Test: update
values.put(TvContract.RecordedPrograms.COLUMN_EPISODE_TITLE, "episode_title1");
values.put(TvContract.RecordedPrograms.COLUMN_SHORT_DESCRIPTION, "short_description1");
values.put(TvContract.RecordedPrograms.COLUMN_INTERNAL_PROVIDER_DATA,
"internal_provider_data1".getBytes());
mContentResolver.update(recordedProgramUri, values, null, null);
verifyRecordedProgram(recordedProgramUri, values, recordedProgramId);
// Test: delete
mContentResolver.delete(recordedProgramUri, null, null);
try (Cursor cursor = mContentResolver.query(recordedProgramsUri, null, null, null, null)) {
assertEquals(0, cursor.getCount());
}
}
public void testRecordedProgramsTable() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
// Set-up: add a channel.
ContentValues values = createDummyChannelValues(mInputId);
Uri channelUri = mContentResolver.insert(mChannelsUri, values);
long channelId = ContentUris.parseId(channelUri);
verifyRecordedProgramsTable(TvContract.RecordedPrograms.CONTENT_URI, channelId);
}
private void verifyQueryWithSortOrder(Uri uri, final String[] projection,
String sortOrder) throws Exception {
try {
getContext().getContentResolver().query(uri, projection, null, null, sortOrder);
} catch (SecurityException e) {
fail("Setting sort order shoud be allowed for " + uri);
}
}
private void verifyQueryWithSelection(Uri uri, final String[] projection,
String selection) throws Exception {
try {
getContext().getContentResolver().query(uri, projection, selection, null, null);
fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
} catch (SecurityException e) {
// Expected exception
}
}
private void verifyUpdateWithSelection(Uri uri, String selection) throws Exception {
try {
ContentValues values = new ContentValues();
getContext().getContentResolver().update(uri, values, selection, null);
fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
} catch (SecurityException e) {
// Expected exception
}
}
private void verifyDeleteWithSelection(Uri uri, String selection) throws Exception {
try {
getContext().getContentResolver().delete(uri, selection, null);
fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
} catch (SecurityException e) {
// Expected exception
}
}
public void testAllEpgPermissionBlocksSortOrderOnQuery_Channels() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
final String[] projection = { TvContract.Channels._ID };
verifyQueryWithSortOrder(TvContract.Channels.CONTENT_URI, projection,
TvContract.Channels._ID + " ASC");
}
public void testAllEpgPermissionBlocksSelectionOnQuery_Channels() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
final String[] projection = { TvContract.Channels._ID };
verifyQueryWithSelection(TvContract.Channels.CONTENT_URI, projection,
TvContract.Channels._ID + ">0");
}
public void testAllEpgPermissionBlocksSelectionOnUpdate_Channels() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
verifyUpdateWithSelection(TvContract.Channels.CONTENT_URI,
TvContract.Channels._ID + ">0");
}
public void testAllEpgPermissionBlocksSelectionOnDelete_Channels() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
verifyDeleteWithSelection(TvContract.Channels.CONTENT_URI,
TvContract.Channels._ID + ">0");
}
public void testAllEpgPermissionBlocksSortOrderOnQuery_Programs() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
final String[] projection = { TvContract.Programs._ID };
verifyQueryWithSortOrder(TvContract.Programs.CONTENT_URI, projection,
TvContract.Programs._ID + " ASC");
}
public void testAllEpgPermissionBlocksSelectionOnQuery_Programs() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
final String[] projection = { TvContract.Channels._ID };
verifyQueryWithSelection(TvContract.Programs.CONTENT_URI, projection,
TvContract.Programs._ID + ">0");
}
public void testAllEpgPermissionBlocksSelectionOnUpdate_Programs() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
verifyUpdateWithSelection(TvContract.Programs.CONTENT_URI,
TvContract.Programs._ID + ">0");
}
public void testAllEpgPermissionBlocksSelectionOnDelete_Programs() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
verifyDeleteWithSelection(TvContract.Programs.CONTENT_URI,
TvContract.Programs._ID + ">0");
}
public void testDefaultValues() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
ContentValues values = new ContentValues();
values.put(TvContract.Channels.COLUMN_INPUT_ID, mInputId);
Uri channelUri = mContentResolver.insert(mChannelsUri, values);
assertNotNull(channelUri);
try (Cursor cursor = mContentResolver.query(
channelUri, CHANNELS_PROJECTION, null, null, null)) {
cursor.moveToNext();
assertEquals(TvContract.Channels.TYPE_OTHER,
cursor.getString(cursor.getColumnIndex(TvContract.Channels.COLUMN_TYPE)));
assertEquals(TvContract.Channels.SERVICE_TYPE_AUDIO_VIDEO,
cursor.getString(cursor.getColumnIndex(
TvContract.Channels.COLUMN_SERVICE_TYPE)));
}
values.clear();
}
public void testChannelsGetVideoResolution() {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution(
Channels.VIDEO_FORMAT_480I));
assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution(
Channels.VIDEO_FORMAT_480P));
assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution(
Channels.VIDEO_FORMAT_576I));
assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution(
Channels.VIDEO_FORMAT_576P));
assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution(
Channels.VIDEO_FORMAT_720P));
assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution(
Channels.VIDEO_FORMAT_1080I));
assertEquals(Channels.VIDEO_RESOLUTION_FHD, Channels.getVideoResolution(
Channels.VIDEO_FORMAT_1080P));
assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution(
Channels.VIDEO_FORMAT_2160P));
assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution(
Channels.VIDEO_FORMAT_4320P));
assertEquals(null, Channels.getVideoResolution("Unknown format"));
}
public void testProgramsGenresDecode() {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
List genres = Arrays.asList(Genres.decode(ENCODED_GENRE_STRING));
assertEquals(11, genres.size());
assertTrue(genres.contains(Genres.ANIMAL_WILDLIFE));
assertTrue(genres.contains(Genres.COMEDY));
assertTrue(genres.contains(Genres.DRAMA));
assertTrue(genres.contains(Genres.EDUCATION));
assertTrue(genres.contains(Genres.FAMILY_KIDS));
assertTrue(genres.contains(Genres.GAMING));
assertTrue(genres.contains(Genres.MOVIES));
assertTrue(genres.contains(Genres.NEWS));
assertTrue(genres.contains(Genres.SHOPPING));
assertTrue(genres.contains(Genres.SPORTS));
assertTrue(genres.contains(Genres.TRAVEL));
assertFalse(genres.contains(","));
}
public void testProgramsGenresEncode() {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
assertEquals(ENCODED_GENRE_STRING, Genres.encode(Genres.ANIMAL_WILDLIFE,
Genres.COMEDY, Genres.DRAMA, Genres.EDUCATION, Genres.FAMILY_KIDS, Genres.GAMING,
Genres.MOVIES, Genres.NEWS, Genres.SHOPPING, Genres.SPORTS, Genres.TRAVEL));
}
public void testProgramsGenresEncodeDecode_empty() {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
String[] genres = new String[] {EMPTY_GENRE};
String expectedEncoded = EMPTY_GENRE;
checkGenreEncodeDecode(genres, expectedEncoded, 0);
genres = new String[] {EMPTY_GENRE, EMPTY_GENRE, EMPTY_GENRE};
expectedEncoded = DELIMITER + DELIMITER;
checkGenreEncodeDecode(genres, expectedEncoded, 0);
}
public void testProgramsGenresEncodeDecode_simpleDelimiter() {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
String[] genres = new String[] {EMPTY_GENRE,
COMMA,
QUOTE,
COMMA + QUOTE,
QUOTE + COMMA,
COMMA + COMMA,
QUOTE + QUOTE,
COMMA + QUOTE + COMMA,
QUOTE + COMMA + QUOTE};
String expectedEncoded =
DELIMITER + COMMA_ENCODED
+ DELIMITER + QUOTE_ENCODED
+ DELIMITER + COMMA_ENCODED + QUOTE_ENCODED
+ DELIMITER + QUOTE_ENCODED + COMMA_ENCODED
+ DELIMITER + COMMA_ENCODED + COMMA_ENCODED
+ DELIMITER + QUOTE_ENCODED + QUOTE_ENCODED
+ DELIMITER + COMMA_ENCODED + QUOTE_ENCODED + COMMA_ENCODED
+ DELIMITER + QUOTE_ENCODED + COMMA_ENCODED + QUOTE_ENCODED;
checkGenreEncodeDecode(genres, expectedEncoded, genres.length - 1);
}
public void testProgramsGenresEncodeDecode_delimiterWithWhiteSpace() {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
String[] genres = new String[] {EMPTY_GENRE,
COMMA + WHITE_SPACES,
QUOTE + WHITE_SPACES,
WHITE_SPACES + COMMA,
WHITE_SPACES + QUOTE,
WHITE_SPACES + COMMA + WHITE_SPACES,
WHITE_SPACES + QUOTE + WHITE_SPACES};
String expectedEncoded =
DELIMITER + COMMA_ENCODED + WHITE_SPACES
+ DELIMITER + QUOTE_ENCODED + WHITE_SPACES
+ DELIMITER + WHITE_SPACES + COMMA_ENCODED
+ DELIMITER + WHITE_SPACES + QUOTE_ENCODED
+ DELIMITER + WHITE_SPACES + COMMA_ENCODED + WHITE_SPACES
+ DELIMITER + WHITE_SPACES + QUOTE_ENCODED + WHITE_SPACES;
checkGenreEncodeDecode(genres, expectedEncoded, genres.length - 1);
}
public void testProgramsGenresEncodeDecode_all() {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
String[] genres = new String[] {EMPTY_GENRE,
Genres.COMEDY,
Genres.COMEDY + COMMA,
Genres.COMEDY + COMMA + Genres.COMEDY,
COMMA + Genres.COMEDY + COMMA,
QUOTE + Genres.COMEDY + QUOTE,
Genres.COMEDY + COMMA + WHITE_SPACES,
Genres.COMEDY + COMMA + Genres.COMEDY + WHITE_SPACES,
COMMA + Genres.COMEDY + COMMA + WHITE_SPACES,
QUOTE + Genres.COMEDY + QUOTE + WHITE_SPACES
};
String expectedEncoded =
DELIMITER + Genres.COMEDY
+ DELIMITER + Genres.COMEDY + COMMA_ENCODED
+ DELIMITER + Genres.COMEDY + COMMA_ENCODED + Genres.COMEDY
+ DELIMITER + COMMA_ENCODED + Genres.COMEDY + COMMA_ENCODED
+ DELIMITER + QUOTE_ENCODED + Genres.COMEDY + QUOTE_ENCODED
+ DELIMITER + Genres.COMEDY + COMMA_ENCODED + WHITE_SPACES
+ DELIMITER + Genres.COMEDY + COMMA_ENCODED + Genres.COMEDY + WHITE_SPACES
+ DELIMITER + COMMA_ENCODED + Genres.COMEDY + COMMA_ENCODED + WHITE_SPACES
+ DELIMITER + QUOTE_ENCODED + Genres.COMEDY + QUOTE_ENCODED + WHITE_SPACES;
checkGenreEncodeDecode(genres, expectedEncoded, genres.length - 1);
}
private void checkGenreEncodeDecode(String[] genres, String expectedEncoded,
int expectedDecodedLength) {
String encoded = Genres.encode(genres);
assertEquals(expectedEncoded, encoded);
String[] decoded = Genres.decode(encoded);
assertEquals(expectedDecodedLength, decoded.length);
int decodedIndex = 0;
for (int i = 0; i < genres.length; ++i) {
String original = genres[i].trim();
if (!original.isEmpty()) {
assertEquals(original, decoded[decodedIndex++]);
}
}
}
private Uri insertProgramWithBroadcastGenre(String[] broadcastGenre) {
ContentValues values = createDummyChannelValues(mInputId);
Uri channelUri = mContentResolver.insert(Channels.CONTENT_URI, values);
long channelId = ContentUris.parseId(channelUri);
long curTime = System.currentTimeMillis();
values = new ContentValues();
values.put(TvContract.Programs.COLUMN_CHANNEL_ID, channelId);
values.put(TvContract.Programs.COLUMN_BROADCAST_GENRE, Genres.encode(broadcastGenre));
values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, curTime - 60000);
values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, curTime + 60000);
Uri programUri = mContentResolver.insert(TvContract.Programs.CONTENT_URI, values);
assertNotNull(programUri);
return programUri;
}
private void verifyChannelCountWithCanonicalGenre(String canonicalGenre, int expectedCount) {
Uri channelUri = TvContract.buildChannelsUriForInput(mInputId).buildUpon()
.appendQueryParameter(PARAM_CANONICAL_GENRE, canonicalGenre).build();
try (Cursor c = mContentResolver.query(channelUri, new String[] {Channels._ID}, null, null,
null)) {
assertNotNull(c);
assertEquals("Query:{Uri=" + channelUri + "}", expectedCount, c.getCount());
}
}
public void testBroadcastGenreEncodeDecode() {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
String[] broadcastGenre = new String[] {"Animation", "Classic, opera"};
insertProgramWithBroadcastGenre(broadcastGenre);
try (Cursor c = mContentResolver.query(TvContract.Programs.CONTENT_URI,
new String[] {TvContract.Programs.COLUMN_BROADCAST_GENRE}, null, null, null)) {
assertNotNull(c);
assertEquals(1, c.getCount());
c.moveToNext();
MoreAsserts.assertEquals(broadcastGenre, Genres.decode(c.getString(0)));
}
}
public void testBroadcastGenreQueryChannel() {
if (!Utils.hasTvInputFramework(getContext())) {
return;
}
// "Animation" is mapped to Genres.MOVIES
// "Classic, opera" is mapped to Genres.MUSIC
insertProgramWithBroadcastGenre(new String[]{"Animation"});
insertProgramWithBroadcastGenre(new String[] {"Classic, opera"});
insertProgramWithBroadcastGenre(new String[]{"Animation", "Classic, opera"});
// There are two channels which belong to MOVIES genre - channel 1 and 3.
verifyChannelCountWithCanonicalGenre(Genres.MOVIES, 2);
// There are two channels which belong to MUSIC genre - channel 2 and 3.
verifyChannelCountWithCanonicalGenre(Genres.MUSIC, 2);
}
}