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