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