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 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 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} */ 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} */ 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} */ 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} */ 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} */ 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} */ 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} */ 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} */ 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} */ 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} */ 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 */ 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 */ 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 */ 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 */ 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} */ 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} */ 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} */ 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)} */ 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 499 public void close() { 500 closeInternal(); 501 } 502 503 /** 504 * @deprecated replaced by {@link #close()}. 505 */ 506 @Deprecated 507 public boolean release() { 508 return closeInternal(); 509 } 510 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 525 protected void finalize() throws Throwable { 526 try { 527 mCloseGuard.warnIfOpen(); 528 close(); 529 } finally { 530 super.finalize(); 531 } 532 } 533 534 /** 535 * Get a reference to the {@link ContentProvider} that is associated with this 536 * client. If the {@link ContentProvider} is running in a different process then 537 * null will be returned. This can be used if you know you are running in the same 538 * process as a provider, and want to get direct access to its implementation details. 539 * 540 * @return If the associated {@link ContentProvider} is local, returns it. 541 * Otherwise returns null. 542 */ 543 public @Nullable ContentProvider getLocalContentProvider() { 544 return ContentProvider.coerceToLocalContentProvider(mContentProvider); 545 } 546 547 /** {@hide} */ 548 public static void releaseQuietly(ContentProviderClient client) { 549 if (client != null) { 550 try { 551 client.release(); 552 } catch (Exception ignored) { 553 } 554 } 555 } 556 557 private class NotRespondingRunnable implements Runnable { 558 @Override 559 public void run() { 560 Log.w(TAG, "Detected provider not responding: " + mContentProvider); 561 mContentResolver.appNotRespondingViaProvider(mContentProvider); 562 } 563 } 564 565 private final class CursorWrapperInner extends CrossProcessCursorWrapper { 566 private final CloseGuard mCloseGuard = CloseGuard.get(); 567 568 CursorWrapperInner(Cursor cursor) { 569 super(cursor); 570 mCloseGuard.open("close"); 571 } 572 573 @Override 574 public void close() { 575 mCloseGuard.close(); 576 super.close(); 577 } 578 579 @Override 580 protected void finalize() throws Throwable { 581 try { 582 mCloseGuard.warnIfOpen(); 583 close(); 584 } finally { 585 super.finalize(); 586 } 587 } 588 } 589 } 590