Search code examples
androidandroid-contentresolverandroid-tv

Live Channels - Query Returns Security Exception


Aside from the unexplained Sample TV Input app (https://github.com/googlesamples/androidtv-sample-inputs/) there's not a lot of documentation on how to create a live channel in the Live Channels app.

I use a ContentResolver to insert channel info, but if I try to update this info using the update method, I get an error:

07-13 03:25:55.695  29194-29194/com.felkertech.n.cumulustv E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.felkertech.n.cumulustv, PID: 29194
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.felkertech.n.cumulustv/com.felkertech.n.cumulustv.SampleSetup}: java.lang.SecurityException: Selection not allowed for content://android.media.tv/channel
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2298)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
        at android.app.ActivityThread.access$800(ActivityThread.java:144)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
 Caused by: java.lang.SecurityException: Selection not allowed for content://android.media.tv/channel
        at android.os.Parcel.readException(Parcel.java:1540)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:185)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
        at android.content.ContentProviderProxy.update(ContentProviderNative.java:567)
        at android.content.ContentResolver.update(ContentResolver.java:1333)
        at com.felkertech.n.cumulustv.SampleSetup.onCreate(SampleSetup.java:81)
        at android.app.Activity.performCreate(Activity.java:5933)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2251)

             Here's the code for the querying that does not appear to work, even after several iterations of trial and error:

    values.put(TvContract.Channels.COLUMN_INPUT_ID, info);
    values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, channel.number);
    values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, channel.name);
    values.put(TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID, 0);
    values.put(TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID, 0);
    values.put(TvContract.Channels.COLUMN_SERVICE_ID, 1);
    values.put(TvContract.Channels.COLUMN_SERVICE_TYPE, TvContract.Channels.SERVICE_TYPE_AUDIO_VIDEO);
    values.put(TvContract.Channels.COLUMN_VIDEO_FORMAT, TvContract.Channels.VIDEO_FORMAT_1080P);
    values.put(TvContract.Channels.COLUMN_TYPE, TvContract.Channels.TYPE_OTHER);
    values.put(TvContract.Channels.COLUMN_VERSION_NUMBER, 2); //TODO So I need to reset

    String[] projection = {TvContract.Channels.COLUMN_INPUT_ID, TvContract.Channels.COLUMN_SERVICE_ID};
    Cursor cursor = null;
    try {
        cursor = getContentResolver().query(TvContract.Channels.CONTENT_URI, projection, null, null, null);
        if (cursor != null || cursor.getCount() > 1) {
            cursor.moveToNext();
            getContentResolver().update(TvContract.Channels.CONTENT_URI, values, "'"+TvContract.Channels.COLUMN_SERVICE_ID + "' = '1'", null);
        } else {
            Log.d(TAG, "Insert new");
        }
        getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    Uri uri = getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);

The error seems to come about from incorrect permissions, but I think they're already enabled (and I can insert fine):

<!-- Required to play internet-based streaming contents. -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- Required to register a SyncStatusObserver. -->
<uses-permission android:name="android.permission.READ_SYNC_STATS"/>
<!-- Required to enable our SyncAdapter after it's created. -->
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<!-- Required because we're manually creating a new account. -->
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<!-- Required to update or read existing channel and program information in TvProvider. -->
<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
<!-- Required to update channel and program information in TvProvider. -->
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />

Solution

  • The solution requires a bit of a different method of querying items using the content resolver.

    Something like querying android.media.tv where channel = 1 will create a security exception, but there is a workaround based on the example code.

    You can change the origin query to only include the channels that you want. For example, you can update or delete using the URI content://android.media.tv/channel/150