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