Calendar Provider: Permission Denial on Android 8
Asked Answered
H

1

3

I'm experiencing app crashes on Oreo Android 8.0/8.1 devices when using the calendar provider. The problem does not show on my devices running Nougat and below.

Permission Denial: reading com.android.providers.calendar.CalendarProvider2 uri content://com.android.calendar/calendars from pid=8522, uid=10197 requires android.permission.READ_CALENDAR, or grantUriPermission()

What puzzles me is that crashes happen even though the system requests persmissions in runtime, they are confirmed by the user, and they can be verified in the system settings. Even checking permissions before access (see my code below) does not help. Access is confirmed - and then denied.

In my AndroidManifest:

<manifest ...>
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
    <application>
        ...
    </application>
</manifest>

As an example (the problem occurs also for other queries), I create a calendar using this method:

public static void createCalendarIfNotExists(android.app.Activity activity) throws SecurityException {

    ContentResolver contentResolver = activity.getContentResolver();

    // Check if calendar already exists.
    String[] projection = new String[]{
                    CalendarContract.Calendars._ID,
                    CalendarContract.Calendars.CALENDAR_DISPLAY_NAME};

    if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_CALENDAR) == PackageManager.PERMISSION_GRANTED) {
        Cursor cursor = contentResolver.query(CalendarContract.Calendars.CONTENT_URI,
                projection,
                CalendarContract.Calendars.CALENDAR_DISPLAY_NAME + " = ?",
                new String[]{CALENDAR_NAME},
                null);

        if (cursor != null) {
            if (cursor.getCount() == 0) {
                final ContentValues cv = buildNewCalContentValues(activity);
                Uri calUri = buildCalUri();
                // Insert the calendar into the database.
                contentResolver.insert(calUri, cv);
            }
            if (!cursor.isClosed()) {
                cursor.close();
            }
        }
    }
}

Despite checking calendar permissions with result GRANTED the query fails due to lack of permissions (the request for permissions is done outside this function but the permissions are verified here before running the query).

Why is this happening, and why does it seem to start happening with Android Version 8?

Hallo answered 17/12, 2017 at 12:18 Comment(0)
A
8

Why is this happening

You are checking for the wrong permission. The error says that you need READ_CALENDAR; you are checking for WRITE_CALENDAR. While you do not show the requestPermissions() call, my guess is that you are only requesting WRITE_CALENDAR.

why does it seem to start happening with Android Version 8?

They tightened up security. Quoting the documentation:

Prior to Android 8.0 (API level 26), if an app requested a permission at runtime and the permission was granted, the system also incorrectly granted the app the rest of the permissions that belonged to the same permission group, and that were registered in the manifest.

For apps targeting Android 8.0, this behavior has been corrected. The app is granted only the permissions it has explicitly requested. However, once the user grants a permission to the app, all subsequent requests for permissions in that permission group are automatically granted.

For example, suppose an app lists both READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE in its manifest. The app requests READ_EXTERNAL_STORAGE and the user grants it. If the app targets API level 25 or lower, the system also grants WRITE_EXTERNAL_STORAGE at the same time, because it belongs to the same STORAGE permission group and is also registered in the manifest. If the app targets Android 8.0 (API level 26), the system grants only READ_EXTERNAL_STORAGE at that time; however, if the app later requests WRITE_EXTERNAL_STORAGE, the system immediately grants that privilege without prompting the user.

Alysaalyse answered 17/12, 2017 at 12:25 Comment(2)
Thank you. This makes total sense. I indeed assumed that, as before, write permissions included read permissions. There is a lot of info available regarding the changes in API 23, so I must have somehow missed this info. I'll try to update my app accordingly and let you know (accept answer) then.Hallo
@jerry: "I indeed assumed that, as before, write permissions included read permissions" -- prior to Android 6.0 and the switch to the runtime permission model, AFAIK, write did imply read. Android 6.0 broke that... but since they put the permissions in the same group, you wound up with code that still worked. Now, we gotta be more specific about what we want in terms of permissions. The UX does not change -- the user doesn't get more permission-confirmation dialogs.Alysaalyse

© 2022 - 2025 — McMap. All rights reserved.