ContentProviderClient.java revision 13deeafe46ac8669b0d66fbc3c8a8f8fec023d67
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 17package android.content; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.content.res.AssetFileDescriptor; 22import android.database.CrossProcessCursorWrapper; 23import android.database.Cursor; 24import android.net.Uri; 25import android.os.Bundle; 26import android.os.CancellationSignal; 27import android.os.DeadObjectException; 28import android.os.Handler; 29import android.os.ICancellationSignal; 30import android.os.Looper; 31import android.os.ParcelFileDescriptor; 32import android.os.RemoteException; 33import android.util.Log; 34 35import com.android.internal.annotations.GuardedBy; 36import com.android.internal.annotations.VisibleForTesting; 37import com.android.internal.util.Preconditions; 38 39import dalvik.system.CloseGuard; 40 41import java.io.FileNotFoundException; 42import java.util.ArrayList; 43import 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 */ 63public 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 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} */ 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 111 private void beforeRemote() { 112 if (mAnrRunnable != null) { 113 sAnrHandler.postDelayed(mAnrRunnable, mAnrTimeout); 114 } 115 } 116 117 private void afterRemote() { 118 if (mAnrRunnable != null) { 119 sAnrHandler.removeCallbacks(mAnrRunnable); 120 } 121 } 122 123 /** See {@link ContentProvider#query ContentProvider.query} */ 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} */ 131 public @Nullable Cursor query(@NonNull Uri url, @Nullable String[] projection, 132 @Nullable String selection, @Nullable String[] selectionArgs, 133 @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) 134 throws RemoteException { 135 Preconditions.checkNotNull(url, "url"); 136 137 beforeRemote(); 138 try { 139 ICancellationSignal remoteCancellationSignal = null; 140 if (cancellationSignal != null) { 141 cancellationSignal.throwIfCanceled(); 142 remoteCancellationSignal = mContentProvider.createCancellationSignal(); 143 cancellationSignal.setRemote(remoteCancellationSignal); 144 } 145 final Cursor cursor = mContentProvider.query(mPackageName, url, projection, selection, 146 selectionArgs, sortOrder, remoteCancellationSignal); 147 if (cursor == null) { 148 return null; 149 } 150 return new CursorWrapperInner(cursor); 151 } catch (DeadObjectException e) { 152 if (!mStable) { 153 mContentResolver.unstableProviderDied(mContentProvider); 154 } 155 throw e; 156 } finally { 157 afterRemote(); 158 } 159 } 160 161 /** See {@link ContentProvider#getType ContentProvider.getType} */ 162 public @Nullable String getType(@NonNull Uri url) throws RemoteException { 163 Preconditions.checkNotNull(url, "url"); 164 165 beforeRemote(); 166 try { 167 return mContentProvider.getType(url); 168 } catch (DeadObjectException e) { 169 if (!mStable) { 170 mContentResolver.unstableProviderDied(mContentProvider); 171 } 172 throw e; 173 } finally { 174 afterRemote(); 175 } 176 } 177 178 /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */ 179 public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter) 180 throws RemoteException { 181 Preconditions.checkNotNull(url, "url"); 182 Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter"); 183 184 beforeRemote(); 185 try { 186 return mContentProvider.getStreamTypes(url, mimeTypeFilter); 187 } catch (DeadObjectException e) { 188 if (!mStable) { 189 mContentResolver.unstableProviderDied(mContentProvider); 190 } 191 throw e; 192 } finally { 193 afterRemote(); 194 } 195 } 196 197 /** See {@link ContentProvider#canonicalize} */ 198 public final @Nullable Uri canonicalize(@NonNull Uri url) throws RemoteException { 199 Preconditions.checkNotNull(url, "url"); 200 201 beforeRemote(); 202 try { 203 return mContentProvider.canonicalize(mPackageName, url); 204 } catch (DeadObjectException e) { 205 if (!mStable) { 206 mContentResolver.unstableProviderDied(mContentProvider); 207 } 208 throw e; 209 } finally { 210 afterRemote(); 211 } 212 } 213 214 /** See {@link ContentProvider#uncanonicalize} */ 215 public final @Nullable Uri uncanonicalize(@NonNull Uri url) throws RemoteException { 216 Preconditions.checkNotNull(url, "url"); 217 218 beforeRemote(); 219 try { 220 return mContentProvider.uncanonicalize(mPackageName, url); 221 } catch (DeadObjectException e) { 222 if (!mStable) { 223 mContentResolver.unstableProviderDied(mContentProvider); 224 } 225 throw e; 226 } finally { 227 afterRemote(); 228 } 229 } 230 231 /** @hide */ 232 public boolean refresh(Uri url, @Nullable Bundle args, 233 @Nullable CancellationSignal cancellationSignal) throws RemoteException { 234 Preconditions.checkNotNull(url, "url"); 235 236 beforeRemote(); 237 try { 238 ICancellationSignal remoteCancellationSignal = null; 239 if (cancellationSignal != null) { 240 cancellationSignal.throwIfCanceled(); 241 remoteCancellationSignal = mContentProvider.createCancellationSignal(); 242 cancellationSignal.setRemote(remoteCancellationSignal); 243 } 244 return mContentProvider.refresh(mPackageName, url, args, remoteCancellationSignal); 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#insert ContentProvider.insert} */ 256 public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues) 257 throws RemoteException { 258 Preconditions.checkNotNull(url, "url"); 259 260 beforeRemote(); 261 try { 262 return mContentProvider.insert(mPackageName, url, initialValues); 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#bulkInsert ContentProvider.bulkInsert} */ 274 public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] initialValues) 275 throws RemoteException { 276 Preconditions.checkNotNull(url, "url"); 277 Preconditions.checkNotNull(initialValues, "initialValues"); 278 279 beforeRemote(); 280 try { 281 return mContentProvider.bulkInsert(mPackageName, url, initialValues); 282 } catch (DeadObjectException e) { 283 if (!mStable) { 284 mContentResolver.unstableProviderDied(mContentProvider); 285 } 286 throw e; 287 } finally { 288 afterRemote(); 289 } 290 } 291 292 /** See {@link ContentProvider#delete ContentProvider.delete} */ 293 public int delete(@NonNull Uri url, @Nullable String selection, 294 @Nullable String[] selectionArgs) throws RemoteException { 295 Preconditions.checkNotNull(url, "url"); 296 297 beforeRemote(); 298 try { 299 return mContentProvider.delete(mPackageName, url, selection, selectionArgs); 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#update ContentProvider.update} */ 311 public int update(@NonNull Uri url, @Nullable ContentValues values, @Nullable String selection, 312 @Nullable String[] selectionArgs) throws RemoteException { 313 Preconditions.checkNotNull(url, "url"); 314 315 beforeRemote(); 316 try { 317 return mContentProvider.update(mPackageName, url, values, selection, selectionArgs); 318 } catch (DeadObjectException e) { 319 if (!mStable) { 320 mContentResolver.unstableProviderDied(mContentProvider); 321 } 322 throw e; 323 } finally { 324 afterRemote(); 325 } 326 } 327 328 /** 329 * See {@link ContentProvider#openFile ContentProvider.openFile}. Note that 330 * this <em>does not</em> 331 * take care of non-content: URIs such as file:. It is strongly recommended 332 * you use the {@link ContentResolver#openFileDescriptor 333 * ContentResolver.openFileDescriptor} API instead. 334 */ 335 public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode) 336 throws RemoteException, FileNotFoundException { 337 return openFile(url, mode, null); 338 } 339 340 /** 341 * See {@link ContentProvider#openFile ContentProvider.openFile}. Note that 342 * this <em>does not</em> 343 * take care of non-content: URIs such as file:. It is strongly recommended 344 * you use the {@link ContentResolver#openFileDescriptor 345 * ContentResolver.openFileDescriptor} API instead. 346 */ 347 public @Nullable ParcelFileDescriptor openFile(@NonNull Uri url, @NonNull String mode, 348 @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException { 349 Preconditions.checkNotNull(url, "url"); 350 Preconditions.checkNotNull(mode, "mode"); 351 352 beforeRemote(); 353 try { 354 ICancellationSignal remoteSignal = null; 355 if (signal != null) { 356 signal.throwIfCanceled(); 357 remoteSignal = mContentProvider.createCancellationSignal(); 358 signal.setRemote(remoteSignal); 359 } 360 return mContentProvider.openFile(mPackageName, url, mode, remoteSignal, null); 361 } catch (DeadObjectException e) { 362 if (!mStable) { 363 mContentResolver.unstableProviderDied(mContentProvider); 364 } 365 throw e; 366 } finally { 367 afterRemote(); 368 } 369 } 370 371 /** 372 * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}. 373 * Note that this <em>does not</em> 374 * take care of non-content: URIs such as file:. It is strongly recommended 375 * you use the {@link ContentResolver#openAssetFileDescriptor 376 * ContentResolver.openAssetFileDescriptor} API instead. 377 */ 378 public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode) 379 throws RemoteException, FileNotFoundException { 380 return openAssetFile(url, mode, null); 381 } 382 383 /** 384 * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}. 385 * Note that this <em>does not</em> 386 * take care of non-content: URIs such as file:. It is strongly recommended 387 * you use the {@link ContentResolver#openAssetFileDescriptor 388 * ContentResolver.openAssetFileDescriptor} API instead. 389 */ 390 public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri url, @NonNull String mode, 391 @Nullable CancellationSignal signal) throws RemoteException, FileNotFoundException { 392 Preconditions.checkNotNull(url, "url"); 393 Preconditions.checkNotNull(mode, "mode"); 394 395 beforeRemote(); 396 try { 397 ICancellationSignal remoteSignal = null; 398 if (signal != null) { 399 signal.throwIfCanceled(); 400 remoteSignal = mContentProvider.createCancellationSignal(); 401 signal.setRemote(remoteSignal); 402 } 403 return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal); 404 } catch (DeadObjectException e) { 405 if (!mStable) { 406 mContentResolver.unstableProviderDied(mContentProvider); 407 } 408 throw e; 409 } finally { 410 afterRemote(); 411 } 412 } 413 414 /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */ 415 public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri, 416 @NonNull String mimeType, @Nullable Bundle opts) 417 throws RemoteException, FileNotFoundException { 418 return openTypedAssetFileDescriptor(uri, mimeType, opts, null); 419 } 420 421 /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */ 422 public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri, 423 @NonNull String mimeType, @Nullable Bundle opts, @Nullable CancellationSignal signal) 424 throws RemoteException, FileNotFoundException { 425 Preconditions.checkNotNull(uri, "uri"); 426 Preconditions.checkNotNull(mimeType, "mimeType"); 427 428 beforeRemote(); 429 try { 430 ICancellationSignal remoteSignal = null; 431 if (signal != null) { 432 signal.throwIfCanceled(); 433 remoteSignal = mContentProvider.createCancellationSignal(); 434 signal.setRemote(remoteSignal); 435 } 436 return mContentProvider.openTypedAssetFile( 437 mPackageName, uri, mimeType, opts, remoteSignal); 438 } catch (DeadObjectException e) { 439 if (!mStable) { 440 mContentResolver.unstableProviderDied(mContentProvider); 441 } 442 throw e; 443 } finally { 444 afterRemote(); 445 } 446 } 447 448 /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */ 449 public @NonNull ContentProviderResult[] applyBatch( 450 @NonNull ArrayList<ContentProviderOperation> operations) 451 throws RemoteException, OperationApplicationException { 452 Preconditions.checkNotNull(operations, "operations"); 453 454 beforeRemote(); 455 try { 456 return mContentProvider.applyBatch(mPackageName, operations); 457 } catch (DeadObjectException e) { 458 if (!mStable) { 459 mContentResolver.unstableProviderDied(mContentProvider); 460 } 461 throw e; 462 } finally { 463 afterRemote(); 464 } 465 } 466 467 /** See {@link ContentProvider#call(String, String, Bundle)} */ 468 public @Nullable Bundle call(@NonNull String method, @Nullable String arg, 469 @Nullable Bundle extras) throws RemoteException { 470 Preconditions.checkNotNull(method, "method"); 471 472 beforeRemote(); 473 try { 474 return mContentProvider.call(mPackageName, method, arg, extras); 475 } catch (DeadObjectException e) { 476 if (!mStable) { 477 mContentResolver.unstableProviderDied(mContentProvider); 478 } 479 throw e; 480 } finally { 481 afterRemote(); 482 } 483 } 484 485 /** 486 * Closes this client connection, indicating to the system that the 487 * underlying {@link ContentProvider} is no longer needed. 488 */ 489 @Override 490 public void close() { 491 closeInternal(); 492 } 493 494 /** 495 * @deprecated replaced by {@link #close()}. 496 */ 497 @Deprecated 498 public boolean release() { 499 return closeInternal(); 500 } 501 502 private boolean closeInternal() { 503 mCloseGuard.close(); 504 if (mClosed.compareAndSet(false, true)) { 505 if (mStable) { 506 return mContentResolver.releaseProvider(mContentProvider); 507 } else { 508 return mContentResolver.releaseUnstableProvider(mContentProvider); 509 } 510 } else { 511 return false; 512 } 513 } 514 515 @Override 516 protected void finalize() throws Throwable { 517 try { 518 mCloseGuard.warnIfOpen(); 519 close(); 520 } finally { 521 super.finalize(); 522 } 523 } 524 525 /** 526 * Get a reference to the {@link ContentProvider} that is associated with this 527 * client. If the {@link ContentProvider} is running in a different process then 528 * null will be returned. This can be used if you know you are running in the same 529 * process as a provider, and want to get direct access to its implementation details. 530 * 531 * @return If the associated {@link ContentProvider} is local, returns it. 532 * Otherwise returns null. 533 */ 534 public @Nullable ContentProvider getLocalContentProvider() { 535 return ContentProvider.coerceToLocalContentProvider(mContentProvider); 536 } 537 538 /** {@hide} */ 539 public static void releaseQuietly(ContentProviderClient client) { 540 if (client != null) { 541 try { 542 client.release(); 543 } catch (Exception ignored) { 544 } 545 } 546 } 547 548 private class NotRespondingRunnable implements Runnable { 549 @Override 550 public void run() { 551 Log.w(TAG, "Detected provider not responding: " + mContentProvider); 552 mContentResolver.appNotRespondingViaProvider(mContentProvider); 553 } 554 } 555 556 private final class CursorWrapperInner extends CrossProcessCursorWrapper { 557 private final CloseGuard mCloseGuard = CloseGuard.get(); 558 559 CursorWrapperInner(Cursor cursor) { 560 super(cursor); 561 mCloseGuard.open("close"); 562 } 563 564 @Override 565 public void close() { 566 mCloseGuard.close(); 567 super.close(); 568 } 569 570 @Override 571 protected void finalize() throws Throwable { 572 try { 573 mCloseGuard.warnIfOpen(); 574 close(); 575 } finally { 576 super.finalize(); 577 } 578 } 579 } 580} 581