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