Reputation: 504
I have an automotive infotainment application that supports multiple user profiles. However, our requirement is to maintain the same data across all user profiles, making it vehicle-specific.
In my application, I have a radio group, and when a radio button is clicked, the selected value needs to be saved in a ContentProvider. Both saving and fetching operations should be handled through the ContentProvider.
When switching to another user profile, the same previously saved data should be retrieved from the ContentProvider.
Example Scenario: UserProfile1:
User selects an option via the radio group (e.g., value = 3). This value (3) should be saved in the ContentProvider.
UserProfile2:
Upon switching to another profile, the application should fetch the previously saved value from the ContentProvider. Expected value: 3 (same as what was saved in UserProfile1). However, the issue I'm facing is that the value is not being set correctly, even for UserProfile1.
am getting crash like :
FATAL EXCEPTION: DefaultDispatcher-worker-3 (Ask Gemini)
Process: com.android.globaldataapp, PID: 6914
java.lang.IllegalArgumentException: Unknown URL content://com.android.globaldataservice.data.provider/globaldataapp
at android.content.ContentResolver.insert(ContentResolver.java:2189)
at android.content.ContentResolver.insert(ContentResolver.java:2155)
at com.android.globaldataservice.data.provider.AppDataProviderWrapper.setValue(AppDataProviderWrapper.kt:23)
at com.android.globaldataservice.data.repository.api.can.ApiRepository.saveData(ApiRepository.kt:691)
at com.android.globaldataservice.data.repository.api.can.ApiRepository$initializeBootUpValues$1.invokeSuspend(ApiRepository.kt:1791)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@2fbca59, Dispatchers.Default]
My code will be like this :
AppGlobalDataContentProvider.kt
class AppGlobalDataContentProvider : ContentProvider() {
private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI("com.android.globaldataservice.data.provider", "globaldataapp", 1)
}
companion object {
const val AUTHORITY =
"com.android.globaldataservice.data.provider"
val CONTENT_URI: Uri = Uri.parse("content://$AUTHORITY/globaldataapp")
}
private lateinit var sharedPreferences: SharedPreferences
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
sharedPreferences.edit().remove(KEY_VALUE).apply()
context?.contentResolver?.notifyChange(uri, null)
return 1
}
override fun getType(uri: Uri): String? {
return null
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
when (sUriMatcher.match(uri)) {
1 -> {
val value = values?.getAsString(KEY_VALUE) ?: return null
sharedPreferences.edit().putString(KEY_VALUE, value).apply()
context?.contentResolver?.notifyChange(uri, null)
return uri
}
else -> throw IllegalArgumentException("Unknown URI: $uri")
}
}
override fun onCreate(): Boolean {
val context = context?.createDeviceProtectedStorageContext()
?: throw IllegalStateException("Device-Protected storage context not available")
sharedPreferences = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE)
return true
}
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
): Cursor? {
val value = sharedPreferences.getString(KEY_VALUE, null) ?: return null
val cursor = MatrixCursor(arrayOf(KEY_VALUE))
cursor.addRow(arrayOf(value))
return cursor
}
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?
): Int {
val value = values?.getAsString(KEY_VALUE) ?: return 0
sharedPreferences.edit().putString(KEY_VALUE, value).apply()
context?.contentResolver?.notifyChange(uri, null)
return 1
}
AppDataProviderWrapper.kt
@Singleton
class AppDataProviderWrapper @Inject constructor(@ApplicationContext private val context: Context) {
private val mContentUri: Uri = Uri.parse("content://$AUTHORITY/globaldataapp")
fun saveData(data: Int) {
val contentResolver = context.contentResolver
val contentValues = ContentValues().apply {
put(KEY_VALUE, data)
}
contentResolver.insert(mContentUri, contentValues)
}
fun getdata(): Int? {
val sharedPreferences = context.createDeviceProtectedStorageContext()
?.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE)
return sharedPreferences?.getInt(KEY_VALUE, 0)
}
}
Androidmanifest.xml
<provider
android:name=".data.provider.AppGlobalDataContentProvider"
android:authorities="com.android.globaldataservice.data.provider"
android:enabled="true"
android:exported="false"
android:grantUriPermissions="true"
android:singleUser="true" />
ApiRepository.kt
appDataProviderWrapper.saveData(chargingType)
So here when I tried save the data app is crashing.
Upvotes: 0
Views: 28
Reputation: 1084
I guess your crash occurs due to an IllegalArgumentException: Unknown URL, which indicates that the URI used in the insert operation does not match the URI pattern registered in the ContentProvider
. First, ensure the AUTHORITY
value in your UriMatcher
matches exactly with what is used in AppDataProviderWrapper
. Update the UriMatcher
definition as follows:
private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(AUTHORITY, "globaldataapp", 1)
}
Similarly, in AppDataProviderWrapper
, ensure you are using the correct URI:
private val mContentUri: Uri = Uri.parse("content://$AUTHORITY/globaldataapp")
Additionally, modify the insert()
method in AppGlobalDataContentProvider
to handle unknown URIs gracefully by logging the error instead of throwing an exception.
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return when (sUriMatcher.match(uri)) {
1 -> {
val value = values?.getAsString(KEY_VALUE) ?: return null
sharedPreferences.edit().putString(KEY_VALUE, value).apply()
context?.contentResolver?.notifyChange(uri, null)
uri
}
else -> {
Log.e("AppGlobalDataContentProvider", "Unknown URI: $uri")
null
}
}
}
Your saveData()
function should also handle potential failures when inserting into the ContentProvider
:
fun saveData(data: Int) {
val contentResolver = context.contentResolver
val contentValues = ContentValues().apply {
put(KEY_VALUE, data)
}
try {
val resultUri = contentResolver.insert(mContentUri, contentValues)
if (resultUri == null) {
Log.e("AppDataProviderWrapper", "Failed to insert data into ContentProvider")
}
} catch (e: Exception) {
Log.e("AppDataProviderWrapper", "Error inserting data", e)
}
}
Furthermore, as you need to maintain data consistency across multiple user profiles, ensure that all reads and writes are performed using DeviceProtectedStorageContext
, allowing the data to persist across different user sessions:
val context = context.createDeviceProtectedStorageContext()
Lastly, check your AndroidManifest.xml`` to ensure that
android:exported="true"is set if other apps or system services require access. Also, consider adding
android:directBootAware="true"``` to ensure preferences are accessible across user profiles. With these fixes, your application should correctly save and retrieve data across all user profiles without crashing.
Upvotes: 0