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