Виджет с поставщиком контента; невозможно использовать ReadPermission?

Итак, я только что реализовал виджет для своего приложения. Он получает свои данные из базы данных через мой ContentProvider. Я определяю свои собственные права на чтение/запись в своем манифесте, заявляю, что я их использую (не похоже на разницу) и требую их в поставщике контента:

<!-- Define my permissions for the provider -->
<permission
    android:name="com.nononsenseapps.notepad.permissions.read"
    android:description="@string/permission_read_desc"
    android:label="@string/permission_read_label"
    android:permissionGroup="android.permission-group.PERSONAL_INFO"
    android:protectionLevel="normal" />
<permission
    android:name="com.nononsenseapps.notepad.permissions.write"
    android:description="@string/permission_write_desc"
    android:label="@string/permission_write_label"
    android:permissionGroup="android.permission-group.PERSONAL_INFO"
    android:protectionLevel="normal" />
......
<uses-permission android:name="com.nononsenseapps.notepad.permissions.read" />
<uses-permission android:name="com.nononsenseapps.notepad.permissions.write" />
<uses-permission android:name="android.permission.BIND_REMOTEVIEWS" />
......
<provider
        android:name=".NotePadProvider"
        android:authorities="com.nononsenseapps.NotePad"
        android:enabled="true"
        android:exported="true"
        android:label="@string/app_name"
        android:readPermission="com.nononsenseapps.notepad.permissions.read"
        android:syncable="true"
        android:writePermission="com.nononsenseapps.notepad.permissions.write" >
        <grant-uri-permission android:pathPattern=".*" />
    </provider>

Я обновляю свой виджет через Service (согласно учебнику виджета):

    <!-- List Widget -->
    <receiver android:name="com.nononsenseapps.notepad.widget.ListWidgetProvider" >
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>

        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/listwidgetinfo" />
    </receiver>

    <service
        android:name="com.nononsenseapps.notepad.widget.ListWidgetService"
        android:exported="false"
        android:permission="android.permission.BIND_REMOTEVIEWS" />

И это Service в свою очередь делает это (с не включенной связкой кода):

/**
 * This is the service that provides the factory to be bound to the collection
 * service.
 */
public class ListWidgetService extends RemoteViewsService {

@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
    return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
}
}

/**
 * This is the factory that will provide data to the collection widget.
 */
class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

public StackRemoteViewsFactory(Context context, Intent intent) {
    mContext = context;
    observer = new ListChecker(null);
    mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
            AppWidgetManager.INVALID_APPWIDGET_ID);
}
public void onDataSetChanged() {
    Log.d(TAG, "onDataSetChanged");
    // Refresh the cursor
    if (mCursor != null) {
        mCursor.close();
    }

    // Get widget settings
    SharedPreferences settings = mContext.getSharedPreferences(
            ListWidgetConfigure.getSharedPrefsFile(mAppWidgetId),
            mContext.MODE_PRIVATE);
    if (settings != null) {
        String listWhere = settings.getString(ListWidgetConfigure.LIST_WHERE, null);
        listId  = settings.getLong(ListWidgetConfigure.LIST_ID, -1);
        String sortOn = settings.getString(ListWidgetConfigure.SORT_ON, NotePad.Notes.ALPHABETIC_ASC_ORDER);

        mCursor = mContext.getContentResolver().query(
                NotePadProvider.CONTENT_VISIBLE_URI, PROJECTION, listWhere, null,
                sortOn);
    }
}
}

Итак, вот проблема: когда я добавляю виджет на рабочий стол, при попытке запросить провайдера, SecurityException сразу же бросается внутрь метода onDataSetChanged(). Это связано с тем, что на рабочем столе нет разрешения на чтение моего поставщика контента. Моя интуиция заключалась в том, что это не будет проблемой, поскольку у моего собственного приложения есть разрешение, и сервис, очевидно, является частью моего приложения.

Все работает красиво, если я удалю требование ReadPermission от поставщика. Но это похоже на проблему безопасности, потому что тогда любое приложение может получить доступ к провайдеру без необходимости одобрять что-либо.

Итак, есть ли способ использовать провайдера с ReadPermission с виджетами? Или вы вынуждены использовать необеспеченного экспортированного поставщика?

Ответ 1

Я смог найти ответ здесь, кредит принадлежит Кости Васильеву.

Хотя контекст на самом деле правильный, он связан с неправильным процессом.

Итак, ключ должен сделать это:

public void onDataSetChanged() {
     // Revert back to our process' identity so we can work with our
     // content provider
     final long identityToken = Binder.clearCallingIdentity();

     // Update your cursor or whaterver here
     // ...

     // Restore the identity - not sure if it needed since we're going
     // to return right here, but it just *seems* cleaner
     Binder.restoreCallingIdentity(identityToken);
}

Ответ 2

Скорее всего, это потому, что вы используете контекст виджета, который будет отображаться на рабочем столе. Попробуйте следующую строку в коде:

mCursor = ListWidgetService.this.getContentResolver().query(
            NotePadProvider.CONTENT_VISIBLE_URI, PROJECTION, listWhere, null,
            sortOn);

Обратите внимание, как я заменил mContext.. Служба - это собственный контекст, который должен иметь правильное разрешение, учитывая ваш манифест.