Detect wedged ContentProviders, treat as ANR.
All ContentProvider calls are currently blocking, making it hard for
an app to recover when a remote provider is wedged. This change adds
hidden support to ContentProviderClient to timeout remote calls,
treating them as ANRs. This behavior is disabled by default.
Update DocumentsUI to use a 20 second timeout whenever interacting
with a storage provider.
Bug: 10993301, 10819461, 10852518
Change-Id: I10fa3c425c6a7225fff9cb7a0a07659028230cd3
diff --git a/src/com/android/documentsui/DirectoryFragment.java b/src/com/android/documentsui/DirectoryFragment.java
index 1f11aed..6ff47f8 100644
--- a/src/com/android/documentsui/DirectoryFragment.java
+++ b/src/com/android/documentsui/DirectoryFragment.java
@@ -31,6 +31,7 @@
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -259,7 +260,7 @@
public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
if (!isAdded()) return;
- mAdapter.swapCursor(result.cursor);
+ mAdapter.swapResult(result.cursor, result.exception);
// Push latest state up to UI
// TODO: if mode change was racing with us, don't overwrite it
@@ -285,7 +286,7 @@
@Override
public void onLoaderReset(Loader<DirectoryResult> loader) {
- mAdapter.swapCursor(null);
+ mAdapter.swapResult(null, null);
}
};
@@ -552,9 +553,16 @@
continue;
}
- if (!DocumentsContract.deleteDocument(resolver, doc.derivedUri)) {
+ ContentProviderClient client = null;
+ try {
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, doc.derivedUri.getAuthority());
+ DocumentsContract.deleteDocument(client, doc.derivedUri);
+ } catch (Exception e) {
Log.w(TAG, "Failed to delete " + doc);
hadTrouble = true;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
}
}
@@ -646,7 +654,7 @@
private List<Footer> mFooters = Lists.newArrayList();
- public void swapCursor(Cursor cursor) {
+ public void swapResult(Cursor cursor, Exception e) {
mCursor = cursor;
mCursorCount = cursor != null ? cursor.getCount() : 0;
@@ -667,6 +675,11 @@
}
}
+ if (e != null) {
+ mFooters.add(new MessageFooter(
+ 3, R.drawable.ic_dialog_alert, getString(R.string.query_error)));
+ }
+
if (isEmpty()) {
mEmptyView.setVisibility(View.VISIBLE);
} else {
@@ -971,19 +984,23 @@
@Override
protected Bitmap doInBackground(Uri... params) {
final Context context = mIconThumb.getContext();
+ final ContentResolver resolver = context.getContentResolver();
+ ContentProviderClient client = null;
Bitmap result = null;
try {
- // TODO: switch to using unstable provider
- result = DocumentsContract.getDocumentThumbnail(
- context.getContentResolver(), mUri, mThumbSize, mSignal);
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, mUri.getAuthority());
+ result = DocumentsContract.getDocumentThumbnail(client, mUri, mThumbSize, mSignal);
if (result != null) {
final ThumbnailCache thumbs = DocumentsApplication.getThumbnailsCache(
context, mThumbSize);
thumbs.put(mUri, result);
}
} catch (Exception e) {
- Log.w(TAG, "Failed to load thumbnail: " + e);
+ Log.w(TAG, "Failed to load thumbnail for " + mUri + ": " + e);
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
}
return result;
}