Permalink
Browse files

Ensure MuzeiArtProviders cannot crash Muzei

Parcel.writeException details a set of RuntimeExceptions that are sent over to the calling app. If a MuzeiArtProvider threw any of these RuntimeExceptions, they'd crash Muzei - we want to avoid that by catching all of these exceptions and rethrowing them as RemoteException, which Muzei uses as the signal that the remote MuzeiArtProvider crashed.

Special handling was added around checking for valid artwork to ensure the now RemoteExceptions don't take down the entire Worker, but instead are captured locally and mark the artwork as invalid.
  • Loading branch information...
ianhanniballake committed Oct 9, 2018
1 parent 470de08 commit c8a30a25fc27960be03874b095d0637d46a36ee5
@@ -213,14 +213,12 @@ class ArtworkLoadWorker(
}
}
}
return Result.FAILURE
} catch (e: RemoteException) {
Log.i(TAG, "Provider $authority crashed while retrieving artwork", e)
return Result.FAILURE
Log.i(TAG, "Provider $authority crashed while retrieving artwork: ${e.message}")
}
return Result.RETRY
}
@Throws(RemoteException::class)
private suspend fun checkForValidArtwork(
client: ContentProviderClientCompat,
contentUri: Uri,
@@ -245,7 +243,10 @@ class ArtworkLoadWorker(
}
}
} catch (e: IOException) {
Log.w(TAG, "Unable to preload artwork $artworkUri", e)
Log.w(TAG, "Unable to preload artwork $artworkUri: ${e.message}")
} catch (e: RemoteException) {
Log.w(TAG, "Provider ${contentUri.authority} crashed preloading artwork " +
"$artworkUri: ${e.message}")
}
return null
@@ -240,7 +240,7 @@ class ProviderChangedWorker(
}
}
} catch (e: RemoteException) {
Log.i(TAG, "Provider ${provider.authority} crashed while retrieving artwork", e)
Log.i(TAG, "Provider ${provider.authority} crashed while retrieving artwork: ${e.message}")
}
return Result.RETRY
}
@@ -259,7 +259,6 @@ class ProviderChangedWorker(
return false
}
@Throws(RemoteException::class)
private suspend fun isValidArtwork(
client: ContentProviderClientCompat,
contentUri: Uri,
@@ -280,7 +279,10 @@ class ProviderChangedWorker(
}
}
} catch (e: IOException) {
Log.w(TAG, "Unable to preload artwork $artworkUri", e)
Log.w(TAG, "Unable to preload artwork $artworkUri: ${e.message}")
} catch (e: RemoteException) {
Log.w(TAG, "Provider ${contentUri.authority} crashed preloading artwork " +
"$artworkUri: ${e.message}")
}
return false
@@ -16,6 +16,7 @@
package com.google.android.apps.muzei.util
import android.annotation.SuppressLint
import android.content.ContentProviderClient
import android.content.Context
import android.database.Cursor
@@ -26,7 +27,6 @@ import android.os.ParcelFileDescriptor
import android.os.RemoteException
import kotlinx.coroutines.experimental.Dispatchers
import kotlinx.coroutines.experimental.withContext
import java.io.FileNotFoundException
import java.io.InputStream
@@ -50,9 +50,18 @@ class ContentProviderClientCompat private constructor(
arg: String? = null,
extras: Bundle? = null
): Bundle? = withContext(Dispatchers.Default) {
mContentProviderClient.call(method, arg, extras)
try {
mContentProviderClient.call(method, arg, extras)
} catch (e: Exception) {
if (e is RemoteException) {
throw e
} else {
throw RemoteException(e.message)
}
}
}
@SuppressLint("Recycle")
@Throws(RemoteException::class)
suspend fun query(
url: Uri,
@@ -61,16 +70,32 @@ class ContentProviderClientCompat private constructor(
selectionArgs: Array<String>? = null,
sortOrder: String? = null
): Cursor? = withContext(Dispatchers.Default) {
return@withContext mContentProviderClient.query(
url, projection, selection, selectionArgs, sortOrder)
try {
mContentProviderClient.query(
url, projection, selection, selectionArgs, sortOrder)
} catch (e: Exception) {
if (e is RemoteException) {
throw e
} else {
throw RemoteException(e.message)
}
}
}
@Throws(FileNotFoundException::class, RemoteException::class)
suspend fun openInputStream(
url: Uri
): InputStream? = withContext(Dispatchers.Default) {
mContentProviderClient.openFile(url, "r")?.run {
ParcelFileDescriptor.AutoCloseInputStream(this)
try {
mContentProviderClient.openFile(url, "r")?.run {
ParcelFileDescriptor.AutoCloseInputStream(this)
}
} catch (e: Exception) {
if (e is FileNotFoundException || e is RemoteException) {
throw e
} else {
throw RemoteException(e.message)
}
}
}

0 comments on commit c8a30a2

Please sign in to comment.