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