1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.content;
18 
19 import android.annotation.DurationMillisLong;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresPermission;
23 import android.annotation.SystemApi;
24 import android.annotation.TestApi;
25 import android.compat.annotation.UnsupportedAppUsage;
26 import android.content.res.AssetFileDescriptor;
27 import android.database.CrossProcessCursorWrapper;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.os.Binder;
31 import android.os.Build;
32 import android.os.Bundle;
33 import android.os.CancellationSignal;
34 import android.os.DeadObjectException;
35 import android.os.Handler;
36 import android.os.ICancellationSignal;
37 import android.os.Looper;
38 import android.os.ParcelFileDescriptor;
39 import android.os.RemoteException;
40 import android.util.Log;
41 
42 import com.android.internal.annotations.GuardedBy;
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.util.Preconditions;
45 
46 import dalvik.system.CloseGuard;
47 
48 import libcore.io.IoUtils;
49 
50 import java.io.FileNotFoundException;
51 import java.util.ArrayList;
52 import java.util.concurrent.atomic.AtomicBoolean;
53 
54 /**
55  * The public interface object used to interact with a specific
56  * {@link ContentProvider}.
57  * <p>
58  * Instances can be obtained by calling
59  * {@link ContentResolver#acquireContentProviderClient} or
60  * {@link ContentResolver#acquireUnstableContentProviderClient}. Instances must
61  * be released using {@link #close()} in order to indicate to the system that
62  * the underlying {@link ContentProvider} is no longer needed and can be killed
63  * to free up resources.
64  * <p>
65  * Note that you should generally create a new ContentProviderClient instance
66  * for each thread that will be performing operations. Unlike
67  * {@link ContentResolver}, the methods here such as {@link #query} and
68  * {@link #openFile} are not thread safe -- you must not call {@link #close()}
69  * on the ContentProviderClient those calls are made from until you are finished
70  * with the data they have returned.
71  */
72 public class ContentProviderClient implements ContentInterface, AutoCloseable {
73     private static final String TAG = "ContentProviderClient";
74 
75     @GuardedBy("ContentProviderClient.class")
76     private static Handler sAnrHandler;
77 
78     private final ContentResolver mContentResolver;
79     @UnsupportedAppUsage
80     private final IContentProvider mContentProvider;
81     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
82     private final String mPackageName;
83     private final String mAuthority;
84     private final boolean mStable;
85 
86     private final AtomicBoolean mClosed = new AtomicBoolean();
87     private final CloseGuard mCloseGuard = CloseGuard.get();
88 
89     private long mAnrTimeout;
90     private NotRespondingRunnable mAnrRunnable;
91 
92     /** {@hide} */
93     @VisibleForTesting
ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider, boolean stable)94     public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
95             boolean stable) {
96         // Only used for testing, so use a fake authority
97         this(contentResolver, contentProvider, "unknown", stable);
98     }
99 
100     /** {@hide} */
ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider, String authority, boolean stable)101     public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
102             String authority, boolean stable) {
103         mContentResolver = contentResolver;
104         mContentProvider = contentProvider;
105         mPackageName = contentResolver.mPackageName;
106 
107         mAuthority = authority;
108         mStable = stable;
109 
110         mCloseGuard.open("close");
111     }
112 
113     /**
114      * Configure this client to automatically detect and kill the remote
115      * provider when an "application not responding" event is detected.
116      *
117      * @param timeoutMillis the duration for which a pending call is allowed
118      *            block before the remote provider is considered to be
119      *            unresponsive. Set to {@code 0} to allow pending calls to block
120      *            indefinitely with no action taken.
121      * @hide
122      */
123     @SystemApi
124     @TestApi
125     @RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
setDetectNotResponding(@urationMillisLong long timeoutMillis)126     public void setDetectNotResponding(@DurationMillisLong long timeoutMillis) {
127         synchronized (ContentProviderClient.class) {
128             mAnrTimeout = timeoutMillis;
129 
130             if (timeoutMillis > 0) {
131                 if (mAnrRunnable == null) {
132                     mAnrRunnable = new NotRespondingRunnable();
133                 }
134                 if (sAnrHandler == null) {
135                     sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
136                 }
137 
138                 // If the remote process hangs, we're going to kill it, so we're
139                 // technically okay doing blocking calls.
140                 Binder.allowBlocking(mContentProvider.asBinder());
141             } else {
142                 mAnrRunnable = null;
143 
144                 // If we're no longer watching for hangs, revert back to default
145                 // blocking behavior.
146                 Binder.defaultBlocking(mContentProvider.asBinder());
147             }
148         }
149     }
150 
beforeRemote()151     private void beforeRemote() {
152         if (mAnrRunnable != null) {
153             sAnrHandler.postDelayed(mAnrRunnable, mAnrTimeout);
154         }
155     }
156 
afterRemote()157     private void afterRemote() {
158         if (mAnrRunnable != null) {
159             sAnrHandler.removeCallbacks(mAnrRunnable);
160         }
161     }
162 
163     /** See {@link ContentProvider#query ContentProvider.query} */
query(@onNull Uri url, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder)164     public @Nullable Cursor query(@NonNull Uri url, @Nullable String[] projection,
165             @Nullable String selection, @Nullable String[] selectionArgs,
166             @Nullable String sortOrder) throws RemoteException {
167         return query(url, projection, selection,  selectionArgs, sortOrder, null);
168     }
169 
170     /** See {@link ContentProvider#query ContentProvider.query} */
query(@onNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal)171     public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
172             @Nullable String selection, @Nullable String[] selectionArgs,
173             @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal)
174                     throws RemoteException {
175         Bundle queryArgs =
176                 ContentResolver.createSqlQueryBundle(selection, selectionArgs, sortOrder);
177         return query(uri, projection, queryArgs, cancellationSignal);
178     }
179 
180     /** See {@link ContentProvider#query ContentProvider.query} */
181     @Override
query(@onNull Uri uri, @Nullable String[] projection, Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)182     public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
183             Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
184                     throws RemoteException {
185         Preconditions.checkNotNull(uri, "url");
186 
187         beforeRemote();
188         try {
189             ICancellationSignal remoteCancellationSignal = null;
190             if (cancellationSignal != null) {
191                 cancellationSignal.throwIfCanceled();
192                 remoteCancellationSignal = mContentProvider.createCancellationSignal();
193                 cancellationSignal.setRemote(remoteCancellationSignal);
194             }
195             final Cursor cursor = mContentProvider.query(
196                     mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
197             if (cursor == null) {
198                 return null;
199             }
200             return new CursorWrapperInner(cursor);
201         } catch (DeadObjectException e) {
202             if (!mStable) {
203                 mContentResolver.unstableProviderDied(mContentProvider);
204             }
205             throw e;
206         } finally {
207             afterRemote();
208         }
209     }
210 
211     /** See {@link ContentProvider#getType ContentProvider.getType} */
212     @Override
getType(@onNull Uri url)213     public @Nullable String getType(@NonNull Uri url) throws RemoteException {
214         Preconditions.checkNotNull(url, "url");
215 
216         beforeRemote();
217         try {
218             return mContentProvider.getType(url);
219         } catch (DeadObjectException e) {
220             if (!mStable) {
221                 mContentResolver.unstableProviderDied(mContentProvider);
222             }
223             throw e;
224         } finally {
225             afterRemote();
226         }
227     }
228 
229     /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
230     @Override
getStreamTypes(@onNull Uri url, @NonNull String mimeTypeFilter)231     public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter)
232             throws RemoteException {
233         Preconditions.checkNotNull(url, "url");
234         Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter");
235 
236         beforeRemote();
237         try {
238             return mContentProvider.getStreamTypes(url, mimeTypeFilter);
239         } catch (DeadObjectException e) {
240             if (!mStable) {
241                 mContentResolver.unstableProviderDied(mContentProvider);
242             }
243             throw e;
244         } finally {
245             afterRemote();
246         }
247     }
248 
249     /** See {@link ContentProvider#canonicalize} */
250     @Override
canonicalize(@onNull Uri url)251     public final @Nullable Uri canonicalize(@NonNull Uri url) throws RemoteException {
252         Preconditions.checkNotNull(url, "url");
253 
254         beforeRemote();
255         try {
256             return mContentProvider.canonicalize(mPackageName, url);
257         } catch (DeadObjectException e) {
258             if (!mStable) {
259                 mContentResolver.unstableProviderDied(mContentProvider);
260             }
261             throw e;
262         } finally {
263             afterRemote();
264         }
265     }
266 
267     /** See {@link ContentProvider#uncanonicalize} */
268     @Override
uncanonicalize(@onNull Uri url)269     public final @Nullable Uri uncanonicalize(@NonNull Uri url) throws RemoteException {
270         Preconditions.checkNotNull(url, "url");
271 
272         beforeRemote();
273         try {
274             return mContentProvider.uncanonicalize(mPackageName, url);
275         } catch (DeadObjectException e) {
276             if (!mStable) {
277                 mContentResolver.unstableProviderDied(mContentProvider);
278             }
279             throw e;
280         } finally {
281             afterRemote();
282         }
283     }
284 
285     /** See {@link ContentProvider#refresh} */
286     @Override
refresh(Uri url, @Nullable Bundle args, @Nullable CancellationSignal cancellationSignal)287     public boolean refresh(Uri url, @Nullable Bundle args,
288             @Nullable CancellationSignal cancellationSignal) throws RemoteException {
289         Preconditions.checkNotNull(url, "url");
290 
291         beforeRemote();
292         try {
293             ICancellationSignal remoteCancellationSignal = null;
294             if (cancellationSignal != null) {
295                 cancellationSignal.throwIfCanceled();
296                 remoteCancellationSignal = mContentProvider.createCancellationSignal();
297                 cancellationSignal.setRemote(remoteCancellationSignal);
298             }
299             return mContentProvider.refresh(mPackageName, url, args, remoteCancellationSignal);
300         } catch (DeadObjectException e) {
301             if (!mStable) {
302                 mContentResolver.unstableProviderDied(mContentProvider);
303             }
304             throw e;
305         } finally {
306             afterRemote();
307         }
308     }
309 
310     /** See {@link ContentProvider#insert ContentProvider.insert} */
311     @Override
insert(@onNull Uri url, @Nullable ContentValues initialValues)312     public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues)
313             throws RemoteException {
314         Preconditions.checkNotNull(url, "url");
315 
316         beforeRemote();
317         try {
318             return mContentProvider.insert(mPackageName, url, initialValues);
319         } catch (DeadObjectException e) {
320             if (!mStable) {
321                 mContentResolver.unstableProviderDied(mContentProvider);
322             }
323             throw e;
324         } finally {
325             afterRemote();
326         }
327     }
328 
329     /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
330     @Override
bulkInsert(@onNull Uri url, @NonNull ContentValues[] initialValues)331     public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] initialValues)
332             throws RemoteException {
333         Preconditions.checkNotNull(url, "url");
334         Preconditions.checkNotNull(initialValues, "initialValues");
335 
336         beforeRemote();
337         try {
338             return mContentProvider.bulkInsert(mPackageName, url, initialValues);
339         } catch (DeadObjectException e) {
340             if (!mStable) {
341                 mContentResolver.unstableProviderDied(mContentProvider);
342             }
343             throw e;
344         } finally {
345             afterRemote();
346         }
347     }
348 
349     /** See {@link ContentProvider#delete ContentProvider.delete} */
350     @Override
delete(@onNull Uri url, @Nullable String selection, @Nullable String[] selectionArgs)351     public int delete(@NonNull Uri url, @Nullable String selection,
352             @Nullable String[] selectionArgs) throws RemoteException {
353         Preconditions.checkNotNull(url, "url");
354 
355         beforeRemote();
356         try {
357             return mContentProvider.delete(mPackageName, url, selection, selectionArgs);
358         } catch (DeadObjectException e) {
359             if (!mStable) {
360                 mContentResolver.unstableProviderDied(mContentProvider);
361             }
362             throw e;
363         } finally {
364             afterRemote();
365         }
366     }
367 
368     /** See {@link ContentProvider#update ContentProvider.update} */
369     @Override
update(@onNull Uri url, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs)370     public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable String selection,
371             @Nullable String[] selectionArgs) throws RemoteException {
372         Preconditions.checkNotNull(url, "url");
373 
374         beforeRemote();
375         try {
376             return mContentProvider.update(mPackageName, url, values, selection, selectionArgs);
377         } catch (DeadObjectException e) {
378             if (!mStable) {
379                 mContentResolver.unstableProviderDied(mContentProvider);
380             }
381             throw e;
382         } finally {
383             afterRemote();
384         }
385     }
386 
387     /**
388      * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
389      * this <em>does not</em>
390      * take care of non-content: URIs such as file:.  It is strongly recommended
391      * you use the {@link ContentResolver#openFileDescriptor
392      * ContentResolver.openFileDescriptor} API instead.
393      */
openFile(@onNull Uri url, @NonNull String mode)394     public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode)
395             throws RemoteException, FileNotFoundException {
396         return openFile(url, mode, null);
397     }
398 
399     /**
400      * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
401      * this <em>does not</em>
402      * take care of non-content: URIs such as file:.  It is strongly recommended
403      * you use the {@link ContentResolver#openFileDescriptor
404      * ContentResolver.openFileDescriptor} API instead.
405      */
406     @Override
openFile(@onNull Uri url, @NonNull String mode, @Nullable CancellationSignal signal)407     public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode,
408             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
409         Preconditions.checkNotNull(url, "url");
410         Preconditions.checkNotNull(mode, "mode");
411 
412         beforeRemote();
413         try {
414             ICancellationSignal remoteSignal = null;
415             if (signal != null) {
416                 signal.throwIfCanceled();
417                 remoteSignal = mContentProvider.createCancellationSignal();
418                 signal.setRemote(remoteSignal);
419             }
420             return mContentProvider.openFile(mPackageName, url, mode, remoteSignal, null);
421         } catch (DeadObjectException e) {
422             if (!mStable) {
423                 mContentResolver.unstableProviderDied(mContentProvider);
424             }
425             throw e;
426         } finally {
427             afterRemote();
428         }
429     }
430 
431     /**
432      * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
433      * Note that this <em>does not</em>
434      * take care of non-content: URIs such as file:.  It is strongly recommended
435      * you use the {@link ContentResolver#openAssetFileDescriptor
436      * ContentResolver.openAssetFileDescriptor} API instead.
437      */
openAssetFile(@onNull Uri url, @NonNull String mode)438     public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode)
439             throws RemoteException, FileNotFoundException {
440         return openAssetFile(url, mode, null);
441     }
442 
443     /**
444      * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
445      * Note that this <em>does not</em>
446      * take care of non-content: URIs such as file:.  It is strongly recommended
447      * you use the {@link ContentResolver#openAssetFileDescriptor
448      * ContentResolver.openAssetFileDescriptor} API instead.
449      */
450     @Override
openAssetFile(@onNull Uri url, @NonNull String mode, @Nullable CancellationSignal signal)451     public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode,
452             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
453         Preconditions.checkNotNull(url, "url");
454         Preconditions.checkNotNull(mode, "mode");
455 
456         beforeRemote();
457         try {
458             ICancellationSignal remoteSignal = null;
459             if (signal != null) {
460                 signal.throwIfCanceled();
461                 remoteSignal = mContentProvider.createCancellationSignal();
462                 signal.setRemote(remoteSignal);
463             }
464             return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal);
465         } catch (DeadObjectException e) {
466             if (!mStable) {
467                 mContentResolver.unstableProviderDied(mContentProvider);
468             }
469             throw e;
470         } finally {
471             afterRemote();
472         }
473     }
474 
475     /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
openTypedAssetFileDescriptor(@onNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts)476     public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
477             @NonNull String mimeType, @Nullable Bundle opts)
478                     throws RemoteException, FileNotFoundException {
479         return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
480     }
481 
482     /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
openTypedAssetFileDescriptor(@onNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal)483     public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
484             @NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal)
485                     throws RemoteException, FileNotFoundException {
486         return openTypedAssetFile(uri, mimeType, opts, signal);
487     }
488 
489     @Override
openTypedAssetFile(@onNull Uri uri, @NonNull String mimeTypeFilter, @Nullable Bundle opts, @Nullable CancellationSignal signal)490     public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
491             @NonNull String mimeTypeFilter, @Nullable Bundle opts,
492             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
493         Preconditions.checkNotNull(uri, "uri");
494         Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter");
495 
496         beforeRemote();
497         try {
498             ICancellationSignal remoteSignal = null;
499             if (signal != null) {
500                 signal.throwIfCanceled();
501                 remoteSignal = mContentProvider.createCancellationSignal();
502                 signal.setRemote(remoteSignal);
503             }
504             return mContentProvider.openTypedAssetFile(
505                     mPackageName, uri, mimeTypeFilter, opts, remoteSignal);
506         } catch (DeadObjectException e) {
507             if (!mStable) {
508                 mContentResolver.unstableProviderDied(mContentProvider);
509             }
510             throw e;
511         } finally {
512             afterRemote();
513         }
514     }
515 
516     /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
applyBatch( @onNull ArrayList<ContentProviderOperation> operations)517     public @NonNull ContentProviderResult[] applyBatch(
518             @NonNull ArrayList<ContentProviderOperation> operations)
519             throws RemoteException, OperationApplicationException {
520         return applyBatch(mAuthority, operations);
521     }
522 
523     /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
524     @Override
applyBatch(@onNull String authority, @NonNull ArrayList<ContentProviderOperation> operations)525     public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
526             @NonNull ArrayList<ContentProviderOperation> operations)
527             throws RemoteException, OperationApplicationException {
528         Preconditions.checkNotNull(operations, "operations");
529 
530         beforeRemote();
531         try {
532             return mContentProvider.applyBatch(mPackageName, authority, operations);
533         } catch (DeadObjectException e) {
534             if (!mStable) {
535                 mContentResolver.unstableProviderDied(mContentProvider);
536             }
537             throw e;
538         } finally {
539             afterRemote();
540         }
541     }
542 
543     /** See {@link ContentProvider#call(String, String, Bundle)} */
call(@onNull String method, @Nullable String arg, @Nullable Bundle extras)544     public @Nullable Bundle call(@NonNull String method, @Nullable String arg,
545             @Nullable Bundle extras) throws RemoteException {
546         return call(mAuthority, method, arg, extras);
547     }
548 
549     /** See {@link ContentProvider#call(String, String, Bundle)} */
550     @Override
call(@onNull String authority, @NonNull String method, @Nullable String arg, @Nullable Bundle extras)551     public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
552             @Nullable String arg, @Nullable Bundle extras) throws RemoteException {
553         Preconditions.checkNotNull(authority, "authority");
554         Preconditions.checkNotNull(method, "method");
555 
556         beforeRemote();
557         try {
558             return mContentProvider.call(mPackageName, authority, method, arg, extras);
559         } catch (DeadObjectException e) {
560             if (!mStable) {
561                 mContentResolver.unstableProviderDied(mContentProvider);
562             }
563             throw e;
564         } finally {
565             afterRemote();
566         }
567     }
568 
569     /**
570      * Closes this client connection, indicating to the system that the
571      * underlying {@link ContentProvider} is no longer needed.
572      */
573     @Override
close()574     public void close() {
575         closeInternal();
576     }
577 
578     /**
579      * @deprecated replaced by {@link #close()}.
580      */
581     @Deprecated
release()582     public boolean release() {
583         return closeInternal();
584     }
585 
closeInternal()586     private boolean closeInternal() {
587         mCloseGuard.close();
588         if (mClosed.compareAndSet(false, true)) {
589             // We can't do ANR checks after we cease to exist! Reset any
590             // blocking behavior changes we might have made.
591             setDetectNotResponding(0);
592 
593             if (mStable) {
594                 return mContentResolver.releaseProvider(mContentProvider);
595             } else {
596                 return mContentResolver.releaseUnstableProvider(mContentProvider);
597             }
598         } else {
599             return false;
600         }
601     }
602 
603     @Override
finalize()604     protected void finalize() throws Throwable {
605         try {
606             if (mCloseGuard != null) {
607                 mCloseGuard.warnIfOpen();
608             }
609 
610             close();
611         } finally {
612             super.finalize();
613         }
614     }
615 
616     /**
617      * Get a reference to the {@link ContentProvider} that is associated with this
618      * client. If the {@link ContentProvider} is running in a different process then
619      * null will be returned. This can be used if you know you are running in the same
620      * process as a provider, and want to get direct access to its implementation details.
621      *
622      * @return If the associated {@link ContentProvider} is local, returns it.
623      * Otherwise returns null.
624      */
getLocalContentProvider()625     public @Nullable ContentProvider getLocalContentProvider() {
626         return ContentProvider.coerceToLocalContentProvider(mContentProvider);
627     }
628 
629     /** {@hide} */
630     @Deprecated
closeQuietly(ContentProviderClient client)631     public static void closeQuietly(ContentProviderClient client) {
632         IoUtils.closeQuietly(client);
633     }
634 
635     /** {@hide} */
636     @Deprecated
releaseQuietly(ContentProviderClient client)637     public static void releaseQuietly(ContentProviderClient client) {
638         IoUtils.closeQuietly(client);
639     }
640 
641     private class NotRespondingRunnable implements Runnable {
642         @Override
run()643         public void run() {
644             Log.w(TAG, "Detected provider not responding: " + mContentProvider);
645             mContentResolver.appNotRespondingViaProvider(mContentProvider);
646         }
647     }
648 
649     private final class CursorWrapperInner extends CrossProcessCursorWrapper {
650         private final CloseGuard mCloseGuard = CloseGuard.get();
651 
CursorWrapperInner(Cursor cursor)652         CursorWrapperInner(Cursor cursor) {
653             super(cursor);
654             mCloseGuard.open("close");
655         }
656 
657         @Override
close()658         public void close() {
659             mCloseGuard.close();
660             super.close();
661         }
662 
663         @Override
finalize()664         protected void finalize() throws Throwable {
665             try {
666                 if (mCloseGuard != null) {
667                     mCloseGuard.warnIfOpen();
668                 }
669 
670                 close();
671             } finally {
672                 super.finalize();
673             }
674         }
675     }
676 }
677