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

Commit 7aa7601c 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 7eb5ce03
Loading
Loading
Loading
Loading
+21 −0
Original line number Original line Diff line number Diff line
@@ -758,6 +758,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
            return true;
            return true;
        }
        }


        case APP_NOT_RESPONDING_VIA_PROVIDER_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            appNotRespondingViaProvider(b);
            reply.writeNoException();
            return true;
        }

        case REMOVE_CONTENT_PROVIDER_TRANSACTION: {
        case REMOVE_CONTENT_PROVIDER_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IBinder b = data.readStrongBinder();
@@ -2891,6 +2899,7 @@ class ActivityManagerProxy implements IActivityManager
        reply.recycle();
        reply.recycle();
        return res;
        return res;
    }
    }

    public void unstableProviderDied(IBinder connection) throws RemoteException {
    public void unstableProviderDied(IBinder connection) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        Parcel reply = Parcel.obtain();
@@ -2902,6 +2911,18 @@ class ActivityManagerProxy implements IActivityManager
        reply.recycle();
        reply.recycle();
    }
    }


    @Override
    public void appNotRespondingViaProvider(IBinder connection) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(connection);
        mRemote.transact(APP_NOT_RESPONDING_VIA_PROVIDER_TRANSACTION, data, reply, 0);
        reply.readException();
        data.recycle();
        reply.recycle();
    }

    public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException {
    public void removeContentProvider(IBinder connection, boolean stable) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        Parcel reply = Parcel.obtain();
+13 −0
Original line number Original line Diff line number Diff line
@@ -4653,6 +4653,19 @@ public final class ActivityThread {
        }
        }
    }
    }


    final void appNotRespondingViaProvider(IBinder provider) {
        synchronized (mProviderMap) {
            ProviderRefCount prc = mProviderRefCountMap.get(provider);
            if (prc != null) {
                try {
                    ActivityManagerNative.getDefault()
                            .appNotRespondingViaProvider(prc.holder.connection);
                } catch (RemoteException e) {
                }
            }
        }
    }

    private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
    private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
            ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
            ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
        final String auths[] = PATTERN_SEMICOLON.split(holder.info.authority);
        final String auths[] = PATTERN_SEMICOLON.split(holder.info.authority);
