content provider custom client - no content is recieved - android

I have read many answers regarding content providers, but I can't understand why my client isn't recieving any data stored in the database.
-I'm using the same uri everywhere.
-I have added the read and write permissions in my provider in the manifest.
-I have exported the provider and set the multiprocess to "true" too.
-My client in app has the same read and write permissions.
-I created a jar containing the resolver for my client in app. I have added it as a library.
-I know that the parameters got into the database because I can see all the info inside the database.
-My client wasn't able to connect to the provider until I have done all mentioned above, because I fixed exception regarding it.
Now everything should work and doesn't crash any more, but all the strings I get from the resolver are empty. I don't know if it really conects to the provider or if something else is incorrect.
Please help me!
Q: Do the permission string and the authorities string in the provider declaration have to match the package structure?
Here are some pieces of code:
Provider declaration in manifest:
<provider
android:name=".lessons.LessonsProvider"
android:authorities="com.scheduler.lessons"
android:readPermission="com.lessons.READ_DATABASE"
android:writePermission="com.lessons.WRITE_DATABASE"
android:multiprocess="true"
android:exported="true" />
Resolver code:
public class LessonsResolver {
private static final String TAG = "LessonProvider";
private final String PROVIDER_NAME = "com.scheduler.lessons";
private Uri uri;
private Context mContext;
public LessonsResolver(Context context) {
super();
mContext = context;
uri = Uri.parse("content://"+ PROVIDER_NAME+"/parameters_table");
}
public String getString(String parameterName, String defaultValue) {
Cursor cursor = mContext.getContentResolver().query(uri, null, "parameter_name='"+parameterName+"'", null, null);
if (cursor == null || cursor.getCount() == 0) {
Log.e(TAG,"getString: for parameterName: " + parameterName + ", cursor from the content provider "+ (cursor == null ? "is NULL" : "is empty" ));
cursor.close();
return defaultValue;
}
cursor.moveToFirst();
String s = cursor.getString(2);
cursor.close();
return s;
}
Permissions in client app manifest:
<uses-permission android:name="com.lessons.READ_DATABASE"/>
<uses-permission android:name="com.lessons.WRITE_DATABASE"/>
My provider:
public class LessonsProvider extends ContentProvider {
public static final String PROVIDER_NAME = "com.scheduler.lessons";
private static final String TABLE_NAME = "lessons";
private static final String TAG = "LessonsProvider";
private static final Uri CONTENT_URI = Uri.parse("content://"+PROVIDER_NAME + "/parameters_table");
...
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
sqlBuilder.setTables(TABLE_NAME);
Cursor c = sqlBuilder.query(mSqlDB, projection, selection, selectionArgs, null, null, null);
return c;
}

Related

Failed to find provider info for com.example.serialprovider.provider.SampleProvider

I've been trying to get data from another app's custom ContentProvider class but I keep getting this error: Failed to find provider info for com.example.serialprovider.provider.SampleProvider..
I searched a lot for similar issues online but still didn't know what's wrong, I checked the manifest multiple times, and I even took a copy of the authorities attribute to use it in the receiver app but still, the receiver app can't find the provider.
Here's the declaration in the manifest:
<provider
android:name=".provider.SampleProvider"
android:authorities="com.example.serialprovider.provider.SampleProvider"
android:enabled="true"
android:exported="true" />
and here's the implementation of onCreate and query methods in the Provider class (I'm using RoomDatabase):
public class SampleProvider extends ContentProvider {
public SampleProvider() {
}
private static final String AUTHORITY = "com.example.serialprovider.provider.SampleProvider";
private static final String TABLE_NAME = "devicepin";
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY, TABLE_NAME, 1);
}
#Override
public boolean onCreate() {
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
if (sURIMatcher.match(uri) == 1) {
final Context context = getContext();
AppDao dao = DatabaseClient.getInstance(context).getAppDatabase().appDao();
final Cursor cursor = dao.get();
cursor.setNotificationUri(getContext().getContentResolver(), uri);
cursor.close();
return cursor;
} else {
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
}
and here's how I try to get the cursor in the other app "receiver":
private void getPin(){
new Thread(() -> {
ContentResolver resolver = getContentResolver();
try{
Cursor cursor = resolver.query(Uri.parse("content://com.example.serialprovider.provider.SampleProvider/devciepin"), null, null, null, null);
cursor.close();
}
catch (Exception e){
e.printStackTrace();
}
}).start();
}
cursor is always null, when I surround it with try and catch blocks, the "failed to find provider info" is what I get as an exception.
Turns out the code is alright, but there's some new restrictions that were introduced in Android 11 (API 30) when accessing the ContentProvider from another app.
Quoting the Documentation on Android 11 behavior changes:
If your app shares a content URI with another app, the intent must grant URI access permissions by setting at least one of the following intent flags: FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_WRITE_URI_PERMISSION. That way, if the other app targets Android 11, it can still access the content URI. Your app must include the intent flags even when the content URI is associated with a content provider that your app doesn't own.
If your app owns the content provider that's associated with the content URI, verify that the content provider isn't exported. We already recommend this security best practice.

Why is my DB empty after I insert an item using the ContentProvider in Xamarin?

I made a custom ContentProvider, and implemented the Insert method, like this:
ServiceDB _s_DB; // implements SQLiteOpenHelper, I'll add the code bellow
public const string AUTHORITY = "com.***.***.CustomProvider";
static string BASE_PATH = "accesstokens";
static string DATABASE_TABLE = "accesstokens";
public static Android.Net.Uri CONTENT_URI = Android.Net.Uri.Parse("content://" + AUTHORITY + "/" + BASE_PATH);
// MIME types used for getting a list, or a single access token
public const string MIME_TYPES = ContentResolver.CursorDirBaseType + "/vnd.com.***.***.AccessTokens";
public const string MIME_TYPE = ContentResolver.CursorItemBaseType + "/vnd.com.***.***.AccessTokens";
// Column names
public static class InterfaceConsts
{
public const string Id = "_id";
public const string Token = "token";
//ubaci exparation date
}
public override Android.Net.Uri Insert(Android.Net.Uri uri, ContentValues values)
{
Android.Util.Log.Debug("Test", "Insert");
//---add a new token---
var _database = _s_DB.WritableDatabase;
_database.BeginTransaction();
long rowID = _database.Insert(DATABASE_TABLE, "", values);
//---if added successfully---
if (rowID > 0)
{
var _uri = ContentUris.WithAppendedId(CONTENT_URI, rowID);
Context.ContentResolver.NotifyChange(_uri, null);
_database.SetTransactionSuccessful();
_database.EndTransaction();
return _uri;
}
throw new SQLException("Failed to insert row into " + uri);
}
My object ServiceDB _s_DB has this method implemented:
const string DatabaseName = "accesstokens.db";
const string DatabaseTable = "accesstokens";
const string create_table_sql = "CREATE TABLE " + DatabaseTable + " (_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, token TEXT NOT NULL UNIQUE)";
const int DatabaseVersion = 1;
public ServiceDB(Context context) : base(context, DatabaseName, null, DatabaseVersion) { }
public override void OnCreate(SQLiteDatabase db)
{
Android.Util.Log.Debug("Test", "Service Database Created");
db.ExecSQL(create_table_sql);
// seed with data
db.ExecSQL("INSERT INTO accesstokens (token) VALUES ('token1')");
db.ExecSQL("INSERT INTO accesstokens (token) VALUES ('token2')");
}
And finaly, my MainActivity class, where I first read the 2 automatically created tokens, then add a third token, then read all three again...
MainActivity onCreate has this:
string[] projection = new string[] { CustomProvider.InterfaceConsts.Id, CustomProvider.InterfaceConsts.Token};
string[] fromColumns = new string[] { CustomProvider.InterfaceConsts.Token };
// CursorLoader introduced in Honeycomb (3.0, API_11)
var loader = new CursorLoader(this, CustomProvider.CONTENT_URI, projection, null, null, null);
var cursor = (ICursor)loader.LoadInBackground();
if (cursor != null)
{
while (cursor.MoveToNext())
{
String s = cursor.GetString(cursor.GetColumnIndexOrThrow("token"));
Android.Util.Log.Debug("Test","aaa " + s);
}
cursor.Close();
}
Android.Util.Log.Debug("Test", "Create new item");
ContentValues content = new ContentValues();
content.Put(CustomProvider.InterfaceConsts.Id, "3");
content.Put(CustomProvider.InterfaceConsts.Token, "token3");
var ddd = ApplicationContext.ContentResolver.Insert(CustomProvider.CONTENT_URI, content);
Android.Util.Log.Debug("Test", "ddd: " + ddd);
ICursor c = ApplicationContext.ContentResolver.Query(CustomProvider.CONTENT_URI, null, null, null, null);
if (c != null)
{
while (c.MoveToNext())
{
String s = c.GetString(c.GetColumnIndexOrThrow("token"));
Android.Util.Log.Debug("Test","ccc " + s);
}
c.Close();
}
Then, I take my created DB from the box (I work on an AndroidTV) and open it with a DB Browser, and the DB is empty, even the table is not created!!
The output from the console shows:
aaa token1
aaa token2
Insert
ddd: content://com.***.***.CustomProvider/accesstokens/3
ccc token1
ccc token2
ccc token3
Again, I close the app, then extract the accesstokens.db from the tv, then open it with a DB Browser, and not even the table is created, and ofc no rows as well.
What am I missing?
Thank you for your time.
Edit 1:
It may have something to do with the permissions, so here are the providers attributes:
[ContentProvider(new string[] { CustomProvider.AUTHORITY }, Exported = true, GrantUriPermissions = true, Label ="CustomProvider")]
Edit 2:
I have managed to capture this error in the console:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.***.***/com.***.***.MainActivity}: android.database.SQLException: Failed to insert row into content://com.***.***.CustomProvider/accesstokens
I make come change with your code, it works well on my side. You could check the source file from the GitHub. I use a Listview to show the items in database.
GitHub:
https://github.com/WendyZang/Test/tree/master/SimpleContentProvider
I my custom ContentProvider: VegetableProvider, override the OnCreate method and make some change with the code.
custom ContentProvider: VegetableProvider
//---add a new book---
_s_DB = new VegetableDatabase(this.Context);
var _database = _s_DB.WritableDatabase;
MainActivity: To show the items in the listview.
cursor = ContentResolver.Query(VegetableProvider.CONTENT_URI, projection, null, null, null);
adapter = new SimpleCursorAdapter(this, Android.Resource.Layout.SimpleListItem1, cursor,
fromColumns, toControlIds);
listView.Adapter = adapter;
I do not have AndroidTV to test, you could check the code first.
Turns out, I asked for permission to the content provider in my main activity via this line:
ApplicationContext.ContentResolver.TakePersistableUriPermission(CustomProvider.CONTENT_URI, Android.Content.ActivityFlags.GrantWriteUriPermission);
But since, this is the app that originally has the content provider component that I am using, it does not need that permission, since it already has it by default.
So, removing it solved my issue.

