• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.NonNull;
20 import android.annotation.Nullable;
21 import android.content.res.AssetFileDescriptor;
22 import android.database.CrossProcessCursorWrapper;
23 import android.database.Cursor;
24 import android.net.Uri;
25 import android.os.Bundle;
26 import android.os.CancellationSignal;
27 import android.os.DeadObjectException;
28 import android.os.Handler;
29 import android.os.ICancellationSignal;
30 import android.os.Looper;
31 import android.os.ParcelFileDescriptor;
32 import android.os.RemoteException;
33 import android.util.Log;
34 
35 import com.android.internal.annotations.GuardedBy;
36 import com.android.internal.annotations.VisibleForTesting;
37 import com.android.internal.util.Preconditions;
38 
39 import dalvik.system.CloseGuard;
40 
41 import java.io.FileNotFoundException;
42 import java.util.ArrayList;
43 import java.util.concurrent.atomic.AtomicBoolean;
44 
45 /**
46  * The public interface object used to interact with a specific
47  * {@link ContentProvider}.
48  * <p>
49  * Instances can be obtained by calling
50  * {@link ContentResolver#acquireContentProviderClient} or
51  * {@link ContentResolver#acquireUnstableContentProviderClient}. Instances must
52  * be released using {@link #close()} in order to indicate to the system that
53  * the underlying {@link ContentProvider} is no longer needed and can be killed
54  * to free up resources.
55  * <p>
56  * Note that you should generally create a new ContentProviderClient instance
57  * for each thread that will be performing operations. Unlike
58  * {@link ContentResolver}, the methods here such as {@link #query} and
59  * {@link #openFile} are not thread safe -- you must not call {@link #close()}
60  * on the ContentProviderClient those calls are made from until you are finished
61  * with the data they have returned.
62  */
63 public class ContentProviderClient implements AutoCloseable {
64     private static final String TAG = "ContentProviderClient";
65 
66     @GuardedBy("ContentProviderClient.class")
67     private static Handler sAnrHandler;
68 
69     private final ContentResolver mContentResolver;
70     private final IContentProvider mContentProvider;
71     private final String mPackageName;
72     private final boolean mStable;
73 
74     private final AtomicBoolean mClosed = new AtomicBoolean();
75     private final CloseGuard mCloseGuard = CloseGuard.get();
76 
77     private long mAnrTimeout;
78     private NotRespondingRunnable mAnrRunnable;
79 
80     /** {@hide} */
81     @VisibleForTesting
ContentProviderClient( ContentResolver contentResolver, IContentProvider contentProvider, boolean stable)82     public ContentProviderClient(
83             ContentResolver contentResolver, IContentProvider contentProvider, boolean stable) {
84         mContentResolver = contentResolver;
85         mContentProvider = contentProvider;
86         mPackageName = contentResolver.mPackageName;
87 
88         mStable = stable;
89 
90         mCloseGuard.open("close");
91     }
92 
93     /** {@hide} */
setDetectNotResponding(long timeoutMillis)94     public void setDetectNotResponding(long timeoutMillis) {
95         synchronized (ContentProviderClient.class) {
96             mAnrTimeout = timeoutMillis;
97 
98             if (timeoutMillis > 0) {
99                 if (mAnrRunnable == null) {
100                     mAnrRunnable = new NotRespondingRunnable();
101                 }
102                 if (sAnrHandler == null) {
103                     sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
104                 }
105             } else {
106                 mAnrRunnable = null;
107             }
108         }
109     }
110 
beforeRemote()111     private void beforeRemote() {
112         if (mAnrRunnable != null) {
113             sAnrHandler.postDelayed(mAnrRunnable, mAnrTimeout);
114         }
115     }
116 
afterRemote()117     private void afterRemote() {
118         if (mAnrRunnable != null) {
119             sAnrHandler.removeCallbacks(mAnrRunnable);
120         }
121     }
122 
123     /** See {@link ContentProvider#query ContentProvider.query} */
query(@onNull Uri url, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder)124     public @Nullable Cursor query(@NonNull Uri url, @Nullable String[] projection,
125             @Nullable String selection, @Nullable String[] selectionArgs,
126             @Nullable String sortOrder) throws RemoteException {
127         return query(url, projection, selection,  selectionArgs, sortOrder, null);
128     }
129 
130     /** 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)131     public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
132             @Nullable String selection, @Nullable String[] selectionArgs,
133             @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal)
134                     throws RemoteException {
135         Bundle queryArgs =
136                 ContentResolver.createSqlQueryBundle(selection, selectionArgs, sortOrder);
137         return query(uri, projection, queryArgs, cancellationSignal);
138     }
139 
140     /** See {@link ContentProvider#query ContentProvider.query} */
query(@onNull Uri uri, @Nullable String[] projection, Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)141     public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
142             Bundle queryArgs, @Nullable CancellationSignal cancellationSignal)
143                     throws RemoteException {
144         Preconditions.checkNotNull(uri, "url");
145 
146         beforeRemote();
147         try {
148             ICancellationSignal remoteCancellationSignal = null;
149             if (cancellationSignal != null) {
150                 cancellationSignal.throwIfCanceled();
151                 remoteCancellationSignal = mContentProvider.createCancellationSignal();
152                 cancellationSignal.setRemote(remoteCancellationSignal);
153             }
154             final Cursor cursor = mContentProvider.query(
155                     mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
156             if (cursor == null) {
157                 return null;
158             }
159             return new CursorWrapperInner(cursor);
160         } catch (DeadObjectException e) {
161             if (!mStable) {
162                 mContentResolver.unstableProviderDied(mContentProvider);
163             }
164             throw e;
165         } finally {
166             afterRemote();
167         }
168     }
169 
170     /** See {@link ContentProvider#getType ContentProvider.getType} */
getType(@onNull Uri url)171     public @Nullable String getType(@NonNull Uri url) throws RemoteException {
172         Preconditions.checkNotNull(url, "url");
173 
174         beforeRemote();
175         try {
176             return mContentProvider.getType(url);
177         } catch (DeadObjectException e) {
178             if (!mStable) {
179                 mContentResolver.unstableProviderDied(mContentProvider);
180             }
181             throw e;
182         } finally {
183             afterRemote();
184         }
185     }
186 
187     /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
getStreamTypes(@onNull Uri url, @NonNull String mimeTypeFilter)188     public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter)
189             throws RemoteException {
190         Preconditions.checkNotNull(url, "url");
191         Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter");
192 
193         beforeRemote();
194         try {
195             return mContentProvider.getStreamTypes(url, mimeTypeFilter);
196         } catch (DeadObjectException e) {
197             if (!mStable) {
198                 mContentResolver.unstableProviderDied(mContentProvider);
199             }
200             throw e;
201         } finally {
202             afterRemote();
203         }
204     }
205 
206     /** See {@link ContentProvider#canonicalize} */
canonicalize(@onNull Uri url)207     public final @Nullable Uri canonicalize(@NonNull Uri url) throws RemoteException {
208         Preconditions.checkNotNull(url, "url");
209 
210         beforeRemote();
211         try {
212             return mContentProvider.canonicalize(mPackageName, url);
213         } catch (DeadObjectException e) {
214             if (!mStable) {
215                 mContentResolver.unstableProviderDied(mContentProvider);
216             }
217             throw e;
218         } finally {
219             afterRemote();
220         }
221     }
222 
223     /** See {@link ContentProvider#uncanonicalize} */
uncanonicalize(@onNull Uri url)224     public final @Nullable Uri uncanonicalize(@NonNull Uri url) throws RemoteException {
225         Preconditions.checkNotNull(url, "url");
226 
227         beforeRemote();
228         try {
229             return mContentProvider.uncanonicalize(mPackageName, url);
230         } catch (DeadObjectException e) {
231             if (!mStable) {
232                 mContentResolver.unstableProviderDied(mContentProvider);
233             }
234             throw e;
235         } finally {
236             afterRemote();
237         }
238     }
239 
240     /** See {@link ContentProvider#refresh} */
refresh(Uri url, @Nullable Bundle args, @Nullable CancellationSignal cancellationSignal)241     public boolean refresh(Uri url, @Nullable Bundle args,
242             @Nullable CancellationSignal cancellationSignal) throws RemoteException {
243         Preconditions.checkNotNull(url, "url");
244 
245         beforeRemote();
246         try {
247             ICancellationSignal remoteCancellationSignal = null;
248             if (cancellationSignal != null) {
249                 cancellationSignal.throwIfCanceled();
250                 remoteCancellationSignal = mContentProvider.createCancellationSignal();
251                 cancellationSignal.setRemote(remoteCancellationSignal);
252             }
253             return mContentProvider.refresh(mPackageName, url, args, remoteCancellationSignal);
254         } catch (DeadObjectException e) {
255             if (!mStable) {
256                 mContentResolver.unstableProviderDied(mContentProvider);
257             }
258             throw e;
259         } finally {
260             afterRemote();
261         }
262     }
263 
264     /** See {@link ContentProvider#insert ContentProvider.insert} */
insert(@onNull Uri url, @Nullable ContentValues initialValues)265     public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues)
266             throws RemoteException {
267         Preconditions.checkNotNull(url, "url");
268 
269         beforeRemote();
270         try {
271             return mContentProvider.insert(mPackageName, url, initialValues);
272         } catch (DeadObjectException e) {
273             if (!mStable) {
274                 mContentResolver.unstableProviderDied(mContentProvider);
275             }
276             throw e;
277         } finally {
278             afterRemote();
279         }
280     }
281 
282     /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
bulkInsert(@onNull Uri url, @NonNull ContentValues[] initialValues)283     public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] initialValues)
284             throws RemoteException {
285         Preconditions.checkNotNull(url, "url");
286         Preconditions.checkNotNull(initialValues, "initialValues");
287 
288         beforeRemote();
289         try {
290             return mContentProvider.bulkInsert(mPackageName, url, initialValues);
291         } catch (DeadObjectException e) {
292             if (!mStable) {
293                 mContentResolver.unstableProviderDied(mContentProvider);
294             }
295             throw e;
296         } finally {
297             afterRemote();
298         }
299     }
300 
301     /** See {@link ContentProvider#delete ContentProvider.delete} */
delete(@onNull Uri url, @Nullable String selection, @Nullable String[] selectionArgs)302     public int delete(@NonNull Uri url, @Nullable String selection,
303             @Nullable String[] selectionArgs) throws RemoteException {
304         Preconditions.checkNotNull(url, "url");
305 
306         beforeRemote();
307         try {
308             return mContentProvider.delete(mPackageName, url, selection, selectionArgs);
309         } catch (DeadObjectException e) {
310             if (!mStable) {
311                 mContentResolver.unstableProviderDied(mContentProvider);
312             }
313             throw e;
314         } finally {
315             afterRemote();
316         }
317     }
318 
319     /** See {@link ContentProvider#update ContentProvider.update} */
update(@onNull Uri url, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs)320     public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable String selection,
321             @Nullable String[] selectionArgs) throws RemoteException {
322         Preconditions.checkNotNull(url, "url");
323 
324         beforeRemote();
325         try {
326             return mContentProvider.update(mPackageName, url, values, selection, selectionArgs);
327         } catch (DeadObjectException e) {
328             if (!mStable) {
329                 mContentResolver.unstableProviderDied(mContentProvider);
330             }
331             throw e;
332         } finally {
333             afterRemote();
334         }
335     }
336 
337     /**
338      * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
339      * this <em>does not</em>
340      * take care of non-content: URIs such as file:.  It is strongly recommended
341      * you use the {@link ContentResolver#openFileDescriptor
342      * ContentResolver.openFileDescriptor} API instead.
343      */
openFile(@onNull Uri url, @NonNull String mode)344     public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode)
345             throws RemoteException, FileNotFoundException {
346         return openFile(url, mode, null);
347     }
348 
349     /**
350      * See {@link ContentProvider#openFile ContentProvider.openFile}.  Note that
351      * this <em>does not</em>
352      * take care of non-content: URIs such as file:.  It is strongly recommended
353      * you use the {@link ContentResolver#openFileDescriptor
354      * ContentResolver.openFileDescriptor} API instead.
355      */
openFile(@onNull Uri url, @NonNull String mode, @Nullable CancellationSignal signal)356     public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode,
357             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
358         Preconditions.checkNotNull(url, "url");
359         Preconditions.checkNotNull(mode, "mode");
360 
361         beforeRemote();
362         try {
363             ICancellationSignal remoteSignal = null;
364             if (signal != null) {
365                 signal.throwIfCanceled();
366                 remoteSignal = mContentProvider.createCancellationSignal();
367                 signal.setRemote(remoteSignal);
368             }
369             return mContentProvider.openFile(mPackageName, url, mode, remoteSignal, null);
370         } catch (DeadObjectException e) {
371             if (!mStable) {
372                 mContentResolver.unstableProviderDied(mContentProvider);
373             }
374             throw e;
375         } finally {
376             afterRemote();
377         }
378     }
379 
380     /**
381      * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
382      * Note that this <em>does not</em>
383      * take care of non-content: URIs such as file:.  It is strongly recommended
384      * you use the {@link ContentResolver#openAssetFileDescriptor
385      * ContentResolver.openAssetFileDescriptor} API instead.
386      */
openAssetFile(@onNull Uri url, @NonNull String mode)387     public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode)
388             throws RemoteException, FileNotFoundException {
389         return openAssetFile(url, mode, null);
390     }
391 
392     /**
393      * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
394      * Note that this <em>does not</em>
395      * take care of non-content: URIs such as file:.  It is strongly recommended
396      * you use the {@link ContentResolver#openAssetFileDescriptor
397      * ContentResolver.openAssetFileDescriptor} API instead.
398      */
openAssetFile(@onNull Uri url, @NonNull String mode, @Nullable CancellationSignal signal)399     public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode,
400             @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException {
401         Preconditions.checkNotNull(url, "url");
402         Preconditions.checkNotNull(mode, "mode");
403 
404         beforeRemote();
405         try {
406             ICancellationSignal remoteSignal = null;
407             if (signal != null) {
408                 signal.throwIfCanceled();
409                 remoteSignal = mContentProvider.createCancellationSignal();
410                 signal.setRemote(remoteSignal);
411             }
412             return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal);
413         } catch (DeadObjectException e) {
414             if (!mStable) {
415                 mContentResolver.unstableProviderDied(mContentProvider);
416             }
417             throw e;
418         } finally {
419             afterRemote();
420         }
421     }
422 
423     /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
openTypedAssetFileDescriptor(@onNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts)424     public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
425             @NonNull String mimeType, @Nullable Bundle opts)
426                     throws RemoteException, FileNotFoundException {
427         return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
428     }
429 
430     /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
openTypedAssetFileDescriptor(@onNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal)431     public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
432             @NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal)
433                     throws RemoteException, FileNotFoundException {
434         Preconditions.checkNotNull(uri, "uri");
435         Preconditions.checkNotNull(mimeType, "mimeType");
436 
437         beforeRemote();
438         try {
439             ICancellationSignal remoteSignal = null;
440             if (signal != null) {
441                 signal.throwIfCanceled();
442                 remoteSignal = mContentProvider.createCancellationSignal();
443                 signal.setRemote(remoteSignal);
444             }
445             return mContentProvider.openTypedAssetFile(
446                     mPackageName, uri, mimeType, opts, remoteSignal);
447         } catch (DeadObjectException e) {
448             if (!mStable) {
449                 mContentResolver.unstableProviderDied(mContentProvider);
450             }
451             throw e;
452         } finally {
453             afterRemote();
454         }
455     }
456 
457     /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
applyBatch( @onNull ArrayList<ContentProviderOperation> operations)458     public @NonNull ContentProviderResult[] applyBatch(
459             @NonNull ArrayList<ContentProviderOperation> operations)
460                     throws RemoteException, OperationApplicationException {
461         Preconditions.checkNotNull(operations, "operations");
462 
463         beforeRemote();
464         try {
465             return mContentProvider.applyBatch(mPackageName, operations);
466         } catch (DeadObjectException e) {
467             if (!mStable) {
468                 mContentResolver.unstableProviderDied(mContentProvider);
469             }
470             throw e;
471         } finally {
472             afterRemote();
473         }
474     }
475 
476     /** See {@link ContentProvider#call(String, String, Bundle)} */
call(@onNull String method, @Nullable String arg, @Nullable Bundle extras)477     public @Nullable Bundle call(@NonNull String method, @Nullable String arg,
478             @Nullable Bundle extras) throws RemoteException {
479         Preconditions.checkNotNull(method, "method");
480 
481         beforeRemote();
482         try {
483             return mContentProvider.call(mPackageName, method, arg, extras);
484         } catch (DeadObjectException e) {
485             if (!mStable) {
486                 mContentResolver.unstableProviderDied(mContentProvider);
487             }
488             throw e;
489         } finally {
490             afterRemote();
491         }
492     }
493 
494     /**
495      * Closes this client connection, indicating to the system that the
496      * underlying {@link ContentProvider} is no longer needed.
497      */
498     @Override
close()499     public void close() {
500         closeInternal();
501     }
502 
503     /**
504      * @deprecated replaced by {@link #close()}.
505      */
506     @Deprecated
release()507     public boolean release() {
508         return closeInternal();
509     }
510 
closeInternal()511     private boolean closeInternal() {
512         mCloseGuard.close();
513         if (mClosed.compareAndSet(false, true)) {
514             if (mStable) {
515                 return mContentResolver.releaseProvider(mContentProvider);
516             } else {
517                 return mContentResolver.releaseUnstableProvider(mContentProvider);
518             }
519         } else {
520             return false;
521         }
522     }
523 
524     @Override
finalize()525     protected void finalize() throws Throwable {
526         try {
527             if (mCloseGuard != null) {
528                 mCloseGuard.warnIfOpen();
529             }
530 
531             close();
532         } finally {
533             super.finalize();
534         }
535     }
536 
537     /**
538      * Get a reference to the {@link ContentProvider} that is associated with this
539      * client. If the {@link ContentProvider} is running in a different process then
540      * null will be returned. This can be used if you know you are running in the same
541      * process as a provider, and want to get direct access to its implementation details.
542      *
543      * @return If the associated {@link ContentProvider} is local, returns it.
544      * Otherwise returns null.
545      */
getLocalContentProvider()546     public @Nullable ContentProvider getLocalContentProvider() {
547         return ContentProvider.coerceToLocalContentProvider(mContentProvider);
548     }
549 
550     /** {@hide} */
releaseQuietly(ContentProviderClient client)551     public static void releaseQuietly(ContentProviderClient client) {
552         if (client != null) {
553             try {
554                 client.release();
555             } catch (Exception ignored) {
556             }
557         }
558     }
559 
560     private class NotRespondingRunnable implements Runnable {
561         @Override
run()562         public void run() {
563             Log.w(TAG, "Detected provider not responding: " + mContentProvider);
564             mContentResolver.appNotRespondingViaProvider(mContentProvider);
565         }
566     }
567 
568     private final class CursorWrapperInner extends CrossProcessCursorWrapper {
569         private final CloseGuard mCloseGuard = CloseGuard.get();
570 
CursorWrapperInner(Cursor cursor)571         CursorWrapperInner(Cursor cursor) {
572             super(cursor);
573             mCloseGuard.open("close");
574         }
575 
576         @Override
close()577         public void close() {
578             mCloseGuard.close();
579             super.close();
580         }
581 
582         @Override
finalize()583         protected void finalize() throws Throwable {
584             try {
585                 if (mCloseGuard != null) {
586                     mCloseGuard.warnIfOpen();
587                 }
588 
589                 close();
590             } finally {
591                 super.finalize();
592             }
593         }
594     }
595 }
596