Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3fd11777 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

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
parent 459f5364
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
        package="com.android.documentsui">

    <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
    <uses-permission android:name="android.permission.REMOVE_TASKS" />

    <application
        android:name=".DocumentsApplication"
+2 −0
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@
    <string name="save_error">Failed to save document</string>
    <!-- Toast shown when creating a folder failed with an error [CHAR LIMIT=48] -->
    <string name="create_error">Failed to create folder</string>
    <!-- Error message shown when querying for a list of documents failed [CHAR LIMIT=48] -->
    <string name="query_error">Failed to query documents</string>

    <!-- Title of storage root location that contains recently modified or used documents [CHAR LIMIT=24] -->
    <string name="root_recent">Recent</string>
+14 −5
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

package com.android.documentsui;

import static com.android.documentsui.DocumentsActivity.TAG;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
@@ -29,6 +32,7 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
@@ -36,8 +40,6 @@ import android.widget.Toast;

import com.android.documentsui.model.DocumentInfo;

import java.io.FileNotFoundException;

/**
 * Dialog to create a new directory.
 */
@@ -88,12 +90,19 @@ public class CreateDirectoryFragment extends DialogFragment {
            final ContentResolver resolver = activity.getContentResolver();

            final DocumentInfo cwd = activity.getCurrentDirectory();
            final Uri childUri = DocumentsContract.createDocument(
                    resolver, cwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);

            ContentProviderClient client = null;
            try {
                client = DocumentsApplication.acquireUnstableProviderOrThrow(
                        resolver, cwd.derivedUri.getAuthority());
                final Uri childUri = DocumentsContract.createDocument(
                        client, cwd.derivedUri, Document.MIME_TYPE_DIR, mDisplayName);
                return DocumentInfo.fromUri(resolver, childUri);
            } catch (FileNotFoundException e) {
            } catch (Exception e) {
                Log.w(TAG, "Failed to create directory", e);
                return null;
            } finally {
                ContentProviderClient.releaseQuietly(client);
            }
        }

+25 −8
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.app.Fragment;
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 class DirectoryFragment extends Fragment {
            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 @@ public class DirectoryFragment extends Fragment {

            @Override
            public void onLoaderReset(Loader<DirectoryResult> loader) {
                mAdapter.swapCursor(null);
                mAdapter.swapResult(null, null);
            }
        };

@@ -552,9 +553,16 @@ public class DirectoryFragment extends Fragment {
                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 @@ public class DirectoryFragment extends Fragment {

        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 @@ public class DirectoryFragment extends Fragment {
                }
            }

            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 @@ public class DirectoryFragment extends Fragment {
        @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;
        }
+5 −3
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ class DirectoryResult implements AutoCloseable {
    @Override
    public void close() {
        IoUtils.closeQuietly(cursor);
        ContentProviderClient.closeQuietly(client);
        ContentProviderClient.releaseQuietly(client);
        cursor = null;
        client = null;
    }
@@ -158,7 +158,9 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
                + result.mode + ", sortOrder=" + result.sortOrder);

        try {
            result.client = resolver.acquireUnstableContentProviderClient(authority);
            result.client = DocumentsApplication.acquireUnstableProviderOrThrow(
                    resolver, authority);

            cursor = result.client.query(
                    mUri, null, null, null, getQuerySortOrder(result.sortOrder), mSignal);
            cursor.registerContentObserver(mObserver);
@@ -177,7 +179,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
        } catch (Exception e) {
            Log.w(TAG, "Failed to query", e);
            result.exception = e;
            ContentProviderClient.closeQuietly(result.client);
            ContentProviderClient.releaseQuietly(result.client);
        } finally {
            synchronized (this) {
                mSignal = null;
Loading