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