+5 −0
Original line number Original line Diff line number Diff line
@@ -2200,5 +2200,10 @@ class ContextImpl extends Context {
        public void unstableProviderDied(IContentProvider icp) {
        public void unstableProviderDied(IContentProvider icp) {
            mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
            mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
        }
        }

        @Override
        public void appNotRespondingViaProvider(IContentProvider icp) {
            mMainThread.appNotRespondingViaProvider(icp.asBinder());
        }
    }
    }
}
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -139,6 +139,7 @@ public interface IActivityManager extends IInterface {
    public boolean refContentProvider(IBinder connection, int stableDelta, int unstableDelta)
    public boolean refContentProvider(IBinder connection, int stableDelta, int unstableDelta)
            throws RemoteException;
            throws RemoteException;
    public void unstableProviderDied(IBinder connection) throws RemoteException;
    public void unstableProviderDied(IBinder connection) throws RemoteException;
    public void appNotRespondingViaProvider(IBinder connection) throws RemoteException;
    public PendingIntent getRunningServiceControlPanel(ComponentName service)
    public PendingIntent getRunningServiceControlPanel(ComponentName service)
            throws RemoteException;
            throws RemoteException;
    public ComponentName startService(IApplicationThread caller, Intent service,
    public ComponentName startService(IApplicationThread caller, Intent service,
@@ -691,4 +692,5 @@ public interface IActivityManager extends IInterface {
    int TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
    int TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179;
    int RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+180;
    int RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+180;
    int GET_PERSISTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+181;
    int GET_PERSISTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+181;
    int APP_NOT_RESPONDING_VIA_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+182;
}
}
+133 −43
Original line number Original line Diff line number Diff line
@@ -16,15 +16,20 @@


package android.content;
package android.content;


import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.Cursor;
import android.net.Uri;
import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.ICancellationSignal;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.content.res.AssetFileDescriptor;
import android.os.RemoteException;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;


import dalvik.system.CloseGuard;
import dalvik.system.CloseGuard;


@@ -45,26 +50,64 @@ import java.util.ArrayList;
 * until you are finished with the data they have returned.
 * until you are finished with the data they have returned.
 */
 */
public class ContentProviderClient {
public class ContentProviderClient {
    private final IContentProvider mContentProvider;
    private static final String TAG = "ContentProviderClient";

    @GuardedBy("ContentProviderClient.class")
    private static Handler sAnrHandler;

    private final ContentResolver mContentResolver;
    private final ContentResolver mContentResolver;
    private final IContentProvider mContentProvider;
    private final String mPackageName;
    private final String mPackageName;
    private final boolean mStable;
    private final boolean mStable;
    private boolean mReleased;


    private final CloseGuard mGuard = CloseGuard.get();
    private final CloseGuard mGuard = CloseGuard.get();


    /**
    private long mAnrTimeout;
     * @hide
    private NotRespondingRunnable mAnrRunnable;
     */

    ContentProviderClient(ContentResolver contentResolver,
    private boolean mReleased;
            IContentProvider contentProvider, boolean stable) {

        mContentProvider = contentProvider;
    /** {@hide} */
    ContentProviderClient(
            ContentResolver contentResolver, IContentProvider contentProvider, boolean stable) {
        mContentResolver = contentResolver;
        mContentResolver = contentResolver;
        mContentProvider = contentProvider;
        mPackageName = contentResolver.mPackageName;
        mPackageName = contentResolver.mPackageName;
        mStable = stable;
        mStable = stable;

        mGuard.open("release");
        mGuard.open("release");
    }
    }


    /** {@hide} */
    public void setDetectNotResponding(long timeoutMillis) {
        synchronized (ContentProviderClient.class) {
            mAnrTimeout = timeoutMillis;

            if (timeoutMillis > 0) {
                if (mAnrRunnable == null) {
                    mAnrRunnable = new NotRespondingRunnable();
                }
                if (sAnrHandler == null) {
                    sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
                }
            } else {
                mAnrRunnable = null;
            }
        }
    }

    private void beforeRemote() {
        if (mAnrRunnable != null) {
            sAnrHandler.postDelayed(mAnrRunnable, mAnrTimeout);
        }
    }

    private void afterRemote() {
        if (mAnrRunnable != null) {
            sAnrHandler.removeCallbacks(mAnrRunnable);
        }
    }

    /** See {@link ContentProvider#query ContentProvider.query} */
    /** See {@link ContentProvider#query ContentProvider.query} */
    public Cursor query(Uri url, String[] projection, String selection,
    public Cursor query(Uri url, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) throws RemoteException {
            String[] selectionArgs, String sortOrder) throws RemoteException {
@@ -72,16 +115,16 @@ public class ContentProviderClient {
    }
    }


    /** See {@link ContentProvider#query ContentProvider.query} */
    /** See {@link ContentProvider#query ContentProvider.query} */
    public Cursor query(Uri url, String[] projection, String selection,
    public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
            String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal)
            String sortOrder, CancellationSignal cancellationSignal) throws RemoteException {
                    throws RemoteException {
        beforeRemote();
        try {
            ICancellationSignal remoteCancellationSignal = null;
            ICancellationSignal remoteCancellationSignal = null;
            if (cancellationSignal != null) {
            if (cancellationSignal != null) {
                cancellationSignal.throwIfCanceled();
                cancellationSignal.throwIfCanceled();
                remoteCancellationSignal = mContentProvider.createCancellationSignal();
                remoteCancellationSignal = mContentProvider.createCancellationSignal();
                cancellationSignal.setRemote(remoteCancellationSignal);
                cancellationSignal.setRemote(remoteCancellationSignal);
            }
            }
        try {
            return mContentProvider.query(mPackageName, url, projection, selection, selectionArgs,
            return mContentProvider.query(mPackageName, url, projection, selection, selectionArgs,
                    sortOrder, remoteCancellationSignal);
                    sortOrder, remoteCancellationSignal);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -89,11 +132,14 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#getType ContentProvider.getType} */
    /** See {@link ContentProvider#getType ContentProvider.getType} */
    public String getType(Uri url) throws RemoteException {
    public String getType(Uri url) throws RemoteException {
        beforeRemote();
        try {
        try {
            return mContentProvider.getType(url);
            return mContentProvider.getType(url);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -101,11 +147,14 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
    /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
    public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
    public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
        beforeRemote();
        try {
        try {
            return mContentProvider.getStreamTypes(url, mimeTypeFilter);
            return mContentProvider.getStreamTypes(url, mimeTypeFilter);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -113,11 +162,14 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#canonicalize} */
    /** See {@link ContentProvider#canonicalize} */
    public final Uri canonicalize(Uri url) throws RemoteException {
    public final Uri canonicalize(Uri url) throws RemoteException {
        beforeRemote();
        try {
        try {
            return mContentProvider.canonicalize(mPackageName, url);
            return mContentProvider.canonicalize(mPackageName, url);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -125,11 +177,14 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#uncanonicalize} */
    /** See {@link ContentProvider#uncanonicalize} */
    public final Uri uncanonicalize(Uri url) throws RemoteException {
    public final Uri uncanonicalize(Uri url) throws RemoteException {
        beforeRemote();
        try {
        try {
            return mContentProvider.uncanonicalize(mPackageName, url);
            return mContentProvider.uncanonicalize(mPackageName, url);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -137,12 +192,14 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#insert ContentProvider.insert} */
    /** See {@link ContentProvider#insert ContentProvider.insert} */
    public Uri insert(Uri url, ContentValues initialValues)
    public Uri insert(Uri url, ContentValues initialValues) throws RemoteException {
            throws RemoteException {
        beforeRemote();
        try {
        try {
            return mContentProvider.insert(mPackageName, url, initialValues);
            return mContentProvider.insert(mPackageName, url, initialValues);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -150,11 +207,14 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
    /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
    public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
    public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
        beforeRemote();
        try {
        try {
            return mContentProvider.bulkInsert(mPackageName, url, initialValues);
            return mContentProvider.bulkInsert(mPackageName, url, initialValues);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -162,12 +222,15 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#delete ContentProvider.delete} */
    /** See {@link ContentProvider#delete ContentProvider.delete} */
    public int delete(Uri url, String selection, String[] selectionArgs)
    public int delete(Uri url, String selection, String[] selectionArgs)
            throws RemoteException {
            throws RemoteException {
        beforeRemote();
        try {
        try {
            return mContentProvider.delete(mPackageName, url, selection, selectionArgs);
            return mContentProvider.delete(mPackageName, url, selection, selectionArgs);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -175,12 +238,15 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#update ContentProvider.update} */
    /** See {@link ContentProvider#update ContentProvider.update} */
    public int update(Uri url, ContentValues values, String selection,
    public int update(Uri url, ContentValues values, String selection,
            String[] selectionArgs) throws RemoteException {
            String[] selectionArgs) throws RemoteException {
        beforeRemote();
        try {
        try {
            return mContentProvider.update(mPackageName, url, values, selection, selectionArgs);
            return mContentProvider.update(mPackageName, url, values, selection, selectionArgs);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -188,6 +254,8 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


@@ -212,19 +280,22 @@ public class ContentProviderClient {
     */
     */
    public ParcelFileDescriptor openFile(Uri url, String mode, CancellationSignal signal)
    public ParcelFileDescriptor openFile(Uri url, String mode, CancellationSignal signal)
            throws RemoteException, FileNotFoundException {
            throws RemoteException, FileNotFoundException {
        beforeRemote();
        try {
            ICancellationSignal remoteSignal = null;
            ICancellationSignal remoteSignal = null;
            if (signal != null) {
            if (signal != null) {
                signal.throwIfCanceled();
                signal.throwIfCanceled();
                remoteSignal = mContentProvider.createCancellationSignal();
                remoteSignal = mContentProvider.createCancellationSignal();
                signal.setRemote(remoteSignal);
                signal.setRemote(remoteSignal);
            }
            }
        try {
            return mContentProvider.openFile(mPackageName, url, mode, remoteSignal);
            return mContentProvider.openFile(mPackageName, url, mode, remoteSignal);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
            if (!mStable) {
            if (!mStable) {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


@@ -249,19 +320,22 @@ public class ContentProviderClient {
     */
     */
    public AssetFileDescriptor openAssetFile(Uri url, String mode, CancellationSignal signal)
    public AssetFileDescriptor openAssetFile(Uri url, String mode, CancellationSignal signal)
            throws RemoteException, FileNotFoundException {
            throws RemoteException, FileNotFoundException {
        beforeRemote();
        try {
            ICancellationSignal remoteSignal = null;
            ICancellationSignal remoteSignal = null;
            if (signal != null) {
            if (signal != null) {
                signal.throwIfCanceled();
                signal.throwIfCanceled();
                remoteSignal = mContentProvider.createCancellationSignal();
                remoteSignal = mContentProvider.createCancellationSignal();
                signal.setRemote(remoteSignal);
                signal.setRemote(remoteSignal);
            }
            }
        try {
            return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal);
            return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
            if (!mStable) {
            if (!mStable) {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


@@ -275,13 +349,14 @@ public class ContentProviderClient {
    public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
    public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
            String mimeType, Bundle opts, CancellationSignal signal)
            String mimeType, Bundle opts, CancellationSignal signal)
            throws RemoteException, FileNotFoundException {
            throws RemoteException, FileNotFoundException {
        beforeRemote();
        try {
            ICancellationSignal remoteSignal = null;
            ICancellationSignal remoteSignal = null;
            if (signal != null) {
            if (signal != null) {
                signal.throwIfCanceled();
                signal.throwIfCanceled();
                remoteSignal = mContentProvider.createCancellationSignal();
                remoteSignal = mContentProvider.createCancellationSignal();
                signal.setRemote(remoteSignal);
                signal.setRemote(remoteSignal);
            }
            }
        try {
            return mContentProvider.openTypedAssetFile(
            return mContentProvider.openTypedAssetFile(
                    mPackageName, uri, mimeType, opts, remoteSignal);
                    mPackageName, uri, mimeType, opts, remoteSignal);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -289,12 +364,15 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
    /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
            throws RemoteException, OperationApplicationException {
            throws RemoteException, OperationApplicationException {
        beforeRemote();
        try {
        try {
            return mContentProvider.applyBatch(mPackageName, operations);
            return mContentProvider.applyBatch(mPackageName, operations);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -302,12 +380,14 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


    /** See {@link ContentProvider#call(String, String, Bundle)} */
    /** See {@link ContentProvider#call(String, String, Bundle)} */
    public Bundle call(String method, String arg, Bundle extras)
    public Bundle call(String method, String arg, Bundle extras) throws RemoteException {
            throws RemoteException {
        beforeRemote();
        try {
        try {
            return mContentProvider.call(mPackageName, method, arg, extras);
            return mContentProvider.call(mPackageName, method, arg, extras);
        } catch (DeadObjectException e) {
        } catch (DeadObjectException e) {
@@ -315,6 +395,8 @@ public class ContentProviderClient {
                mContentResolver.unstableProviderDied(mContentProvider);
                mContentResolver.unstableProviderDied(mContentProvider);
            }
            }
            throw e;
            throw e;
        } finally {
            afterRemote();
        }
        }
    }
    }


@@ -359,7 +441,7 @@ public class ContentProviderClient {
    }
    }


    /** {@hide} */
    /** {@hide} */
    public static void closeQuietly(ContentProviderClient client) {
    public static void releaseQuietly(ContentProviderClient client) {
        if (client != null) {
        if (client != null) {
            try {
            try {
                client.release();
                client.release();
@@ -367,4 +449,12 @@ public class ContentProviderClient {
            }
            }
        }
        }
    }
    }

    private class NotRespondingRunnable implements Runnable {
        @Override
        public void run() {
            Log.w(TAG, "Detected provider not responding: " + mContentProvider);
            mContentResolver.appNotRespondingViaProvider(mContentProvider);
        }
    }
}
}
Loading