Global variable defined in Content Provider and initialized in onCreate() is null in query()

I defined a content provider and made my database helper a global variable, which I initialize in the onCreate method. However, the value of this global variable is null, in the query() method.
This is my content provider:
/** Tag for the log messages */
public static final String LOG_TAG = FeedingProvider.class.getSimpleName();
// Declaring a FeedingDbHelper variable;
private FeedingDbHelper mDbHelper;
/** URI matcher code for the content URI for the pets table */
private static final int FEEDINGS = 100;
/** URI matcher code for the content URI for a single pet in the pets table */
private static final int FEEDING_ID = 101;
/**
* UriMatcher object to match a content URI to a corresponding code.
* The input passed into the constructor represents the code to return for the root URI.
*/
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Static initializer. This is run the first time anything is called from this class.
static {
// The calls to addURI() go here, for all of the content URI patterns that the provider
// should recognize. All paths added to the UriMatcher have a corresponding code to return
// when a match is found.
sUriMatcher.addURI(FeedingContract.CONTENT_AUTHORITY, FeedingContract.PATH_FEEDINGS,
FEEDINGS);
sUriMatcher.addURI(FeedingContract.CONTENT_AUTHORITY, FeedingContract.PATH_FEEDINGS +
"/#", FEEDING_ID);
}
/**
* Initialize the provider and the database helper object.
*/
#Override
public boolean onCreate() {
mDbHelper = new FeedingDbHelper(this.getContext());
// Make sure the variable is a global variable, so it can be referenced from other
// ContentProvider methods.
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Log.d(LOG_TAG, "Test in query: " + (mDbHelper == null));
SQLiteDatabase db = mDbHelper.getReadableDatabase();
Cursor cursor;
int match = sUriMatcher.match(uri);
switch (match) {
case FEEDINGS:
cursor = db.query(FeedingContract.FeedingEntry.TABLE_NAME, projection, selection,
selectionArgs, null, null, sortOrder);
break;
case FEEDING_ID:
selection = FeedingEntry._ID + "=?";
selectionArgs = new String[] {
String.valueOf(ContentUris.parseId(uri))
};
cursor = db.query(FeedingContract.FeedingEntry.TABLE_NAME, projection, selection,
selectionArgs, null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Can't find uri in PetProvider.query()");
}
Log.e("Gerke", "Cursor: " + DatabaseUtils.dumpCursorToString(cursor));
return cursor;
}
The resulting error message is:
"java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase com.example.android.smartfeed.data.FeedingDbHelper.getReadableDatabase()' on a null object reference"
My questions is: why is the global variable mDbHelper null and not an instance of my dbHelper?
Also tried saving the context to a global variable, but this also does not work.
I found my mistake:
The definition of the ContentProvider is fine, but when using it, I tried creating a new instance of it, which is wrong.
So for everyone else having my problem:
The communication with the ContentProvider is done via the ContentResolver. To get an instance of the ContentResolver, call getContentResolver();

App crashes when using Content Resolver and Content Provider

I have an activity class which uses the ContentResolver's query() method with Uri & projection[] being set as two of its arguments and rest are set as 'null'.
Like:
ContentResolver resolverCatalog = getContentResolver();
Cursor cursor = resolverCatalog.query(PetsEntry.CONTENT_URI,projection,null,null,null);
However in the ContentProvider class the query() method is defined as:
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// Making an instance of the SQLiteOpenHelper class named as 'SQLdbHelper'
SQLdbHelper PetdbHelper = new SQLdbHelper(getContext());
//getting access to database
SQLiteDatabase database_query = PetdbHelper.getReadableDatabase();
// This cursor will hold the result of the query
Cursor cursor_query;
// Figure out if the URI matcher can match the URI to a specific code
int match = sUriMatcher.match(uri);
switch (match) {
case PETS:
cursor_query = database_query.query(TABLE_NAME,projection,null,null,null,null,null);
break;
case PET_ID:
selection = PetContract.PetsEntry._ID + "=?";
selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
// This will perform a query on the pets table where the _id equals 3 to return a
// Cursor containing that row of the table.
cursor_query = database_query.query(TABLE_NAME, projection, selection, selectionArgs,
null, null, sortOrder);
break;
default:
throw new IllegalArgumentException("Cannot query unknown URI " + uri);
}
return cursor_query;
}
The PETS & PETS_ID are defined (within this ContentProvider class) as:
public class PetProvider extends ContentProvider {
//object made of the helper class for the provider, to get access of the database
private SQLdbHelper PetdbHelper;
private static final int PETS = 1;
private static final int PET_ID = 2;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sUriMatcher.addURI(CONTENT_AUTHORITY, PATH_PETS, PETS);
sUriMatcher.addURI(CONTENT_AUTHORITY, PATH_PETS_ID, PET_ID);
}...//class continues
In the AndroidManifest I've wrote:
<provider
android:name=".data.PetProvider"
android:authorities="com.example.android.petsretry.data"
android:exported="false">
</provider>
Have tried multiple things over it but couldn't get out of it...Please help!
Thanks in advance...
Have found the solution, problem was with the Android Manifest file, correct code is:
<provider android:name=".data.PetProvider" android:authorities="com.example.android.petsretry.data" android:exported="false"/>
All 'name', 'authorities' and 'exported' property should have been inside the opening tag of the provider instead of being in between the opening & closing tag of provider...
Hoof...

