所以我刚刚为我的应用程序实现了一个小部件。它通过我的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的提供程序与小部件一起使用呢?还是您被迫使用不安全的导出提供程序?
我能在这里找到答案,这要归功于Kostya Vasilyev。
虽然上下文实际上是正确的,但它必然会进入错误的过程。
所以关键是要做到这一点:
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 whatever here
// ...
// Restore the identity - not sure if it's needed since we're going
// to return right here, but it just *seems* cleaner
Binder.restoreCallingIdentity(identityToken);
}
很可能是因为您使用的是小部件的上下文,也就是主屏幕的上下文。尝试代码中的以下行:
mCursor = ListWidgetService.this.getContentResolver().query(
NotePadProvider.CONTENT_VISIBLE_URI, PROJECTION, listWhere, null,
sortOn);
请注意我是如何更换mContext.
的。服务是它自己的上下文,根据您的清单,它应该具有正确的权限。