So I've just implemented a widget for my app. It gets its data from the database through my ContentProvider
. I define my own read/write-permissions in my manifest, state that I use them (doesn't seem to make a difference), and require them in the content provider:
<!-- 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>
I update my widget through a Service
(as per the widget tutorial):
<!-- 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" />
And that Service
in turn does this (with a bunch of code not included):
/**
* 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);
}
}
}
So here's the problem: when I add the widget to the homescreen, a SecurityException
is immediately thrown inside the onDataSetChanged()
method when I try to query the provider. This appears to be because the homescreen does not hold the permission to read my content provider. My intuition was that it would not be a problem since my own app has the permission, and the service is obviously a part of my app.
Everything works beautifully if I remove the ReadPermission requirement from the provider. But that seems like a security problem because then any app can access the provider without the user having to approve anything.
So is there a way to use a provider with a ReadPermission with a widget? Or are you forced to use an unsecured exported provider?
A content provider manages access to a central repository of data. A provider is part of an Android application, which often provides its own UI for working with the data. However, content providers are primarily intended to be used by other applications, which access the provider using a provider client object.
Answer to your question is a android:protectionLevel property of a permission. You can set it to signature so only applications that signed with same key will be able to request this permissions.
Content Resolver resolves a URI to a specific Content provider. Content Provider provides an interface to query content. The way to query a content provider is contentResolverInstance.
I was able to find the answer here, credit goes to Kostya Vasilyev.
While the context is actually correct, it is bound to the wrong process.
So the key is to do this:
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With