How to Create a Playlist

I am trying to create an app which simply offers an edittext and imagebutton. If the butten gets clicked, the idea is that an album is added to the Playlist, named in the edittext box. Albums should be selected randomly. Goes without saying that the album tracks should be in the correct order.
I can add more functionality later eg. save, overwrite, delete etc.
I have the interface but am struggling with the code. I sort of get the concept of ContentProviders.
so the code needs to:
access the Playlists, which I believe is achieved by using MediaStore.Audio.Playlists
access the Albums, which I believe is achieved by using MediaStore.Audio.Albums
add to the Playlist
I have the following code (most bits obtained from this site. Thanks btw) to access the Playlist but it crashes with a Null Exception error.
public void checkforplaylists()
{
//Get a cursor over all playlists.
final ContentResolver resolver= MediaProvider.mContentResolver;
final Uri uri=MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI;
final String id=MediaStore.Audio.Playlists._ID;
final String name=MediaStore.Audio.Playlists.NAME;
final String[]columns={id,name};
final Cursor playlists= resolver.query(uri, columns, null, null, null);
if(playlists==null)
{
Log.e(TAG,"Found no playlists.");
return;
}
return;
}
Anyone who can help?
I think you mean NullPointerException, which means one of your assignments is null and then you try to access a member of the object you intended it to be. Most likely it is resolver, but to be sure you need the line number reported and/or to step through that with a debugger.
This works. When using the ContentResolver, the Context (this) is required.
public void checkforplaylists(Context context)
{
ContentResolver cr = context.getContentResolver();
final Uri uri=MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
final String id=MediaStore.Audio.Playlists._ID;
final String name=MediaStore.Audio.Playlists.NAME;
final String[]columns={id,name};
final Cursor playlists= cr.query(uri, columns, null, null, null);
if(playlists==null)
{
Log.e(TAG,"Found no playlists.");
return;
}
Log.e(TAG,"Found playlists.");
return;
}
use this code, will work
public boolean addPlaylist(String pname) {
Uri playlists = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
Cursor c = resolver.query(playlists, new String[] { "*" }, null, null,
null);
long playlistId = 0;
c.moveToFirst();
do {
String plname = c.getString(c
.getColumnIndex(MediaStore.Audio.Playlists.NAME));
if (plname.equalsIgnoreCase(pname)) {
playlistId = c.getLong(c
.getColumnIndex(MediaStore.Audio.Playlists._ID));
break;
}
} while (c.moveToNext());
c.close();
if (playlistId != 0) {
Uri deleteUri = ContentUris.withAppendedId(playlists, playlistId);
Log.d(TAG, "REMOVING Existing Playlist: " + playlistId);
// delete the playlist
resolver.delete(deleteUri, null, null);
}
Log.d(TAG, "CREATING PLAYLIST: " + pname);
ContentValues v1 = new ContentValues();
v1.put(MediaStore.Audio.Playlists.NAME, pname);
v1.put(MediaStore.Audio.Playlists.DATE_MODIFIED,
System.currentTimeMillis());
Uri newpl = resolver.insert(playlists, v1);
Log.d(TAG, "Added PlayLIst: " + newpl);
flag=true;
return flag;
}

Categories

Resources