1 /* 2 * Copyright (C) 2006 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 static android.Manifest.permission.INTERACT_ACROSS_USERS; 20 import static android.app.AppOpsManager.MODE_ALLOWED; 21 import static android.app.AppOpsManager.MODE_ERRORED; 22 import static android.app.AppOpsManager.MODE_IGNORED; 23 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 24 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.app.AppOpsManager; 28 import android.content.pm.PathPermission; 29 import android.content.pm.ProviderInfo; 30 import android.content.res.AssetFileDescriptor; 31 import android.content.res.Configuration; 32 import android.database.Cursor; 33 import android.database.MatrixCursor; 34 import android.database.SQLException; 35 import android.net.Uri; 36 import android.os.AsyncTask; 37 import android.os.Binder; 38 import android.os.Bundle; 39 import android.os.CancellationSignal; 40 import android.os.IBinder; 41 import android.os.ICancellationSignal; 42 import android.os.OperationCanceledException; 43 import android.os.ParcelFileDescriptor; 44 import android.os.Process; 45 import android.os.UserHandle; 46 import android.text.TextUtils; 47 import android.util.Log; 48 49 import java.io.File; 50 import java.io.FileDescriptor; 51 import java.io.FileNotFoundException; 52 import java.io.IOException; 53 import java.io.PrintWriter; 54 import java.util.ArrayList; 55 import java.util.Arrays; 56 57 /** 58 * Content providers are one of the primary building blocks of Android applications, providing 59 * content to applications. They encapsulate data and provide it to applications through the single 60 * {@link ContentResolver} interface. A content provider is only required if you need to share 61 * data between multiple applications. For example, the contacts data is used by multiple 62 * applications and must be stored in a content provider. If you don't need to share data amongst 63 * multiple applications you can use a database directly via 64 * {@link android.database.sqlite.SQLiteDatabase}. 65 * 66 * <p>When a request is made via 67 * a {@link ContentResolver} the system inspects the authority of the given URI and passes the 68 * request to the content provider registered with the authority. The content provider can interpret 69 * the rest of the URI however it wants. The {@link UriMatcher} class is helpful for parsing 70 * URIs.</p> 71 * 72 * <p>The primary methods that need to be implemented are: 73 * <ul> 74 * <li>{@link #onCreate} which is called to initialize the provider</li> 75 * <li>{@link #query} which returns data to the caller</li> 76 * <li>{@link #insert} which inserts new data into the content provider</li> 77 * <li>{@link #update} which updates existing data in the content provider</li> 78 * <li>{@link #delete} which deletes data from the content provider</li> 79 * <li>{@link #getType} which returns the MIME type of data in the content provider</li> 80 * </ul></p> 81 * 82 * <p class="caution">Data access methods (such as {@link #insert} and 83 * {@link #update}) may be called from many threads at once, and must be thread-safe. 84 * Other methods (such as {@link #onCreate}) are only called from the application 85 * main thread, and must avoid performing lengthy operations. See the method 86 * descriptions for their expected thread behavior.</p> 87 * 88 * <p>Requests to {@link ContentResolver} are automatically forwarded to the appropriate 89 * ContentProvider instance, so subclasses don't have to worry about the details of 90 * cross-process calls.</p> 91 * 92 * <div class="special reference"> 93 * <h3>Developer Guides</h3> 94 * <p>For more information about using content providers, read the 95 * <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a> 96 * developer guide.</p> 97 */ 98 public abstract class ContentProvider implements ComponentCallbacks2 { 99 private static final String TAG = "ContentProvider"; 100 101 /* 102 * Note: if you add methods to ContentProvider, you must add similar methods to 103 * MockContentProvider. 104 */ 105 106 private Context mContext = null; 107 private int mMyUid; 108 109 // Since most Providers have only one authority, we keep both a String and a String[] to improve 110 // performance. 111 private String mAuthority; 112 private String[] mAuthorities; 113 private String mReadPermission; 114 private String mWritePermission; 115 private PathPermission[] mPathPermissions; 116 private boolean mExported; 117 private boolean mNoPerms; 118 private boolean mSingleUser; 119 120 private final ThreadLocal<String> mCallingPackage = new ThreadLocal<String>(); 121 122 private Transport mTransport = new Transport(); 123 124 /** 125 * Construct a ContentProvider instance. Content providers must be 126 * <a href="{@docRoot}guide/topics/manifest/provider-element.html">declared 127 * in the manifest</a>, accessed with {@link ContentResolver}, and created 128 * automatically by the system, so applications usually do not create 129 * ContentProvider instances directly. 130 * 131 * <p>At construction time, the object is uninitialized, and most fields and 132 * methods are unavailable. Subclasses should initialize themselves in 133 * {@link #onCreate}, not the constructor. 134 * 135 * <p>Content providers are created on the application main thread at 136 * application launch time. The constructor must not perform lengthy 137 * operations, or application startup will be delayed. 138 */ ContentProvider()139 public ContentProvider() { 140 } 141 142 /** 143 * Constructor just for mocking. 144 * 145 * @param context A Context object which should be some mock instance (like the 146 * instance of {@link android.test.mock.MockContext}). 147 * @param readPermission The read permision you want this instance should have in the 148 * test, which is available via {@link #getReadPermission()}. 149 * @param writePermission The write permission you want this instance should have 150 * in the test, which is available via {@link #getWritePermission()}. 151 * @param pathPermissions The PathPermissions you want this instance should have 152 * in the test, which is available via {@link #getPathPermissions()}. 153 * @hide 154 */ ContentProvider( Context context, String readPermission, String writePermission, PathPermission[] pathPermissions)155 public ContentProvider( 156 Context context, 157 String readPermission, 158 String writePermission, 159 PathPermission[] pathPermissions) { 160 mContext = context; 161 mReadPermission = readPermission; 162 mWritePermission = writePermission; 163 mPathPermissions = pathPermissions; 164 } 165 166 /** 167 * Given an IContentProvider, try to coerce it back to the real 168 * ContentProvider object if it is running in the local process. This can 169 * be used if you know you are running in the same process as a provider, 170 * and want to get direct access to its implementation details. Most 171 * clients should not nor have a reason to use it. 172 * 173 * @param abstractInterface The ContentProvider interface that is to be 174 * coerced. 175 * @return If the IContentProvider is non-{@code null} and local, returns its actual 176 * ContentProvider instance. Otherwise returns {@code null}. 177 * @hide 178 */ coerceToLocalContentProvider( IContentProvider abstractInterface)179 public static ContentProvider coerceToLocalContentProvider( 180 IContentProvider abstractInterface) { 181 if (abstractInterface instanceof Transport) { 182 return ((Transport)abstractInterface).getContentProvider(); 183 } 184 return null; 185 } 186 187 /** 188 * Binder object that deals with remoting. 189 * 190 * @hide 191 */ 192 class Transport extends ContentProviderNative { 193 AppOpsManager mAppOpsManager = null; 194 int mReadOp = AppOpsManager.OP_NONE; 195 int mWriteOp = AppOpsManager.OP_NONE; 196 getContentProvider()197 ContentProvider getContentProvider() { 198 return ContentProvider.this; 199 } 200 201 @Override getProviderName()202 public String getProviderName() { 203 return getContentProvider().getClass().getName(); 204 } 205 206 @Override query(String callingPkg, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)207 public Cursor query(String callingPkg, Uri uri, String[] projection, 208 String selection, String[] selectionArgs, String sortOrder, 209 ICancellationSignal cancellationSignal) { 210 validateIncomingUri(uri); 211 uri = getUriWithoutUserId(uri); 212 if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { 213 // The caller has no access to the data, so return an empty cursor with 214 // the columns in the requested order. The caller may ask for an invalid 215 // column and we would not catch that but this is not a problem in practice. 216 // We do not call ContentProvider#query with a modified where clause since 217 // the implementation is not guaranteed to be backed by a SQL database, hence 218 // it may not handle properly the tautology where clause we would have created. 219 if (projection != null) { 220 return new MatrixCursor(projection, 0); 221 } 222 223 // Null projection means all columns but we have no idea which they are. 224 // However, the caller may be expecting to access them my index. Hence, 225 // we have to execute the query as if allowed to get a cursor with the 226 // columns. We then use the column names to return an empty cursor. 227 Cursor cursor = ContentProvider.this.query(uri, projection, selection, 228 selectionArgs, sortOrder, CancellationSignal.fromTransport( 229 cancellationSignal)); 230 if (cursor == null) { 231 return null; 232 } 233 234 // Return an empty cursor for all columns. 235 return new MatrixCursor(cursor.getColumnNames(), 0); 236 } 237 final String original = setCallingPackage(callingPkg); 238 try { 239 return ContentProvider.this.query( 240 uri, projection, selection, selectionArgs, sortOrder, 241 CancellationSignal.fromTransport(cancellationSignal)); 242 } finally { 243 setCallingPackage(original); 244 } 245 } 246 247 @Override getType(Uri uri)248 public String getType(Uri uri) { 249 validateIncomingUri(uri); 250 uri = getUriWithoutUserId(uri); 251 return ContentProvider.this.getType(uri); 252 } 253 254 @Override insert(String callingPkg, Uri uri, ContentValues initialValues)255 public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) { 256 validateIncomingUri(uri); 257 int userId = getUserIdFromUri(uri); 258 uri = getUriWithoutUserId(uri); 259 if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { 260 return rejectInsert(uri, initialValues); 261 } 262 final String original = setCallingPackage(callingPkg); 263 try { 264 return maybeAddUserId(ContentProvider.this.insert(uri, initialValues), userId); 265 } finally { 266 setCallingPackage(original); 267 } 268 } 269 270 @Override bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues)271 public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) { 272 validateIncomingUri(uri); 273 uri = getUriWithoutUserId(uri); 274 if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { 275 return 0; 276 } 277 final String original = setCallingPackage(callingPkg); 278 try { 279 return ContentProvider.this.bulkInsert(uri, initialValues); 280 } finally { 281 setCallingPackage(original); 282 } 283 } 284 285 @Override applyBatch(String callingPkg, ArrayList<ContentProviderOperation> operations)286 public ContentProviderResult[] applyBatch(String callingPkg, 287 ArrayList<ContentProviderOperation> operations) 288 throws OperationApplicationException { 289 int numOperations = operations.size(); 290 final int[] userIds = new int[numOperations]; 291 for (int i = 0; i < numOperations; i++) { 292 ContentProviderOperation operation = operations.get(i); 293 Uri uri = operation.getUri(); 294 validateIncomingUri(uri); 295 userIds[i] = getUserIdFromUri(uri); 296 if (userIds[i] != UserHandle.USER_CURRENT) { 297 // Removing the user id from the uri. 298 operation = new ContentProviderOperation(operation, true); 299 operations.set(i, operation); 300 } 301 if (operation.isReadOperation()) { 302 if (enforceReadPermission(callingPkg, uri, null) 303 != AppOpsManager.MODE_ALLOWED) { 304 throw new OperationApplicationException("App op not allowed", 0); 305 } 306 } 307 if (operation.isWriteOperation()) { 308 if (enforceWritePermission(callingPkg, uri, null) 309 != AppOpsManager.MODE_ALLOWED) { 310 throw new OperationApplicationException("App op not allowed", 0); 311 } 312 } 313 } 314 final String original = setCallingPackage(callingPkg); 315 try { 316 ContentProviderResult[] results = ContentProvider.this.applyBatch(operations); 317 if (results != null) { 318 for (int i = 0; i < results.length ; i++) { 319 if (userIds[i] != UserHandle.USER_CURRENT) { 320 // Adding the userId to the uri. 321 results[i] = new ContentProviderResult(results[i], userIds[i]); 322 } 323 } 324 } 325 return results; 326 } finally { 327 setCallingPackage(original); 328 } 329 } 330 331 @Override delete(String callingPkg, Uri uri, String selection, String[] selectionArgs)332 public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) { 333 validateIncomingUri(uri); 334 uri = getUriWithoutUserId(uri); 335 if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { 336 return 0; 337 } 338 final String original = setCallingPackage(callingPkg); 339 try { 340 return ContentProvider.this.delete(uri, selection, selectionArgs); 341 } finally { 342 setCallingPackage(original); 343 } 344 } 345 346 @Override update(String callingPkg, Uri uri, ContentValues values, String selection, String[] selectionArgs)347 public int update(String callingPkg, Uri uri, ContentValues values, String selection, 348 String[] selectionArgs) { 349 validateIncomingUri(uri); 350 uri = getUriWithoutUserId(uri); 351 if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { 352 return 0; 353 } 354 final String original = setCallingPackage(callingPkg); 355 try { 356 return ContentProvider.this.update(uri, values, selection, selectionArgs); 357 } finally { 358 setCallingPackage(original); 359 } 360 } 361 362 @Override openFile( String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal, IBinder callerToken)363 public ParcelFileDescriptor openFile( 364 String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal, 365 IBinder callerToken) throws FileNotFoundException { 366 validateIncomingUri(uri); 367 uri = getUriWithoutUserId(uri); 368 enforceFilePermission(callingPkg, uri, mode, callerToken); 369 final String original = setCallingPackage(callingPkg); 370 try { 371 return ContentProvider.this.openFile( 372 uri, mode, CancellationSignal.fromTransport(cancellationSignal)); 373 } finally { 374 setCallingPackage(original); 375 } 376 } 377 378 @Override openAssetFile( String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)379 public AssetFileDescriptor openAssetFile( 380 String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal) 381 throws FileNotFoundException { 382 validateIncomingUri(uri); 383 uri = getUriWithoutUserId(uri); 384 enforceFilePermission(callingPkg, uri, mode, null); 385 final String original = setCallingPackage(callingPkg); 386 try { 387 return ContentProvider.this.openAssetFile( 388 uri, mode, CancellationSignal.fromTransport(cancellationSignal)); 389 } finally { 390 setCallingPackage(original); 391 } 392 } 393 394 @Override call( String callingPkg, String method, @Nullable String arg, @Nullable Bundle extras)395 public Bundle call( 396 String callingPkg, String method, @Nullable String arg, @Nullable Bundle extras) { 397 Bundle.setDefusable(extras, true); 398 final String original = setCallingPackage(callingPkg); 399 try { 400 return ContentProvider.this.call(method, arg, extras); 401 } finally { 402 setCallingPackage(original); 403 } 404 } 405 406 @Override getStreamTypes(Uri uri, String mimeTypeFilter)407 public String[] getStreamTypes(Uri uri, String mimeTypeFilter) { 408 validateIncomingUri(uri); 409 uri = getUriWithoutUserId(uri); 410 return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter); 411 } 412 413 @Override openTypedAssetFile(String callingPkg, Uri uri, String mimeType, Bundle opts, ICancellationSignal cancellationSignal)414 public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType, 415 Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException { 416 Bundle.setDefusable(opts, true); 417 validateIncomingUri(uri); 418 uri = getUriWithoutUserId(uri); 419 enforceFilePermission(callingPkg, uri, "r", null); 420 final String original = setCallingPackage(callingPkg); 421 try { 422 return ContentProvider.this.openTypedAssetFile( 423 uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal)); 424 } finally { 425 setCallingPackage(original); 426 } 427 } 428 429 @Override createCancellationSignal()430 public ICancellationSignal createCancellationSignal() { 431 return CancellationSignal.createTransport(); 432 } 433 434 @Override canonicalize(String callingPkg, Uri uri)435 public Uri canonicalize(String callingPkg, Uri uri) { 436 validateIncomingUri(uri); 437 int userId = getUserIdFromUri(uri); 438 uri = getUriWithoutUserId(uri); 439 if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { 440 return null; 441 } 442 final String original = setCallingPackage(callingPkg); 443 try { 444 return maybeAddUserId(ContentProvider.this.canonicalize(uri), userId); 445 } finally { 446 setCallingPackage(original); 447 } 448 } 449 450 @Override uncanonicalize(String callingPkg, Uri uri)451 public Uri uncanonicalize(String callingPkg, Uri uri) { 452 validateIncomingUri(uri); 453 int userId = getUserIdFromUri(uri); 454 uri = getUriWithoutUserId(uri); 455 if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) { 456 return null; 457 } 458 final String original = setCallingPackage(callingPkg); 459 try { 460 return maybeAddUserId(ContentProvider.this.uncanonicalize(uri), userId); 461 } finally { 462 setCallingPackage(original); 463 } 464 } 465 enforceFilePermission(String callingPkg, Uri uri, String mode, IBinder callerToken)466 private void enforceFilePermission(String callingPkg, Uri uri, String mode, 467 IBinder callerToken) throws FileNotFoundException, SecurityException { 468 if (mode != null && mode.indexOf('w') != -1) { 469 if (enforceWritePermission(callingPkg, uri, callerToken) 470 != AppOpsManager.MODE_ALLOWED) { 471 throw new FileNotFoundException("App op not allowed"); 472 } 473 } else { 474 if (enforceReadPermission(callingPkg, uri, callerToken) 475 != AppOpsManager.MODE_ALLOWED) { 476 throw new FileNotFoundException("App op not allowed"); 477 } 478 } 479 } 480 enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken)481 private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken) 482 throws SecurityException { 483 final int mode = enforceReadPermissionInner(uri, callingPkg, callerToken); 484 if (mode != MODE_ALLOWED) { 485 return mode; 486 } 487 488 if (mReadOp != AppOpsManager.OP_NONE) { 489 return mAppOpsManager.noteProxyOp(mReadOp, callingPkg); 490 } 491 492 return AppOpsManager.MODE_ALLOWED; 493 } 494 enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken)495 private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken) 496 throws SecurityException { 497 final int mode = enforceWritePermissionInner(uri, callingPkg, callerToken); 498 if (mode != MODE_ALLOWED) { 499 return mode; 500 } 501 502 if (mWriteOp != AppOpsManager.OP_NONE) { 503 return mAppOpsManager.noteProxyOp(mWriteOp, callingPkg); 504 } 505 506 return AppOpsManager.MODE_ALLOWED; 507 } 508 } 509 checkUser(int pid, int uid, Context context)510 boolean checkUser(int pid, int uid, Context context) { 511 return UserHandle.getUserId(uid) == context.getUserId() 512 || mSingleUser 513 || context.checkPermission(INTERACT_ACROSS_USERS, pid, uid) 514 == PERMISSION_GRANTED; 515 } 516 517 /** 518 * Verify that calling app holds both the given permission and any app-op 519 * associated with that permission. 520 */ checkPermissionAndAppOp(String permission, String callingPkg, IBinder callerToken)521 private int checkPermissionAndAppOp(String permission, String callingPkg, 522 IBinder callerToken) { 523 if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(), 524 callerToken) != PERMISSION_GRANTED) { 525 return MODE_ERRORED; 526 } 527 528 final int permOp = AppOpsManager.permissionToOpCode(permission); 529 if (permOp != AppOpsManager.OP_NONE) { 530 return mTransport.mAppOpsManager.noteProxyOp(permOp, callingPkg); 531 } 532 533 return MODE_ALLOWED; 534 } 535 536 /** {@hide} */ enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)537 protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken) 538 throws SecurityException { 539 final Context context = getContext(); 540 final int pid = Binder.getCallingPid(); 541 final int uid = Binder.getCallingUid(); 542 String missingPerm = null; 543 int strongestMode = MODE_ALLOWED; 544 545 if (UserHandle.isSameApp(uid, mMyUid)) { 546 return MODE_ALLOWED; 547 } 548 549 if (mExported && checkUser(pid, uid, context)) { 550 final String componentPerm = getReadPermission(); 551 if (componentPerm != null) { 552 final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken); 553 if (mode == MODE_ALLOWED) { 554 return MODE_ALLOWED; 555 } else { 556 missingPerm = componentPerm; 557 strongestMode = Math.max(strongestMode, mode); 558 } 559 } 560 561 // track if unprotected read is allowed; any denied 562 // <path-permission> below removes this ability 563 boolean allowDefaultRead = (componentPerm == null); 564 565 final PathPermission[] pps = getPathPermissions(); 566 if (pps != null) { 567 final String path = uri.getPath(); 568 for (PathPermission pp : pps) { 569 final String pathPerm = pp.getReadPermission(); 570 if (pathPerm != null && pp.match(path)) { 571 final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken); 572 if (mode == MODE_ALLOWED) { 573 return MODE_ALLOWED; 574 } else { 575 // any denied <path-permission> means we lose 576 // default <provider> access. 577 allowDefaultRead = false; 578 missingPerm = pathPerm; 579 strongestMode = Math.max(strongestMode, mode); 580 } 581 } 582 } 583 } 584 585 // if we passed <path-permission> checks above, and no default 586 // <provider> permission, then allow access. 587 if (allowDefaultRead) return MODE_ALLOWED; 588 } 589 590 // last chance, check against any uri grants 591 final int callingUserId = UserHandle.getUserId(uid); 592 final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid)) 593 ? maybeAddUserId(uri, callingUserId) : uri; 594 if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION, 595 callerToken) == PERMISSION_GRANTED) { 596 return MODE_ALLOWED; 597 } 598 599 // If the worst denial we found above was ignored, then pass that 600 // ignored through; otherwise we assume it should be a real error below. 601 if (strongestMode == MODE_IGNORED) { 602 return MODE_IGNORED; 603 } 604 605 final String failReason = mExported 606 ? " requires " + missingPerm + ", or grantUriPermission()" 607 : " requires the provider be exported, or grantUriPermission()"; 608 throw new SecurityException("Permission Denial: reading " 609 + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid 610 + ", uid=" + uid + failReason); 611 } 612 613 /** {@hide} */ enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)614 protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken) 615 throws SecurityException { 616 final Context context = getContext(); 617 final int pid = Binder.getCallingPid(); 618 final int uid = Binder.getCallingUid(); 619 String missingPerm = null; 620 int strongestMode = MODE_ALLOWED; 621 622 if (UserHandle.isSameApp(uid, mMyUid)) { 623 return MODE_ALLOWED; 624 } 625 626 if (mExported && checkUser(pid, uid, context)) { 627 final String componentPerm = getWritePermission(); 628 if (componentPerm != null) { 629 final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken); 630 if (mode == MODE_ALLOWED) { 631 return MODE_ALLOWED; 632 } else { 633 missingPerm = componentPerm; 634 strongestMode = Math.max(strongestMode, mode); 635 } 636 } 637 638 // track if unprotected write is allowed; any denied 639 // <path-permission> below removes this ability 640 boolean allowDefaultWrite = (componentPerm == null); 641 642 final PathPermission[] pps = getPathPermissions(); 643 if (pps != null) { 644 final String path = uri.getPath(); 645 for (PathPermission pp : pps) { 646 final String pathPerm = pp.getWritePermission(); 647 if (pathPerm != null && pp.match(path)) { 648 final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken); 649 if (mode == MODE_ALLOWED) { 650 return MODE_ALLOWED; 651 } else { 652 // any denied <path-permission> means we lose 653 // default <provider> access. 654 allowDefaultWrite = false; 655 missingPerm = pathPerm; 656 strongestMode = Math.max(strongestMode, mode); 657 } 658 } 659 } 660 } 661 662 // if we passed <path-permission> checks above, and no default 663 // <provider> permission, then allow access. 664 if (allowDefaultWrite) return MODE_ALLOWED; 665 } 666 667 // last chance, check against any uri grants 668 if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 669 callerToken) == PERMISSION_GRANTED) { 670 return MODE_ALLOWED; 671 } 672 673 // If the worst denial we found above was ignored, then pass that 674 // ignored through; otherwise we assume it should be a real error below. 675 if (strongestMode == MODE_IGNORED) { 676 return MODE_IGNORED; 677 } 678 679 final String failReason = mExported 680 ? " requires " + missingPerm + ", or grantUriPermission()" 681 : " requires the provider be exported, or grantUriPermission()"; 682 throw new SecurityException("Permission Denial: writing " 683 + ContentProvider.this.getClass().getName() + " uri " + uri + " from pid=" + pid 684 + ", uid=" + uid + failReason); 685 } 686 687 /** 688 * Retrieves the Context this provider is running in. Only available once 689 * {@link #onCreate} has been called -- this will return {@code null} in the 690 * constructor. 691 */ getContext()692 public final @Nullable Context getContext() { 693 return mContext; 694 } 695 696 /** 697 * Set the calling package, returning the current value (or {@code null}) 698 * which can be used later to restore the previous state. 699 */ setCallingPackage(String callingPackage)700 private String setCallingPackage(String callingPackage) { 701 final String original = mCallingPackage.get(); 702 mCallingPackage.set(callingPackage); 703 return original; 704 } 705 706 /** 707 * Return the package name of the caller that initiated the request being 708 * processed on the current thread. The returned package will have been 709 * verified to belong to the calling UID. Returns {@code null} if not 710 * currently processing a request. 711 * <p> 712 * This will always return {@code null} when processing 713 * {@link #getType(Uri)} or {@link #getStreamTypes(Uri, String)} requests. 714 * 715 * @see Binder#getCallingUid() 716 * @see Context#grantUriPermission(String, Uri, int) 717 * @throws SecurityException if the calling package doesn't belong to the 718 * calling UID. 719 */ getCallingPackage()720 public final @Nullable String getCallingPackage() { 721 final String pkg = mCallingPackage.get(); 722 if (pkg != null) { 723 mTransport.mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg); 724 } 725 return pkg; 726 } 727 728 /** 729 * Change the authorities of the ContentProvider. 730 * This is normally set for you from its manifest information when the provider is first 731 * created. 732 * @hide 733 * @param authorities the semi-colon separated authorities of the ContentProvider. 734 */ setAuthorities(String authorities)735 protected final void setAuthorities(String authorities) { 736 if (authorities != null) { 737 if (authorities.indexOf(';') == -1) { 738 mAuthority = authorities; 739 mAuthorities = null; 740 } else { 741 mAuthority = null; 742 mAuthorities = authorities.split(";"); 743 } 744 } 745 } 746 747 /** @hide */ matchesOurAuthorities(String authority)748 protected final boolean matchesOurAuthorities(String authority) { 749 if (mAuthority != null) { 750 return mAuthority.equals(authority); 751 } 752 if (mAuthorities != null) { 753 int length = mAuthorities.length; 754 for (int i = 0; i < length; i++) { 755 if (mAuthorities[i].equals(authority)) return true; 756 } 757 } 758 return false; 759 } 760 761 762 /** 763 * Change the permission required to read data from the content 764 * provider. This is normally set for you from its manifest information 765 * when the provider is first created. 766 * 767 * @param permission Name of the permission required for read-only access. 768 */ setReadPermission(@ullable String permission)769 protected final void setReadPermission(@Nullable String permission) { 770 mReadPermission = permission; 771 } 772 773 /** 774 * Return the name of the permission required for read-only access to 775 * this content provider. This method can be called from multiple 776 * threads, as described in 777 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 778 * and Threads</a>. 779 */ getReadPermission()780 public final @Nullable String getReadPermission() { 781 return mReadPermission; 782 } 783 784 /** 785 * Change the permission required to read and write data in the content 786 * provider. This is normally set for you from its manifest information 787 * when the provider is first created. 788 * 789 * @param permission Name of the permission required for read/write access. 790 */ setWritePermission(@ullable String permission)791 protected final void setWritePermission(@Nullable String permission) { 792 mWritePermission = permission; 793 } 794 795 /** 796 * Return the name of the permission required for read/write access to 797 * this content provider. This method can be called from multiple 798 * threads, as described in 799 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 800 * and Threads</a>. 801 */ getWritePermission()802 public final @Nullable String getWritePermission() { 803 return mWritePermission; 804 } 805 806 /** 807 * Change the path-based permission required to read and/or write data in 808 * the content provider. This is normally set for you from its manifest 809 * information when the provider is first created. 810 * 811 * @param permissions Array of path permission descriptions. 812 */ setPathPermissions(@ullable PathPermission[] permissions)813 protected final void setPathPermissions(@Nullable PathPermission[] permissions) { 814 mPathPermissions = permissions; 815 } 816 817 /** 818 * Return the path-based permissions required for read and/or write access to 819 * this content provider. This method can be called from multiple 820 * threads, as described in 821 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 822 * and Threads</a>. 823 */ getPathPermissions()824 public final @Nullable PathPermission[] getPathPermissions() { 825 return mPathPermissions; 826 } 827 828 /** @hide */ setAppOps(int readOp, int writeOp)829 public final void setAppOps(int readOp, int writeOp) { 830 if (!mNoPerms) { 831 mTransport.mReadOp = readOp; 832 mTransport.mWriteOp = writeOp; 833 } 834 } 835 836 /** @hide */ getAppOpsManager()837 public AppOpsManager getAppOpsManager() { 838 return mTransport.mAppOpsManager; 839 } 840 841 /** 842 * Implement this to initialize your content provider on startup. 843 * This method is called for all registered content providers on the 844 * application main thread at application launch time. It must not perform 845 * lengthy operations, or application startup will be delayed. 846 * 847 * <p>You should defer nontrivial initialization (such as opening, 848 * upgrading, and scanning databases) until the content provider is used 849 * (via {@link #query}, {@link #insert}, etc). Deferred initialization 850 * keeps application startup fast, avoids unnecessary work if the provider 851 * turns out not to be needed, and stops database errors (such as a full 852 * disk) from halting application launch. 853 * 854 * <p>If you use SQLite, {@link android.database.sqlite.SQLiteOpenHelper} 855 * is a helpful utility class that makes it easy to manage databases, 856 * and will automatically defer opening until first use. If you do use 857 * SQLiteOpenHelper, make sure to avoid calling 858 * {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} or 859 * {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 860 * from this method. (Instead, override 861 * {@link android.database.sqlite.SQLiteOpenHelper#onOpen} to initialize the 862 * database when it is first opened.) 863 * 864 * @return true if the provider was successfully loaded, false otherwise 865 */ onCreate()866 public abstract boolean onCreate(); 867 868 /** 869 * {@inheritDoc} 870 * This method is always called on the application main thread, and must 871 * not perform lengthy operations. 872 * 873 * <p>The default content provider implementation does nothing. 874 * Override this method to take appropriate action. 875 * (Content providers do not usually care about things like screen 876 * orientation, but may want to know about locale changes.) 877 */ onConfigurationChanged(Configuration newConfig)878 public void onConfigurationChanged(Configuration newConfig) { 879 } 880 881 /** 882 * {@inheritDoc} 883 * This method is always called on the application main thread, and must 884 * not perform lengthy operations. 885 * 886 * <p>The default content provider implementation does nothing. 887 * Subclasses may override this method to take appropriate action. 888 */ onLowMemory()889 public void onLowMemory() { 890 } 891 onTrimMemory(int level)892 public void onTrimMemory(int level) { 893 } 894 895 /** 896 * Implement this to handle query requests from clients. 897 * This method can be called from multiple threads, as described in 898 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 899 * and Threads</a>. 900 * <p> 901 * Example client call:<p> 902 * <pre>// Request a specific record. 903 * Cursor managedCursor = managedQuery( 904 ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2), 905 projection, // Which columns to return. 906 null, // WHERE clause. 907 null, // WHERE clause value substitution 908 People.NAME + " ASC"); // Sort order.</pre> 909 * Example implementation:<p> 910 * <pre>// SQLiteQueryBuilder is a helper class that creates the 911 // proper SQL syntax for us. 912 SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder(); 913 914 // Set the table we're querying. 915 qBuilder.setTables(DATABASE_TABLE_NAME); 916 917 // If the query ends in a specific record number, we're 918 // being asked for a specific record, so set the 919 // WHERE clause in our query. 920 if((URI_MATCHER.match(uri)) == SPECIFIC_MESSAGE){ 921 qBuilder.appendWhere("_id=" + uri.getPathLeafId()); 922 } 923 924 // Make the query. 925 Cursor c = qBuilder.query(mDb, 926 projection, 927 selection, 928 selectionArgs, 929 groupBy, 930 having, 931 sortOrder); 932 c.setNotificationUri(getContext().getContentResolver(), uri); 933 return c;</pre> 934 * 935 * @param uri The URI to query. This will be the full URI sent by the client; 936 * if the client is requesting a specific record, the URI will end in a record number 937 * that the implementation should parse and add to a WHERE or HAVING clause, specifying 938 * that _id value. 939 * @param projection The list of columns to put into the cursor. If 940 * {@code null} all columns are included. 941 * @param selection A selection criteria to apply when filtering rows. 942 * If {@code null} then all rows are included. 943 * @param selectionArgs You may include ?s in selection, which will be replaced by 944 * the values from selectionArgs, in order that they appear in the selection. 945 * The values will be bound as Strings. 946 * @param sortOrder How the rows in the cursor should be sorted. 947 * If {@code null} then the provider is free to define the sort order. 948 * @return a Cursor or {@code null}. 949 */ query(@onNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder)950 public abstract @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection, 951 @Nullable String selection, @Nullable String[] selectionArgs, 952 @Nullable String sortOrder); 953 954 /** 955 * Implement this to handle query requests from clients with support for cancellation. 956 * This method can be called from multiple threads, as described in 957 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 958 * and Threads</a>. 959 * <p> 960 * Example client call:<p> 961 * <pre>// Request a specific record. 962 * Cursor managedCursor = managedQuery( 963 ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2), 964 projection, // Which columns to return. 965 null, // WHERE clause. 966 null, // WHERE clause value substitution 967 People.NAME + " ASC"); // Sort order.</pre> 968 * Example implementation:<p> 969 * <pre>// SQLiteQueryBuilder is a helper class that creates the 970 // proper SQL syntax for us. 971 SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder(); 972 973 // Set the table we're querying. 974 qBuilder.setTables(DATABASE_TABLE_NAME); 975 976 // If the query ends in a specific record number, we're 977 // being asked for a specific record, so set the 978 // WHERE clause in our query. 979 if((URI_MATCHER.match(uri)) == SPECIFIC_MESSAGE){ 980 qBuilder.appendWhere("_id=" + uri.getPathLeafId()); 981 } 982 983 // Make the query. 984 Cursor c = qBuilder.query(mDb, 985 projection, 986 selection, 987 selectionArgs, 988 groupBy, 989 having, 990 sortOrder); 991 c.setNotificationUri(getContext().getContentResolver(), uri); 992 return c;</pre> 993 * <p> 994 * If you implement this method then you must also implement the version of 995 * {@link #query(Uri, String[], String, String[], String)} that does not take a cancellation 996 * signal to ensure correct operation on older versions of the Android Framework in 997 * which the cancellation signal overload was not available. 998 * 999 * @param uri The URI to query. This will be the full URI sent by the client; 1000 * if the client is requesting a specific record, the URI will end in a record number 1001 * that the implementation should parse and add to a WHERE or HAVING clause, specifying 1002 * that _id value. 1003 * @param projection The list of columns to put into the cursor. If 1004 * {@code null} all columns are included. 1005 * @param selection A selection criteria to apply when filtering rows. 1006 * If {@code null} then all rows are included. 1007 * @param selectionArgs You may include ?s in selection, which will be replaced by 1008 * the values from selectionArgs, in order that they appear in the selection. 1009 * The values will be bound as Strings. 1010 * @param sortOrder How the rows in the cursor should be sorted. 1011 * If {@code null} then the provider is free to define the sort order. 1012 * @param cancellationSignal A signal to cancel the operation in progress, or {@code null} if none. 1013 * If the operation is canceled, then {@link OperationCanceledException} will be thrown 1014 * when the query is executed. 1015 * @return a Cursor or {@code null}. 1016 */ query(@onNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal)1017 public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection, 1018 @Nullable String selection, @Nullable String[] selectionArgs, 1019 @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) { 1020 return query(uri, projection, selection, selectionArgs, sortOrder); 1021 } 1022 1023 /** 1024 * Implement this to handle requests for the MIME type of the data at the 1025 * given URI. The returned MIME type should start with 1026 * <code>vnd.android.cursor.item</code> for a single record, 1027 * or <code>vnd.android.cursor.dir/</code> for multiple items. 1028 * This method can be called from multiple threads, as described in 1029 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1030 * and Threads</a>. 1031 * 1032 * <p>Note that there are no permissions needed for an application to 1033 * access this information; if your content provider requires read and/or 1034 * write permissions, or is not exported, all applications can still call 1035 * this method regardless of their access permissions. This allows them 1036 * to retrieve the MIME type for a URI when dispatching intents. 1037 * 1038 * @param uri the URI to query. 1039 * @return a MIME type string, or {@code null} if there is no type. 1040 */ getType(@onNull Uri uri)1041 public abstract @Nullable String getType(@NonNull Uri uri); 1042 1043 /** 1044 * Implement this to support canonicalization of URIs that refer to your 1045 * content provider. A canonical URI is one that can be transported across 1046 * devices, backup/restore, and other contexts, and still be able to refer 1047 * to the same data item. Typically this is implemented by adding query 1048 * params to the URI allowing the content provider to verify that an incoming 1049 * canonical URI references the same data as it was originally intended for and, 1050 * if it doesn't, to find that data (if it exists) in the current environment. 1051 * 1052 * <p>For example, if the content provider holds people and a normal URI in it 1053 * is created with a row index into that people database, the cananical representation 1054 * may have an additional query param at the end which specifies the name of the 1055 * person it is intended for. Later calls into the provider with that URI will look 1056 * up the row of that URI's base index and, if it doesn't match or its entry's 1057 * name doesn't match the name in the query param, perform a query on its database 1058 * to find the correct row to operate on.</p> 1059 * 1060 * <p>If you implement support for canonical URIs, <b>all</b> incoming calls with 1061 * URIs (including this one) must perform this verification and recovery of any 1062 * canonical URIs they receive. In addition, you must also implement 1063 * {@link #uncanonicalize} to strip the canonicalization of any of these URIs.</p> 1064 * 1065 * <p>The default implementation of this method returns null, indicating that 1066 * canonical URIs are not supported.</p> 1067 * 1068 * @param url The Uri to canonicalize. 1069 * 1070 * @return Return the canonical representation of <var>url</var>, or null if 1071 * canonicalization of that Uri is not supported. 1072 */ canonicalize(@onNull Uri url)1073 public @Nullable Uri canonicalize(@NonNull Uri url) { 1074 return null; 1075 } 1076 1077 /** 1078 * Remove canonicalization from canonical URIs previously returned by 1079 * {@link #canonicalize}. For example, if your implementation is to add 1080 * a query param to canonicalize a URI, this method can simply trip any 1081 * query params on the URI. The default implementation always returns the 1082 * same <var>url</var> that was passed in. 1083 * 1084 * @param url The Uri to remove any canonicalization from. 1085 * 1086 * @return Return the non-canonical representation of <var>url</var>, return 1087 * the <var>url</var> as-is if there is nothing to do, or return null if 1088 * the data identified by the canonical representation can not be found in 1089 * the current environment. 1090 */ uncanonicalize(@onNull Uri url)1091 public @Nullable Uri uncanonicalize(@NonNull Uri url) { 1092 return url; 1093 } 1094 1095 /** 1096 * @hide 1097 * Implementation when a caller has performed an insert on the content 1098 * provider, but that call has been rejected for the operation given 1099 * to {@link #setAppOps(int, int)}. The default implementation simply 1100 * returns a dummy URI that is the base URI with a 0 path element 1101 * appended. 1102 */ rejectInsert(Uri uri, ContentValues values)1103 public Uri rejectInsert(Uri uri, ContentValues values) { 1104 // If not allowed, we need to return some reasonable URI. Maybe the 1105 // content provider should be responsible for this, but for now we 1106 // will just return the base URI with a dummy '0' tagged on to it. 1107 // You shouldn't be able to read if you can't write, anyway, so it 1108 // shouldn't matter much what is returned. 1109 return uri.buildUpon().appendPath("0").build(); 1110 } 1111 1112 /** 1113 * Implement this to handle requests to insert a new row. 1114 * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} 1115 * after inserting. 1116 * This method can be called from multiple threads, as described in 1117 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1118 * and Threads</a>. 1119 * @param uri The content:// URI of the insertion request. This must not be {@code null}. 1120 * @param values A set of column_name/value pairs to add to the database. 1121 * This must not be {@code null}. 1122 * @return The URI for the newly inserted item. 1123 */ insert(@onNull Uri uri, @Nullable ContentValues values)1124 public abstract @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values); 1125 1126 /** 1127 * Override this to handle requests to insert a set of new rows, or the 1128 * default implementation will iterate over the values and call 1129 * {@link #insert} on each of them. 1130 * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} 1131 * after inserting. 1132 * This method can be called from multiple threads, as described in 1133 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1134 * and Threads</a>. 1135 * 1136 * @param uri The content:// URI of the insertion request. 1137 * @param values An array of sets of column_name/value pairs to add to the database. 1138 * This must not be {@code null}. 1139 * @return The number of values that were inserted. 1140 */ bulkInsert(@onNull Uri uri, @NonNull ContentValues[] values)1141 public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) { 1142 int numValues = values.length; 1143 for (int i = 0; i < numValues; i++) { 1144 insert(uri, values[i]); 1145 } 1146 return numValues; 1147 } 1148 1149 /** 1150 * Implement this to handle requests to delete one or more rows. 1151 * The implementation should apply the selection clause when performing 1152 * deletion, allowing the operation to affect multiple rows in a directory. 1153 * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} 1154 * after deleting. 1155 * This method can be called from multiple threads, as described in 1156 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1157 * and Threads</a>. 1158 * 1159 * <p>The implementation is responsible for parsing out a row ID at the end 1160 * of the URI, if a specific row is being deleted. That is, the client would 1161 * pass in <code>content://contacts/people/22</code> and the implementation is 1162 * responsible for parsing the record number (22) when creating a SQL statement. 1163 * 1164 * @param uri The full URI to query, including a row ID (if a specific record is requested). 1165 * @param selection An optional restriction to apply to rows when deleting. 1166 * @return The number of rows affected. 1167 * @throws SQLException 1168 */ delete(@onNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs)1169 public abstract int delete(@NonNull Uri uri, @Nullable String selection, 1170 @Nullable String[] selectionArgs); 1171 1172 /** 1173 * Implement this to handle requests to update one or more rows. 1174 * The implementation should update all rows matching the selection 1175 * to set the columns according to the provided values map. 1176 * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} 1177 * after updating. 1178 * This method can be called from multiple threads, as described in 1179 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1180 * and Threads</a>. 1181 * 1182 * @param uri The URI to query. This can potentially have a record ID if this 1183 * is an update request for a specific record. 1184 * @param values A set of column_name/value pairs to update in the database. 1185 * This must not be {@code null}. 1186 * @param selection An optional filter to match rows to update. 1187 * @return the number of rows affected. 1188 */ update(@onNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs)1189 public abstract int update(@NonNull Uri uri, @Nullable ContentValues values, 1190 @Nullable String selection, @Nullable String[] selectionArgs); 1191 1192 /** 1193 * Override this to handle requests to open a file blob. 1194 * The default implementation always throws {@link FileNotFoundException}. 1195 * This method can be called from multiple threads, as described in 1196 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1197 * and Threads</a>. 1198 * 1199 * <p>This method returns a ParcelFileDescriptor, which is returned directly 1200 * to the caller. This way large data (such as images and documents) can be 1201 * returned without copying the content. 1202 * 1203 * <p>The returned ParcelFileDescriptor is owned by the caller, so it is 1204 * their responsibility to close it when done. That is, the implementation 1205 * of this method should create a new ParcelFileDescriptor for each call. 1206 * <p> 1207 * If opened with the exclusive "r" or "w" modes, the returned 1208 * ParcelFileDescriptor can be a pipe or socket pair to enable streaming 1209 * of data. Opening with the "rw" or "rwt" modes implies a file on disk that 1210 * supports seeking. 1211 * <p> 1212 * If you need to detect when the returned ParcelFileDescriptor has been 1213 * closed, or if the remote process has crashed or encountered some other 1214 * error, you can use {@link ParcelFileDescriptor#open(File, int, 1215 * android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener)}, 1216 * {@link ParcelFileDescriptor#createReliablePipe()}, or 1217 * {@link ParcelFileDescriptor#createReliableSocketPair()}. 1218 * 1219 * <p class="note">For use in Intents, you will want to implement {@link #getType} 1220 * to return the appropriate MIME type for the data returned here with 1221 * the same URI. This will allow intent resolution to automatically determine the data MIME 1222 * type and select the appropriate matching targets as part of its operation.</p> 1223 * 1224 * <p class="note">For better interoperability with other applications, it is recommended 1225 * that for any URIs that can be opened, you also support queries on them 1226 * containing at least the columns specified by {@link android.provider.OpenableColumns}. 1227 * You may also want to support other common columns if you have additional meta-data 1228 * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED} 1229 * in {@link android.provider.MediaStore.MediaColumns}.</p> 1230 * 1231 * @param uri The URI whose file is to be opened. 1232 * @param mode Access mode for the file. May be "r" for read-only access, 1233 * "rw" for read and write access, or "rwt" for read and write access 1234 * that truncates any existing file. 1235 * 1236 * @return Returns a new ParcelFileDescriptor which you can use to access 1237 * the file. 1238 * 1239 * @throws FileNotFoundException Throws FileNotFoundException if there is 1240 * no file associated with the given URI or the mode is invalid. 1241 * @throws SecurityException Throws SecurityException if the caller does 1242 * not have permission to access the file. 1243 * 1244 * @see #openAssetFile(Uri, String) 1245 * @see #openFileHelper(Uri, String) 1246 * @see #getType(android.net.Uri) 1247 * @see ParcelFileDescriptor#parseMode(String) 1248 */ openFile(@onNull Uri uri, @NonNull String mode)1249 public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) 1250 throws FileNotFoundException { 1251 throw new FileNotFoundException("No files supported by provider at " 1252 + uri); 1253 } 1254 1255 /** 1256 * Override this to handle requests to open a file blob. 1257 * The default implementation always throws {@link FileNotFoundException}. 1258 * This method can be called from multiple threads, as described in 1259 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1260 * and Threads</a>. 1261 * 1262 * <p>This method returns a ParcelFileDescriptor, which is returned directly 1263 * to the caller. This way large data (such as images and documents) can be 1264 * returned without copying the content. 1265 * 1266 * <p>The returned ParcelFileDescriptor is owned by the caller, so it is 1267 * their responsibility to close it when done. That is, the implementation 1268 * of this method should create a new ParcelFileDescriptor for each call. 1269 * <p> 1270 * If opened with the exclusive "r" or "w" modes, the returned 1271 * ParcelFileDescriptor can be a pipe or socket pair to enable streaming 1272 * of data. Opening with the "rw" or "rwt" modes implies a file on disk that 1273 * supports seeking. 1274 * <p> 1275 * If you need to detect when the returned ParcelFileDescriptor has been 1276 * closed, or if the remote process has crashed or encountered some other 1277 * error, you can use {@link ParcelFileDescriptor#open(File, int, 1278 * android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener)}, 1279 * {@link ParcelFileDescriptor#createReliablePipe()}, or 1280 * {@link ParcelFileDescriptor#createReliableSocketPair()}. 1281 * 1282 * <p class="note">For use in Intents, you will want to implement {@link #getType} 1283 * to return the appropriate MIME type for the data returned here with 1284 * the same URI. This will allow intent resolution to automatically determine the data MIME 1285 * type and select the appropriate matching targets as part of its operation.</p> 1286 * 1287 * <p class="note">For better interoperability with other applications, it is recommended 1288 * that for any URIs that can be opened, you also support queries on them 1289 * containing at least the columns specified by {@link android.provider.OpenableColumns}. 1290 * You may also want to support other common columns if you have additional meta-data 1291 * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED} 1292 * in {@link android.provider.MediaStore.MediaColumns}.</p> 1293 * 1294 * @param uri The URI whose file is to be opened. 1295 * @param mode Access mode for the file. May be "r" for read-only access, 1296 * "w" for write-only access, "rw" for read and write access, or 1297 * "rwt" for read and write access that truncates any existing 1298 * file. 1299 * @param signal A signal to cancel the operation in progress, or 1300 * {@code null} if none. For example, if you are downloading a 1301 * file from the network to service a "rw" mode request, you 1302 * should periodically call 1303 * {@link CancellationSignal#throwIfCanceled()} to check whether 1304 * the client has canceled the request and abort the download. 1305 * 1306 * @return Returns a new ParcelFileDescriptor which you can use to access 1307 * the file. 1308 * 1309 * @throws FileNotFoundException Throws FileNotFoundException if there is 1310 * no file associated with the given URI or the mode is invalid. 1311 * @throws SecurityException Throws SecurityException if the caller does 1312 * not have permission to access the file. 1313 * 1314 * @see #openAssetFile(Uri, String) 1315 * @see #openFileHelper(Uri, String) 1316 * @see #getType(android.net.Uri) 1317 * @see ParcelFileDescriptor#parseMode(String) 1318 */ openFile(@onNull Uri uri, @NonNull String mode, @Nullable CancellationSignal signal)1319 public @Nullable ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode, 1320 @Nullable CancellationSignal signal) throws FileNotFoundException { 1321 return openFile(uri, mode); 1322 } 1323 1324 /** 1325 * This is like {@link #openFile}, but can be implemented by providers 1326 * that need to be able to return sub-sections of files, often assets 1327 * inside of their .apk. 1328 * This method can be called from multiple threads, as described in 1329 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1330 * and Threads</a>. 1331 * 1332 * <p>If you implement this, your clients must be able to deal with such 1333 * file slices, either directly with 1334 * {@link ContentResolver#openAssetFileDescriptor}, or by using the higher-level 1335 * {@link ContentResolver#openInputStream ContentResolver.openInputStream} 1336 * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream} 1337 * methods. 1338 * <p> 1339 * The returned AssetFileDescriptor can be a pipe or socket pair to enable 1340 * streaming of data. 1341 * 1342 * <p class="note">If you are implementing this to return a full file, you 1343 * should create the AssetFileDescriptor with 1344 * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with 1345 * applications that cannot handle sub-sections of files.</p> 1346 * 1347 * <p class="note">For use in Intents, you will want to implement {@link #getType} 1348 * to return the appropriate MIME type for the data returned here with 1349 * the same URI. This will allow intent resolution to automatically determine the data MIME 1350 * type and select the appropriate matching targets as part of its operation.</p> 1351 * 1352 * <p class="note">For better interoperability with other applications, it is recommended 1353 * that for any URIs that can be opened, you also support queries on them 1354 * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p> 1355 * 1356 * @param uri The URI whose file is to be opened. 1357 * @param mode Access mode for the file. May be "r" for read-only access, 1358 * "w" for write-only access (erasing whatever data is currently in 1359 * the file), "wa" for write-only access to append to any existing data, 1360 * "rw" for read and write access on any existing data, and "rwt" for read 1361 * and write access that truncates any existing file. 1362 * 1363 * @return Returns a new AssetFileDescriptor which you can use to access 1364 * the file. 1365 * 1366 * @throws FileNotFoundException Throws FileNotFoundException if there is 1367 * no file associated with the given URI or the mode is invalid. 1368 * @throws SecurityException Throws SecurityException if the caller does 1369 * not have permission to access the file. 1370 * 1371 * @see #openFile(Uri, String) 1372 * @see #openFileHelper(Uri, String) 1373 * @see #getType(android.net.Uri) 1374 */ openAssetFile(@onNull Uri uri, @NonNull String mode)1375 public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode) 1376 throws FileNotFoundException { 1377 ParcelFileDescriptor fd = openFile(uri, mode); 1378 return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; 1379 } 1380 1381 /** 1382 * This is like {@link #openFile}, but can be implemented by providers 1383 * that need to be able to return sub-sections of files, often assets 1384 * inside of their .apk. 1385 * This method can be called from multiple threads, as described in 1386 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1387 * and Threads</a>. 1388 * 1389 * <p>If you implement this, your clients must be able to deal with such 1390 * file slices, either directly with 1391 * {@link ContentResolver#openAssetFileDescriptor}, or by using the higher-level 1392 * {@link ContentResolver#openInputStream ContentResolver.openInputStream} 1393 * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream} 1394 * methods. 1395 * <p> 1396 * The returned AssetFileDescriptor can be a pipe or socket pair to enable 1397 * streaming of data. 1398 * 1399 * <p class="note">If you are implementing this to return a full file, you 1400 * should create the AssetFileDescriptor with 1401 * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with 1402 * applications that cannot handle sub-sections of files.</p> 1403 * 1404 * <p class="note">For use in Intents, you will want to implement {@link #getType} 1405 * to return the appropriate MIME type for the data returned here with 1406 * the same URI. This will allow intent resolution to automatically determine the data MIME 1407 * type and select the appropriate matching targets as part of its operation.</p> 1408 * 1409 * <p class="note">For better interoperability with other applications, it is recommended 1410 * that for any URIs that can be opened, you also support queries on them 1411 * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p> 1412 * 1413 * @param uri The URI whose file is to be opened. 1414 * @param mode Access mode for the file. May be "r" for read-only access, 1415 * "w" for write-only access (erasing whatever data is currently in 1416 * the file), "wa" for write-only access to append to any existing data, 1417 * "rw" for read and write access on any existing data, and "rwt" for read 1418 * and write access that truncates any existing file. 1419 * @param signal A signal to cancel the operation in progress, or 1420 * {@code null} if none. For example, if you are downloading a 1421 * file from the network to service a "rw" mode request, you 1422 * should periodically call 1423 * {@link CancellationSignal#throwIfCanceled()} to check whether 1424 * the client has canceled the request and abort the download. 1425 * 1426 * @return Returns a new AssetFileDescriptor which you can use to access 1427 * the file. 1428 * 1429 * @throws FileNotFoundException Throws FileNotFoundException if there is 1430 * no file associated with the given URI or the mode is invalid. 1431 * @throws SecurityException Throws SecurityException if the caller does 1432 * not have permission to access the file. 1433 * 1434 * @see #openFile(Uri, String) 1435 * @see #openFileHelper(Uri, String) 1436 * @see #getType(android.net.Uri) 1437 */ openAssetFile(@onNull Uri uri, @NonNull String mode, @Nullable CancellationSignal signal)1438 public @Nullable AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode, 1439 @Nullable CancellationSignal signal) throws FileNotFoundException { 1440 return openAssetFile(uri, mode); 1441 } 1442 1443 /** 1444 * Convenience for subclasses that wish to implement {@link #openFile} 1445 * by looking up a column named "_data" at the given URI. 1446 * 1447 * @param uri The URI to be opened. 1448 * @param mode The file mode. May be "r" for read-only access, 1449 * "w" for write-only access (erasing whatever data is currently in 1450 * the file), "wa" for write-only access to append to any existing data, 1451 * "rw" for read and write access on any existing data, and "rwt" for read 1452 * and write access that truncates any existing file. 1453 * 1454 * @return Returns a new ParcelFileDescriptor that can be used by the 1455 * client to access the file. 1456 */ openFileHelper(@onNull Uri uri, @NonNull String mode)1457 protected final @NonNull ParcelFileDescriptor openFileHelper(@NonNull Uri uri, 1458 @NonNull String mode) throws FileNotFoundException { 1459 Cursor c = query(uri, new String[]{"_data"}, null, null, null); 1460 int count = (c != null) ? c.getCount() : 0; 1461 if (count != 1) { 1462 // If there is not exactly one result, throw an appropriate 1463 // exception. 1464 if (c != null) { 1465 c.close(); 1466 } 1467 if (count == 0) { 1468 throw new FileNotFoundException("No entry for " + uri); 1469 } 1470 throw new FileNotFoundException("Multiple items at " + uri); 1471 } 1472 1473 c.moveToFirst(); 1474 int i = c.getColumnIndex("_data"); 1475 String path = (i >= 0 ? c.getString(i) : null); 1476 c.close(); 1477 if (path == null) { 1478 throw new FileNotFoundException("Column _data not found."); 1479 } 1480 1481 int modeBits = ParcelFileDescriptor.parseMode(mode); 1482 return ParcelFileDescriptor.open(new File(path), modeBits); 1483 } 1484 1485 /** 1486 * Called by a client to determine the types of data streams that this 1487 * content provider supports for the given URI. The default implementation 1488 * returns {@code null}, meaning no types. If your content provider stores data 1489 * of a particular type, return that MIME type if it matches the given 1490 * mimeTypeFilter. If it can perform type conversions, return an array 1491 * of all supported MIME types that match mimeTypeFilter. 1492 * 1493 * @param uri The data in the content provider being queried. 1494 * @param mimeTypeFilter The type of data the client desires. May be 1495 * a pattern, such as */* to retrieve all possible data types. 1496 * @return Returns {@code null} if there are no possible data streams for the 1497 * given mimeTypeFilter. Otherwise returns an array of all available 1498 * concrete MIME types. 1499 * 1500 * @see #getType(Uri) 1501 * @see #openTypedAssetFile(Uri, String, Bundle) 1502 * @see ClipDescription#compareMimeTypes(String, String) 1503 */ getStreamTypes(@onNull Uri uri, @NonNull String mimeTypeFilter)1504 public @Nullable String[] getStreamTypes(@NonNull Uri uri, @NonNull String mimeTypeFilter) { 1505 return null; 1506 } 1507 1508 /** 1509 * Called by a client to open a read-only stream containing data of a 1510 * particular MIME type. This is like {@link #openAssetFile(Uri, String)}, 1511 * except the file can only be read-only and the content provider may 1512 * perform data conversions to generate data of the desired type. 1513 * 1514 * <p>The default implementation compares the given mimeType against the 1515 * result of {@link #getType(Uri)} and, if they match, simply calls 1516 * {@link #openAssetFile(Uri, String)}. 1517 * 1518 * <p>See {@link ClipData} for examples of the use and implementation 1519 * of this method. 1520 * <p> 1521 * The returned AssetFileDescriptor can be a pipe or socket pair to enable 1522 * streaming of data. 1523 * 1524 * <p class="note">For better interoperability with other applications, it is recommended 1525 * that for any URIs that can be opened, you also support queries on them 1526 * containing at least the columns specified by {@link android.provider.OpenableColumns}. 1527 * You may also want to support other common columns if you have additional meta-data 1528 * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED} 1529 * in {@link android.provider.MediaStore.MediaColumns}.</p> 1530 * 1531 * @param uri The data in the content provider being queried. 1532 * @param mimeTypeFilter The type of data the client desires. May be 1533 * a pattern, such as */*, if the caller does not have specific type 1534 * requirements; in this case the content provider will pick its best 1535 * type matching the pattern. 1536 * @param opts Additional options from the client. The definitions of 1537 * these are specific to the content provider being called. 1538 * 1539 * @return Returns a new AssetFileDescriptor from which the client can 1540 * read data of the desired type. 1541 * 1542 * @throws FileNotFoundException Throws FileNotFoundException if there is 1543 * no file associated with the given URI or the mode is invalid. 1544 * @throws SecurityException Throws SecurityException if the caller does 1545 * not have permission to access the data. 1546 * @throws IllegalArgumentException Throws IllegalArgumentException if the 1547 * content provider does not support the requested MIME type. 1548 * 1549 * @see #getStreamTypes(Uri, String) 1550 * @see #openAssetFile(Uri, String) 1551 * @see ClipDescription#compareMimeTypes(String, String) 1552 */ openTypedAssetFile(@onNull Uri uri, @NonNull String mimeTypeFilter, @Nullable Bundle opts)1553 public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri, 1554 @NonNull String mimeTypeFilter, @Nullable Bundle opts) throws FileNotFoundException { 1555 if ("*/*".equals(mimeTypeFilter)) { 1556 // If they can take anything, the untyped open call is good enough. 1557 return openAssetFile(uri, "r"); 1558 } 1559 String baseType = getType(uri); 1560 if (baseType != null && ClipDescription.compareMimeTypes(baseType, mimeTypeFilter)) { 1561 // Use old untyped open call if this provider has a type for this 1562 // URI and it matches the request. 1563 return openAssetFile(uri, "r"); 1564 } 1565 throw new FileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter); 1566 } 1567 1568 1569 /** 1570 * Called by a client to open a read-only stream containing data of a 1571 * particular MIME type. This is like {@link #openAssetFile(Uri, String)}, 1572 * except the file can only be read-only and the content provider may 1573 * perform data conversions to generate data of the desired type. 1574 * 1575 * <p>The default implementation compares the given mimeType against the 1576 * result of {@link #getType(Uri)} and, if they match, simply calls 1577 * {@link #openAssetFile(Uri, String)}. 1578 * 1579 * <p>See {@link ClipData} for examples of the use and implementation 1580 * of this method. 1581 * <p> 1582 * The returned AssetFileDescriptor can be a pipe or socket pair to enable 1583 * streaming of data. 1584 * 1585 * <p class="note">For better interoperability with other applications, it is recommended 1586 * that for any URIs that can be opened, you also support queries on them 1587 * containing at least the columns specified by {@link android.provider.OpenableColumns}. 1588 * You may also want to support other common columns if you have additional meta-data 1589 * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED} 1590 * in {@link android.provider.MediaStore.MediaColumns}.</p> 1591 * 1592 * @param uri The data in the content provider being queried. 1593 * @param mimeTypeFilter The type of data the client desires. May be 1594 * a pattern, such as */*, if the caller does not have specific type 1595 * requirements; in this case the content provider will pick its best 1596 * type matching the pattern. 1597 * @param opts Additional options from the client. The definitions of 1598 * these are specific to the content provider being called. 1599 * @param signal A signal to cancel the operation in progress, or 1600 * {@code null} if none. For example, if you are downloading a 1601 * file from the network to service a "rw" mode request, you 1602 * should periodically call 1603 * {@link CancellationSignal#throwIfCanceled()} to check whether 1604 * the client has canceled the request and abort the download. 1605 * 1606 * @return Returns a new AssetFileDescriptor from which the client can 1607 * read data of the desired type. 1608 * 1609 * @throws FileNotFoundException Throws FileNotFoundException if there is 1610 * no file associated with the given URI or the mode is invalid. 1611 * @throws SecurityException Throws SecurityException if the caller does 1612 * not have permission to access the data. 1613 * @throws IllegalArgumentException Throws IllegalArgumentException if the 1614 * content provider does not support the requested MIME type. 1615 * 1616 * @see #getStreamTypes(Uri, String) 1617 * @see #openAssetFile(Uri, String) 1618 * @see ClipDescription#compareMimeTypes(String, String) 1619 */ openTypedAssetFile(@onNull Uri uri, @NonNull String mimeTypeFilter, @Nullable Bundle opts, @Nullable CancellationSignal signal)1620 public @Nullable AssetFileDescriptor openTypedAssetFile(@NonNull Uri uri, 1621 @NonNull String mimeTypeFilter, @Nullable Bundle opts, 1622 @Nullable CancellationSignal signal) throws FileNotFoundException { 1623 return openTypedAssetFile(uri, mimeTypeFilter, opts); 1624 } 1625 1626 /** 1627 * Interface to write a stream of data to a pipe. Use with 1628 * {@link ContentProvider#openPipeHelper}. 1629 */ 1630 public interface PipeDataWriter<T> { 1631 /** 1632 * Called from a background thread to stream data out to a pipe. 1633 * Note that the pipe is blocking, so this thread can block on 1634 * writes for an arbitrary amount of time if the client is slow 1635 * at reading. 1636 * 1637 * @param output The pipe where data should be written. This will be 1638 * closed for you upon returning from this function. 1639 * @param uri The URI whose data is to be written. 1640 * @param mimeType The desired type of data to be written. 1641 * @param opts Options supplied by caller. 1642 * @param args Your own custom arguments. 1643 */ writeDataToPipe(@onNull ParcelFileDescriptor output, @NonNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts, @Nullable T args)1644 public void writeDataToPipe(@NonNull ParcelFileDescriptor output, @NonNull Uri uri, 1645 @NonNull String mimeType, @Nullable Bundle opts, @Nullable T args); 1646 } 1647 1648 /** 1649 * A helper function for implementing {@link #openTypedAssetFile}, for 1650 * creating a data pipe and background thread allowing you to stream 1651 * generated data back to the client. This function returns a new 1652 * ParcelFileDescriptor that should be returned to the caller (the caller 1653 * is responsible for closing it). 1654 * 1655 * @param uri The URI whose data is to be written. 1656 * @param mimeType The desired type of data to be written. 1657 * @param opts Options supplied by caller. 1658 * @param args Your own custom arguments. 1659 * @param func Interface implementing the function that will actually 1660 * stream the data. 1661 * @return Returns a new ParcelFileDescriptor holding the read side of 1662 * the pipe. This should be returned to the caller for reading; the caller 1663 * is responsible for closing it when done. 1664 */ openPipeHelper(final @NonNull Uri uri, final @NonNull String mimeType, final @Nullable Bundle opts, final @Nullable T args, final @NonNull PipeDataWriter<T> func)1665 public @NonNull <T> ParcelFileDescriptor openPipeHelper(final @NonNull Uri uri, 1666 final @NonNull String mimeType, final @Nullable Bundle opts, final @Nullable T args, 1667 final @NonNull PipeDataWriter<T> func) throws FileNotFoundException { 1668 try { 1669 final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); 1670 1671 AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() { 1672 @Override 1673 protected Object doInBackground(Object... params) { 1674 func.writeDataToPipe(fds[1], uri, mimeType, opts, args); 1675 try { 1676 fds[1].close(); 1677 } catch (IOException e) { 1678 Log.w(TAG, "Failure closing pipe", e); 1679 } 1680 return null; 1681 } 1682 }; 1683 task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[])null); 1684 1685 return fds[0]; 1686 } catch (IOException e) { 1687 throw new FileNotFoundException("failure making pipe"); 1688 } 1689 } 1690 1691 /** 1692 * Returns true if this instance is a temporary content provider. 1693 * @return true if this instance is a temporary content provider 1694 */ isTemporary()1695 protected boolean isTemporary() { 1696 return false; 1697 } 1698 1699 /** 1700 * Returns the Binder object for this provider. 1701 * 1702 * @return the Binder object for this provider 1703 * @hide 1704 */ getIContentProvider()1705 public IContentProvider getIContentProvider() { 1706 return mTransport; 1707 } 1708 1709 /** 1710 * Like {@link #attachInfo(Context, android.content.pm.ProviderInfo)}, but for use 1711 * when directly instantiating the provider for testing. 1712 * @hide 1713 */ attachInfoForTesting(Context context, ProviderInfo info)1714 public void attachInfoForTesting(Context context, ProviderInfo info) { 1715 attachInfo(context, info, true); 1716 } 1717 1718 /** 1719 * After being instantiated, this is called to tell the content provider 1720 * about itself. 1721 * 1722 * @param context The context this provider is running in 1723 * @param info Registered information about this content provider 1724 */ attachInfo(Context context, ProviderInfo info)1725 public void attachInfo(Context context, ProviderInfo info) { 1726 attachInfo(context, info, false); 1727 } 1728 attachInfo(Context context, ProviderInfo info, boolean testing)1729 private void attachInfo(Context context, ProviderInfo info, boolean testing) { 1730 mNoPerms = testing; 1731 1732 /* 1733 * Only allow it to be set once, so after the content service gives 1734 * this to us clients can't change it. 1735 */ 1736 if (mContext == null) { 1737 mContext = context; 1738 if (context != null) { 1739 mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService( 1740 Context.APP_OPS_SERVICE); 1741 } 1742 mMyUid = Process.myUid(); 1743 if (info != null) { 1744 setReadPermission(info.readPermission); 1745 setWritePermission(info.writePermission); 1746 setPathPermissions(info.pathPermissions); 1747 mExported = info.exported; 1748 mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0; 1749 setAuthorities(info.authority); 1750 } 1751 ContentProvider.this.onCreate(); 1752 } 1753 } 1754 1755 /** 1756 * Override this to handle requests to perform a batch of operations, or the 1757 * default implementation will iterate over the operations and call 1758 * {@link ContentProviderOperation#apply} on each of them. 1759 * If all calls to {@link ContentProviderOperation#apply} succeed 1760 * then a {@link ContentProviderResult} array with as many 1761 * elements as there were operations will be returned. If any of the calls 1762 * fail, it is up to the implementation how many of the others take effect. 1763 * This method can be called from multiple threads, as described in 1764 * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes 1765 * and Threads</a>. 1766 * 1767 * @param operations the operations to apply 1768 * @return the results of the applications 1769 * @throws OperationApplicationException thrown if any operation fails. 1770 * @see ContentProviderOperation#apply 1771 */ applyBatch( @onNull ArrayList<ContentProviderOperation> operations)1772 public @NonNull ContentProviderResult[] applyBatch( 1773 @NonNull ArrayList<ContentProviderOperation> operations) 1774 throws OperationApplicationException { 1775 final int numOperations = operations.size(); 1776 final ContentProviderResult[] results = new ContentProviderResult[numOperations]; 1777 for (int i = 0; i < numOperations; i++) { 1778 results[i] = operations.get(i).apply(this, results, i); 1779 } 1780 return results; 1781 } 1782 1783 /** 1784 * Call a provider-defined method. This can be used to implement 1785 * interfaces that are cheaper and/or unnatural for a table-like 1786 * model. 1787 * 1788 * <p class="note"><strong>WARNING:</strong> The framework does no permission checking 1789 * on this entry into the content provider besides the basic ability for the application 1790 * to get access to the provider at all. For example, it has no idea whether the call 1791 * being executed may read or write data in the provider, so can't enforce those 1792 * individual permissions. Any implementation of this method <strong>must</strong> 1793 * do its own permission checks on incoming calls to make sure they are allowed.</p> 1794 * 1795 * @param method method name to call. Opaque to framework, but should not be {@code null}. 1796 * @param arg provider-defined String argument. May be {@code null}. 1797 * @param extras provider-defined Bundle argument. May be {@code null}. 1798 * @return provider-defined return value. May be {@code null}, which is also 1799 * the default for providers which don't implement any call methods. 1800 */ call(@onNull String method, @Nullable String arg, @Nullable Bundle extras)1801 public @Nullable Bundle call(@NonNull String method, @Nullable String arg, 1802 @Nullable Bundle extras) { 1803 return null; 1804 } 1805 1806 /** 1807 * Implement this to shut down the ContentProvider instance. You can then 1808 * invoke this method in unit tests. 1809 * 1810 * <p> 1811 * Android normally handles ContentProvider startup and shutdown 1812 * automatically. You do not need to start up or shut down a 1813 * ContentProvider. When you invoke a test method on a ContentProvider, 1814 * however, a ContentProvider instance is started and keeps running after 1815 * the test finishes, even if a succeeding test instantiates another 1816 * ContentProvider. A conflict develops because the two instances are 1817 * usually running against the same underlying data source (for example, an 1818 * sqlite database). 1819 * </p> 1820 * <p> 1821 * Implementing shutDown() avoids this conflict by providing a way to 1822 * terminate the ContentProvider. This method can also prevent memory leaks 1823 * from multiple instantiations of the ContentProvider, and it can ensure 1824 * unit test isolation by allowing you to completely clean up the test 1825 * fixture before moving on to the next test. 1826 * </p> 1827 */ shutdown()1828 public void shutdown() { 1829 Log.w(TAG, "implement ContentProvider shutdown() to make sure all database " + 1830 "connections are gracefully shutdown"); 1831 } 1832 1833 /** 1834 * Print the Provider's state into the given stream. This gets invoked if 1835 * you run "adb shell dumpsys activity provider <provider_component_name>". 1836 * 1837 * @param fd The raw file descriptor that the dump is being sent to. 1838 * @param writer The PrintWriter to which you should dump your state. This will be 1839 * closed for you after you return. 1840 * @param args additional arguments to the dump request. 1841 */ dump(FileDescriptor fd, PrintWriter writer, String[] args)1842 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 1843 writer.println("nothing to dump"); 1844 } 1845 1846 /** @hide */ validateIncomingUri(Uri uri)1847 private void validateIncomingUri(Uri uri) throws SecurityException { 1848 String auth = uri.getAuthority(); 1849 int userId = getUserIdFromAuthority(auth, UserHandle.USER_CURRENT); 1850 if (userId != UserHandle.USER_CURRENT && userId != mContext.getUserId()) { 1851 throw new SecurityException("trying to query a ContentProvider in user " 1852 + mContext.getUserId() + " with a uri belonging to user " + userId); 1853 } 1854 if (!matchesOurAuthorities(getAuthorityWithoutUserId(auth))) { 1855 String message = "The authority of the uri " + uri + " does not match the one of the " 1856 + "contentProvider: "; 1857 if (mAuthority != null) { 1858 message += mAuthority; 1859 } else { 1860 message += Arrays.toString(mAuthorities); 1861 } 1862 throw new SecurityException(message); 1863 } 1864 } 1865 1866 /** @hide */ getUserIdFromAuthority(String auth, int defaultUserId)1867 public static int getUserIdFromAuthority(String auth, int defaultUserId) { 1868 if (auth == null) return defaultUserId; 1869 int end = auth.lastIndexOf('@'); 1870 if (end == -1) return defaultUserId; 1871 String userIdString = auth.substring(0, end); 1872 try { 1873 return Integer.parseInt(userIdString); 1874 } catch (NumberFormatException e) { 1875 Log.w(TAG, "Error parsing userId.", e); 1876 return UserHandle.USER_NULL; 1877 } 1878 } 1879 1880 /** @hide */ getUserIdFromAuthority(String auth)1881 public static int getUserIdFromAuthority(String auth) { 1882 return getUserIdFromAuthority(auth, UserHandle.USER_CURRENT); 1883 } 1884 1885 /** @hide */ getUserIdFromUri(Uri uri, int defaultUserId)1886 public static int getUserIdFromUri(Uri uri, int defaultUserId) { 1887 if (uri == null) return defaultUserId; 1888 return getUserIdFromAuthority(uri.getAuthority(), defaultUserId); 1889 } 1890 1891 /** @hide */ getUserIdFromUri(Uri uri)1892 public static int getUserIdFromUri(Uri uri) { 1893 return getUserIdFromUri(uri, UserHandle.USER_CURRENT); 1894 } 1895 1896 /** 1897 * Removes userId part from authority string. Expects format: 1898 * userId@some.authority 1899 * If there is no userId in the authority, it symply returns the argument 1900 * @hide 1901 */ getAuthorityWithoutUserId(String auth)1902 public static String getAuthorityWithoutUserId(String auth) { 1903 if (auth == null) return null; 1904 int end = auth.lastIndexOf('@'); 1905 return auth.substring(end+1); 1906 } 1907 1908 /** @hide */ getUriWithoutUserId(Uri uri)1909 public static Uri getUriWithoutUserId(Uri uri) { 1910 if (uri == null) return null; 1911 Uri.Builder builder = uri.buildUpon(); 1912 builder.authority(getAuthorityWithoutUserId(uri.getAuthority())); 1913 return builder.build(); 1914 } 1915 1916 /** @hide */ uriHasUserId(Uri uri)1917 public static boolean uriHasUserId(Uri uri) { 1918 if (uri == null) return false; 1919 return !TextUtils.isEmpty(uri.getUserInfo()); 1920 } 1921 1922 /** @hide */ maybeAddUserId(Uri uri, int userId)1923 public static Uri maybeAddUserId(Uri uri, int userId) { 1924 if (uri == null) return null; 1925 if (userId != UserHandle.USER_CURRENT 1926 && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { 1927 if (!uriHasUserId(uri)) { 1928 //We don't add the user Id if there's already one 1929 Uri.Builder builder = uri.buildUpon(); 1930 builder.encodedAuthority("" + userId + "@" + uri.getEncodedAuthority()); 1931 return builder.build(); 1932 } 1933 } 1934 return uri; 1935 } 1936 } 1937