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 
45 import dalvik.system.CloseGuard;
46 
47 import libcore.io.IoUtils;
48 
49 import java.io.FileNotFoundException;
50 import java.util.ArrayList;
51 import java.util.Objects;
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 @Nullable String mAttributionTag;
84     private final String mAuthority;
85     private final boolean mStable;
86 
87     private final AtomicBoolean mClosed = new AtomicBoolean();
88     private final CloseGuard mCloseGuard = CloseGuard.get();
89 
90     private long mAnrTimeout;
91     private NotRespondingRunnable mAnrRunnable;
92 
93     /** {@hide} */
94     @VisibleForTesting
ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider, boolean stable)95     public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
96             boolean stable) {
97         // Only used for testing, so use a fake authority
98         this(contentResolver, contentProvider, "unknown", stable);
99     }
100 
101     /** {@hide} */
ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider, String authority, boolean stable)102     public ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider,
103             String authority, boolean stable) {
104         mContentResolver = contentResolver;
105         mContentProvider = contentProvider;
106         mPackageName = contentResolver.mPackageName;
107         mAttributionTag = contentResolver.mAttributionTag;
108 
109         mAuthority = authority;
110         mStable = stable;
111 
112         mCloseGuard.open("close");
113     }
114 
115     /**
116      * Configure this client to automatically detect and kill the remote
117      * provider when an "application not responding" event is detected.
118      *
119      * @param timeoutMillis the duration for which a pending call is allowed
120      *            block before the remote provider is considered to be
121      *            unresponsive. Set to {@code 0} to allow pending calls to block
122      *            indefinitely with no action taken.
123      * @hide
124      */
125     @SystemApi
126     @TestApi
127     @RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
setDetectNotResponding(@urationMillisLong long timeoutMillis)128     public void setDetectNotResponding(@DurationMillisLong long timeoutMillis) {
129         synchronized (ContentProviderClient.class) {
130             mAnrTimeout = timeoutMillis;
131 
132             if (timeoutMillis > 0) {
133                 if (mAnrRunnable == null) {
134                     mAnrRunnable = new NotRespondingRunnable();
135                 }
136                 if (sAnrHandler == null) {
137                     sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
138                 }
139 
140                 // If the remote process hangs, we're going to kill it, so we're
141                 // technically okay doing blocking calls.
142                 Binder.allowBlocking(mContentProvider.asBinder());
143             } else {
144                 mAnrRunnable = null;
145 
146                 // If we're no longer watching for hangs, revert back to default
147                 // blocking behavior.
148                 Binder.defaultBlocking(mContentProvider.asBinder());
149             }
150         }
151     }
152 
beforeRemote()153     private void beforeRemote() {
154         if (mAnrRunnable != null) {
155             sAnrHandler.postDelayed(mAnrRunnable, mAnrTimeout);
156         }
157     }
158 
afterRemote()159     private void afterRemote() {
160         if (mAnrRunnable != null) {
161             sAnrHandler.removeCallbacks(mAnrRunnable);
162         }
163     }
164 
165     /** See {@link ContentProvider#query ContentProvider.query} */
query(@onNull Uri url, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder)166     public @Nullable Cursor query(@NonNull Uri url, @Nullable String[] projection,
167             @Nullable String selection, @Nullable String[] selectionArgs,
168             @Nullable String sortOrder) throws RemoteException {
169         return query(url, projection, selection,  selectionArgs, sortOrder, null);
170     }
171 
172     /** 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)173     public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
174             @Nullable String selection, @Nullable String[] selectionArgs,
175             @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal)
176                     throws RemoteException {
177         Bundle queryArgs =
178                 ContentResolver.createSqlQueryBundle(selection, selectionArgs, sortOrder);
179         return query(uri, projection, queryArgs, cancellationSignal);
180     }
181 
182     /** See {@link ContentProvider#query ContentProvider.query} */
183     @Override
query(@onNull Uri uri, @Nullable String[] projection, Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)184     public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
185             Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
186                     throws RemoteException {
187         Objects.requireNonNull(uri, "url");
188 
189         beforeRemote();
190         try {
191             ICancellationSignal remoteCancellationSignal = null;
192             if (cancellationSignal != null) {
193                 cancellationSignal.throwIfCanceled();
194                 remoteCancellationSignal = mContentProvider.createCancellationSignal();
195                 cancellationSignal.setRemote(remoteCancellationSignal);
196             }
197             final Cursor cursor = mContentProvider.query(
198                     mPackageName, mAttributionTag, uri, projection, queryArgs,
199                     remoteCancellationSignal);
200             if (cursor == null) {
201                 return null;
202             }
203             return new CursorWrapperInner(cursor);
204         } catch (DeadObjectException e) {
205             if (!mStable) {
206                 mContentResolver.unstableProviderDied(mContentProvider);
207             }
208             throw e;
209         } finally {
210             afterRemote();
211         }
212     }
213 
214     /** See {@link ContentProvider#getType ContentProvider.getType} */
215     @Override
getType(@onNull Uri url)216     public @Nullable String getType(@NonNull Uri url) throws RemoteException {
217         Objects.requireNonNull(url, "url");
218 
219         beforeRemote();
220         try {
221             return mContentProvider.getType(url);
222         } catch (DeadObjectException e) {
223             if (!mStable) {
224                 mContentResolver.unstableProviderDied(mContentProvider);
225             }
226             throw e;
227         } finally {
228             afterRemote();
229         }
230     }
231 
232     /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
233     @Override
getStreamTypes(@onNull Uri url, @NonNull String mimeTypeFilter)234     public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter)
235             throws RemoteException {
236         Objects.requireNonNull(url, "url");
237         Objects.requireNonNull(mimeTypeFilter, "mimeTypeFilter");
238 
239         beforeRemote();
240         try {
241             return mContentProvider.getStreamTypes(url, mimeTypeFilter);
242         } catch (DeadObjectException e) {
243             if (!mStable) {
244                 mContentResolver.unstableProviderDied(mContentProvider);
245             }
246             throw e;
247         } finally {
248             afterRemote();
249         }
250     }
251 
252     /** See {@link ContentProvider#canonicalize} */
253     @Override
canonicalize(@onNull Uri url)254     public final @Nullable Uri canonicalize(@NonNull Uri url) throws RemoteException {
255         Objects.requireNonNull(url, "url");
256 
257         beforeRemote();
258         try {
259             return mContentProvider.canonicalize(mPackageName, mAttributionTag, url);
260         } catch (DeadObjectException e) {
261             if (!mStable) {
262                 mContentResolver.unstableProviderDied(mContentProvider);
263             }
264             throw e;
265         } finally {
266             afterRemote();
267         }
268     }
269 
270     /** See {@link ContentProvider#uncanonicalize} */
271     @Override
uncanonicalize(@onNull Uri url)272     public final @Nullable Uri uncanonicalize(@NonNull Uri url) throws RemoteException {
273         Objects.requireNonNull(url, "url");
274 
275         beforeRemote();
276         try {
277             return mContentProvider.uncanonicalize(mPackageName, mAttributionTag, url);
278         } catch (DeadObjectException e) {
279             if (!mStable) {
280                 mContentResolver.unstableProviderDied(mContentProvider);
281             }
282             throw e;
283         } finally {
284             afterRemote();
285         }
286     }
287 
288     /** See {@link ContentProvider#refresh} */
289     @Override
refresh(Uri url, @Nullable Bundle extras, @Nullable CancellationSignal cancellationSignal)290     public boolean refresh(Uri url, @Nullable Bundle extras,
291             @Nullable CancellationSignal cancellationSignal) throws RemoteException {
292         Objects.requireNonNull(url, "url");
293 
294         beforeRemote();
295         try {
296             ICancellationSignal remoteCancellationSignal = null;
297             if (cancellationSignal != null) {
298                 cancellationSignal.throwIfCanceled();
299                 remoteCancellationSignal = mContentProvider.createCancellationSignal();
300                 cancellationSignal.setRemote(remoteCancellationSignal);
301             }
302             return mContentProvider.refresh(mPackageName, mAttributionTag, url, extras,
303                     remoteCancellationSignal);
304         } catch (DeadObjectException e) {
305             if (!mStable) {
306                 mContentResolver.unstableProviderDied(mContentProvider);
307             }
308             throw e;
309         } finally {
310             afterRemote();
311         }
312     }
313 
314     /** {@hide} */
315     @Override
checkUriPermission(@onNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags)316     public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags)
317             throws RemoteException {
318         Objects.requireNonNull(uri, "uri");
319 
320         beforeRemote();
321         try {
322             return mContentProvider.checkUriPermission(mPackageName, mAttributionTag, uri, uid,
323                     modeFlags);
324         } catch (DeadObjectException e) {
325             if (!mStable) {
326                 mContentResolver.unstableProviderDied(mContentProvider);
327             }
328             throw e;
329         } finally {
330             afterRemote();
331         }
332     }
333 
334     /** See {@link ContentProvider#insert ContentProvider.insert} */
insert(@onNull Uri url, @Nullable ContentValues initialValues)335     public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues)
336             throws RemoteException {
337         return insert(url, initialValues, null);
338     }
339 
340     /** See {@link ContentProvider#insert ContentProvider.insert} */
341     @Override
insert(@onNull Uri url, @Nullable ContentValues initialValues, @Nullable Bundle extras)342     public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues,
343             @Nullable Bundle extras) throws RemoteException {
344         Objects.requireNonNull(url, "url");
345 
346         beforeRemote();
347         try {
348             return mContentProvider.insert(mPackageName, mAttributionTag, url, initialValues,
349                     extras);
350         } catch (DeadObjectException e) {
351             if (!mStable) {
352                 mContentResolver.unstableProviderDied(mContentProvider);
353             }
354             throw e;
355         } finally {
356             afterRemote();
357         }
358     }
359 
360     /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
361     @Override
bulkInsert(@onNull Uri url, @NonNull ContentValues[] initialValues)362     public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] initialValues)
363             throws RemoteException {
364         Objects.requireNonNull(url, "url");
365         Objects.requireNonNull(initialValues, "initialValues");
366 
367         beforeRemote();
368         try {
369             return mContentProvider.bulkInsert(mPackageName, mAttributionTag, url, initialValues);
370         } catch (DeadObjectException e) {
371             if (!mStable) {
372                 mContentResolver.unstableProviderDied(mContentProvider);
373             }
374             throw e;
375         } finally {
376             afterRemote();
377         }
378     }
379 
380     /** See {@link ContentProvider#delete ContentProvider.delete} */
delete(@onNull Uri url, @Nullable String selection, @Nullable String[] selectionArgs)381     public int delete(@NonNull Uri url, @Nullable String selection,
382             @Nullable String[] selectionArgs) throws RemoteException {
383         return delete(url, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
384     }
385 
386     /** See {@link ContentProvider#delete ContentProvider.delete} */
387     @Override
delete(@onNull Uri url, @Nullable Bundle extras)388     public int delete(@NonNull Uri url, @Nullable Bundle extras) throws RemoteException {
389         Objects.requireNonNull(url, "url");
390 
391         beforeRemote();
392         try {
393             return mContentProvider.delete(mPackageName, mAttributionTag, url, extras);
394         } catch (DeadObjectException e) {
395             if (!mStable) {
396                 mContentResolver.unstableProviderDied(mContentProvider);
397             }
398             throw e;
399         } finally {
400             afterRemote();
401         }
402     }
403 
404     /** See {@link ContentProvider#update ContentProvider.update} */
update(@onNull Uri url, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs)405     public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable String selection,
406             @Nullable String[] selectionArgs) throws RemoteException {
407         return update(url, values, ContentResolver.createSqlQueryBundle(selection, selectionArgs));
408     }
409 
410     /** See {@link ContentProvider#update ContentProvider.update} */
411     @Override
update(@onNull Uri url, @Nullable ContentValues values, @Nullable Bundle extras)412     public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable Bundle extras)
413             throws RemoteException {
414         Objects.requireNonNull(url, "url");
415 
416         beforeRemote();
417         try {
418             return mContentProvider.update(mPackageName, mAttributionTag, url, values, extras);
419         } catch (DeadObjectException e) {
420             if (!mStable) {
421                 mContentResolver.unstableProviderDied(mContentProvider);
422             }
423             throw e;
424         } finally {
425             afterRemote();
426         }
427     }
428 
429     /**
430      * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
431      * this <em>does not</em>
432      * take care of non-content: URIs such as file:.  It is strongly recommended
433      * you use the {@link ContentResolver#openFileDescriptor
434      * ContentResolver.openFileDescriptor} API instead.
435      */
openFile(@onNull Uri url, @NonNull String mode)436     public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode)
437             throws RemoteException, FileNotFoundException {
438         return openFile(url, mode, null);
439     }
440 
441     /**
442      * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
443      * this <em>does not</em>
444      * take care of non-content: URIs such as file:.  It is strongly recommended
445      * you use the {@link ContentResolver#openFileDescriptor
446      * ContentResolver.openFileDescriptor} API instead.
447      */
448     @Override
openFile(@onNull Uri url, @NonNull String mode, @Nullable CancellationSignal signal)449     public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode,
450             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
451         Objects.requireNonNull(url, "url");
452         Objects.requireNonNull(mode, "mode");
453 
454         beforeRemote();
455         try {
456             ICancellationSignal remoteSignal = null;
457             if (signal != null) {
458                 signal.throwIfCanceled();
459                 remoteSignal = mContentProvider.createCancellationSignal();
460                 signal.setRemote(remoteSignal);
461             }
462             return mContentProvider.openFile(mPackageName, mAttributionTag, url, mode,
463                     remoteSignal, null);
464         } catch (DeadObjectException e) {
465             if (!mStable) {
466                 mContentResolver.unstableProviderDied(mContentProvider);
467             }
468             throw e;
469         } finally {
470             afterRemote();
471         }
472     }
473 
474     /**
475      * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
476      * Note that this <em>does not</em>
477      * take care of non-content: URIs such as file:.  It is strongly recommended
478      * you use the {@link ContentResolver#openAssetFileDescriptor
479      * ContentResolver.openAssetFileDescriptor} API instead.
480      */
openAssetFile(@onNull Uri url, @NonNull String mode)481     public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode)
482             throws RemoteException, FileNotFoundException {
483         return openAssetFile(url, mode, null);
484     }
485 
486     /**
487      * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
488      * Note that this <em>does not</em>
489      * take care of non-content: URIs such as file:.  It is strongly recommended
490      * you use the {@link ContentResolver#openAssetFileDescriptor
491      * ContentResolver.openAssetFileDescriptor} API instead.
492      */
493     @Override
openAssetFile(@onNull Uri url, @NonNull String mode, @Nullable CancellationSignal signal)494     public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode,
495             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
496         Objects.requireNonNull(url, "url");
497         Objects.requireNonNull(mode, "mode");
498 
499         beforeRemote();
500         try {
501             ICancellationSignal remoteSignal = null;
502             if (signal != null) {
503                 signal.throwIfCanceled();
504                 remoteSignal = mContentProvider.createCancellationSignal();
505                 signal.setRemote(remoteSignal);
506             }
507             return mContentProvider.openAssetFile(mPackageName, mAttributionTag, url, mode,
508                     remoteSignal);
509         } catch (DeadObjectException e) {
510             if (!mStable) {
511                 mContentResolver.unstableProviderDied(mContentProvider);
512             }
513             throw e;
514         } finally {
515             afterRemote();
516         }
517     }
518 
519     /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
openTypedAssetFileDescriptor(@onNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts)520     public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
521             @NonNull String mimeType, @Nullable Bundle opts)
522                     throws RemoteException, FileNotFoundException {
523         return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
524     }
525 
526     /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
openTypedAssetFileDescriptor(@onNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal)527     public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
528             @NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal)
529                     throws RemoteException, FileNotFoundException {
530         return openTypedAssetFile(uri, mimeType, opts, signal);
531     }
532 
533     @Override
openTypedAssetFile(@onNull Uri uri, @NonNull String mimeTypeFilter, @Nullable Bundle opts, @Nullable CancellationSignal signal)534     public final @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri,
535             @NonNull String mimeTypeFilter, @Nullable Bundle opts,
536             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
537         Objects.requireNonNull(uri, "uri");
538         Objects.requireNonNull(mimeTypeFilter, "mimeTypeFilter");
539 
540         beforeRemote();
541         try {
542             ICancellationSignal remoteSignal = null;
543             if (signal != null) {
544                 signal.throwIfCanceled();
545                 remoteSignal = mContentProvider.createCancellationSignal();
546                 signal.setRemote(remoteSignal);
547             }
548             return mContentProvider.openTypedAssetFile(
549                     mPackageName, mAttributionTag, uri, mimeTypeFilter, opts, remoteSignal);
550         } catch (DeadObjectException e) {
551             if (!mStable) {
552                 mContentResolver.unstableProviderDied(mContentProvider);
553             }
554             throw e;
555         } finally {
556             afterRemote();
557         }
558     }
559 
560     /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
applyBatch( @onNull ArrayList<ContentProviderOperation> operations)561     public @NonNull ContentProviderResult[] applyBatch(
562             @NonNull ArrayList<ContentProviderOperation> operations)
563             throws RemoteException, OperationApplicationException {
564         return applyBatch(mAuthority, operations);
565     }
566 
567     /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
568     @Override
applyBatch(@onNull String authority, @NonNull ArrayList<ContentProviderOperation> operations)569     public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
570             @NonNull ArrayList<ContentProviderOperation> operations)
571             throws RemoteException, OperationApplicationException {
572         Objects.requireNonNull(operations, "operations");
573 
574         beforeRemote();
575         try {
576             return mContentProvider.applyBatch(mPackageName, mAttributionTag, authority,
577                     operations);
578         } catch (DeadObjectException e) {
579             if (!mStable) {
580                 mContentResolver.unstableProviderDied(mContentProvider);
581             }
582             throw e;
583         } finally {
584             afterRemote();
585         }
586     }
587 
588     /** See {@link ContentProvider#call(String, String, Bundle)} */
call(@onNull String method, @Nullable String arg, @Nullable Bundle extras)589     public @Nullable Bundle call(@NonNull String method, @Nullable String arg,
590             @Nullable Bundle extras) throws RemoteException {
591         return call(mAuthority, method, arg, extras);
592     }
593 
594     /** See {@link ContentProvider#call(String, String, Bundle)} */
595     @Override
call(@onNull String authority, @NonNull String method, @Nullable String arg, @Nullable Bundle extras)596     public @Nullable Bundle call(@NonNull String authority, @NonNull String method,
597             @Nullable String arg, @Nullable Bundle extras) throws RemoteException {
598         Objects.requireNonNull(authority, "authority");
599         Objects.requireNonNull(method, "method");
600 
601         beforeRemote();
602         try {
603             return mContentProvider.call(mPackageName, mAttributionTag, authority, method, arg,
604                     extras);
605         } catch (DeadObjectException e) {
606             if (!mStable) {
607                 mContentResolver.unstableProviderDied(mContentProvider);
608             }
609             throw e;
610         } finally {
611             afterRemote();
612         }
613     }
614 
615     /**
616      * Closes this client connection, indicating to the system that the
617      * underlying {@link ContentProvider} is no longer needed.
618      */
619     @Override
close()620     public void close() {
621         closeInternal();
622     }
623 
624     /**
625      * @deprecated replaced by {@link #close()}.
626      */
627     @Deprecated
release()628     public boolean release() {
629         return closeInternal();
630     }
631 
closeInternal()632     private boolean closeInternal() {
633         mCloseGuard.close();
634         if (mClosed.compareAndSet(false, true)) {
635             // We can't do ANR checks after we cease to exist! Reset any
636             // blocking behavior changes we might have made.
637             setDetectNotResponding(0);
638 
639             if (mStable) {
640                 return mContentResolver.releaseProvider(mContentProvider);
641             } else {
642                 return mContentResolver.releaseUnstableProvider(mContentProvider);
643             }
644         } else {
645             return false;
646         }
647     }
648 
649     @Override
finalize()650     protected void finalize() throws Throwable {
651         try {
652             if (mCloseGuard != null) {
653                 mCloseGuard.warnIfOpen();
654             }
655 
656             close();
657         } finally {
658             super.finalize();
659         }
660     }
661 
662     /**
663      * Get a reference to the {@link ContentProvider} that is associated with this
664      * client. If the {@link ContentProvider} is running in a different process then
665      * null will be returned. This can be used if you know you are running in the same
666      * process as a provider, and want to get direct access to its implementation details.
667      *
668      * @return If the associated {@link ContentProvider} is local, returns it.
669      * Otherwise returns null.
670      */
getLocalContentProvider()671     public @Nullable ContentProvider getLocalContentProvider() {
672         return ContentProvider.coerceToLocalContentProvider(mContentProvider);
673     }
674 
675     /** {@hide} */
676     @Deprecated
closeQuietly(ContentProviderClient client)677     public static void closeQuietly(ContentProviderClient client) {
678         IoUtils.closeQuietly(client);
679     }
680 
681     /** {@hide} */
682     @Deprecated
releaseQuietly(ContentProviderClient client)683     public static void releaseQuietly(ContentProviderClient client) {
684         IoUtils.closeQuietly(client);
685     }
686 
687     private class NotRespondingRunnable implements Runnable {
688         @Override
run()689         public void run() {
690             Log.w(TAG, "Detected provider not responding: " + mContentProvider);
691             mContentResolver.appNotRespondingViaProvider(mContentProvider);
692         }
693     }
694 
695     private final class CursorWrapperInner extends CrossProcessCursorWrapper {
696         private final CloseGuard mCloseGuard = CloseGuard.get();
697 
CursorWrapperInner(Cursor cursor)698         CursorWrapperInner(Cursor cursor) {
699             super(cursor);
700             mCloseGuard.open("close");
701         }
702 
703         @Override
close()704         public void close() {
705             mCloseGuard.close();
706             super.close();
707         }
708 
709         @Override
finalize()710         protected void finalize() throws Throwable {
711             try {
712                 if (mCloseGuard != null) {
713                     mCloseGuard.warnIfOpen();
714                 }
715 
716                 close();
717             } finally {
718                 super.finalize();
719             }
720         }
721     }
722 }
723