blob: 9ccc552f77f576a33e1f7a1c99199834ca85a7bd [file] [log] [blame]
Justin Klaassen10d07c82017-09-15 17:58:39 -04001/*
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
17package android.content;
18
19import android.accounts.Account;
20import android.annotation.IntDef;
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.annotation.RequiresPermission;
24import android.annotation.TestApi;
25import android.annotation.UserIdInt;
26import android.app.ActivityManager;
27import android.app.ActivityThread;
28import android.app.AppGlobals;
29import android.content.pm.PackageManager.NameNotFoundException;
30import android.content.res.AssetFileDescriptor;
31import android.content.res.Resources;
32import android.database.ContentObserver;
33import android.database.CrossProcessCursorWrapper;
34import android.database.Cursor;
35import android.database.IContentObserver;
36import android.graphics.Point;
37import android.graphics.drawable.Drawable;
38import android.net.Uri;
39import android.os.Bundle;
40import android.os.CancellationSignal;
41import android.os.DeadObjectException;
42import android.os.IBinder;
43import android.os.ICancellationSignal;
44import android.os.OperationCanceledException;
45import android.os.ParcelFileDescriptor;
46import android.os.RemoteException;
47import android.os.ServiceManager;
48import android.os.SystemClock;
49import android.os.UserHandle;
50import android.text.TextUtils;
51import android.util.EventLog;
52import android.util.Log;
53
54import com.android.internal.util.ArrayUtils;
55import com.android.internal.util.MimeIconUtils;
56import com.android.internal.util.Preconditions;
57
58import dalvik.system.CloseGuard;
59
60import java.io.File;
61import java.io.FileInputStream;
62import java.io.FileNotFoundException;
63import java.io.IOException;
64import java.io.InputStream;
65import java.io.OutputStream;
66import java.lang.annotation.Retention;
67import java.lang.annotation.RetentionPolicy;
68import java.util.ArrayList;
69import java.util.List;
70import java.util.Random;
71import java.util.concurrent.atomic.AtomicBoolean;
72
73/**
74 * This class provides applications access to the content model.
75 *
76 * <div class="special reference">
77 * <h3>Developer Guides</h3>
78 * <p>For more information about using a ContentResolver with content providers, read the
79 * <a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a>
80 * developer guide.</p>
81 */
82public abstract class ContentResolver {
83 /**
84 * @deprecated instead use
85 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
86 */
87 @Deprecated
88 public static final String SYNC_EXTRAS_ACCOUNT = "account";
89
90 /**
91 * If this extra is set to true, the sync request will be scheduled
92 * at the front of the sync request queue and without any delay
93 */
94 public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
95
96 /**
97 * If this extra is set to true, the sync request will be scheduled
98 * only when the device is plugged in. This is equivalent to calling
99 * setRequiresCharging(true) on {@link SyncRequest}.
100 */
101 public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
102
103 /**
104 * @deprecated instead use
105 * {@link #SYNC_EXTRAS_MANUAL}
106 */
107 @Deprecated
108 public static final String SYNC_EXTRAS_FORCE = "force";
109
110 /**
111 * If this extra is set to true then the sync settings (like getSyncAutomatically())
112 * are ignored by the sync scheduler.
113 */
114 public static final String SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings";
115
116 /**
117 * If this extra is set to true then any backoffs for the initial attempt (e.g. due to retries)
118 * are ignored by the sync scheduler. If this request fails and gets rescheduled then the
119 * retries will still honor the backoff.
120 */
121 public static final String SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff";
122
123 /**
124 * If this extra is set to true then the request will not be retried if it fails.
125 */
126 public static final String SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry";
127
128 /**
129 * Setting this extra is the equivalent of setting both {@link #SYNC_EXTRAS_IGNORE_SETTINGS}
130 * and {@link #SYNC_EXTRAS_IGNORE_BACKOFF}
131 */
132 public static final String SYNC_EXTRAS_MANUAL = "force";
133
134 /**
135 * Indicates that this sync is intended to only upload local changes to the server.
136 * For example, this will be set to true if the sync is initiated by a call to
137 * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
138 */
139 public static final String SYNC_EXTRAS_UPLOAD = "upload";
140
141 /**
142 * Indicates that the sync adapter should proceed with the delete operations,
143 * even if it determines that there are too many.
144 * See {@link SyncResult#tooManyDeletions}
145 */
146 public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
147
148 /**
149 * Indicates that the sync adapter should not proceed with the delete operations,
150 * if it determines that there are too many.
151 * See {@link SyncResult#tooManyDeletions}
152 */
153 public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
154
155 /* Extensions to API. TODO: Not clear if we will keep these as public flags. */
156 /** {@hide} User-specified flag for expected upload size. */
157 public static final String SYNC_EXTRAS_EXPECTED_UPLOAD = "expected_upload";
158
159 /** {@hide} User-specified flag for expected download size. */
160 public static final String SYNC_EXTRAS_EXPECTED_DOWNLOAD = "expected_download";
161
162 /** {@hide} Priority of this sync with respect to other syncs scheduled for this application. */
163 public static final String SYNC_EXTRAS_PRIORITY = "sync_priority";
164
165 /** {@hide} Flag to allow sync to occur on metered network. */
166 public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";
167
168 /**
169 * Set by the SyncManager to request that the SyncAdapter initialize itself for
170 * the given account/authority pair. One required initialization step is to
171 * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
172 * called with a >= 0 value. When this flag is set the SyncAdapter does not need to
173 * do a full sync, though it is allowed to do so.
174 */
175 public static final String SYNC_EXTRAS_INITIALIZE = "initialize";
176
177 /** @hide */
178 public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED =
179 new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
180
181 public static final String SCHEME_CONTENT = "content";
182 public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
183 public static final String SCHEME_FILE = "file";
184
185 /**
186 * An extra {@link Point} describing the optimal size for a requested image
187 * resource, in pixels. If a provider has multiple sizes of the image, it
188 * should return the image closest to this size.
189 *
190 * @see #openTypedAssetFileDescriptor(Uri, String, Bundle)
191 * @see #openTypedAssetFileDescriptor(Uri, String, Bundle,
192 * CancellationSignal)
193 */
194 public static final String EXTRA_SIZE = "android.content.extra.SIZE";
195
196 /**
197 * An extra boolean describing whether a particular provider supports refresh
198 * or not. If a provider supports refresh, it should include this key in its
199 * returned Cursor as part of its query call.
200 *
201 */
202 public static final String EXTRA_REFRESH_SUPPORTED = "android.content.extra.REFRESH_SUPPORTED";
203
204 /**
205 * Key for an SQL style selection string that may be present in the query Bundle argument
206 * passed to {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}
207 * when called by a legacy client.
208 *
209 * <p>Clients should never include user supplied values directly in the selection string,
210 * as this presents an avenue for SQL injection attacks. In lieu of this, a client
211 * should use standard placeholder notation to represent values in a selection string,
212 * then supply a corresponding value in {@value #QUERY_ARG_SQL_SELECTION_ARGS}.
213 *
214 * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
215 * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
216 *
217 * @see #QUERY_ARG_SORT_COLUMNS
218 * @see #QUERY_ARG_SORT_DIRECTION
219 * @see #QUERY_ARG_SORT_COLLATION
220 */
221 public static final String QUERY_ARG_SQL_SELECTION = "android:query-arg-sql-selection";
222
223 /**
224 * Key for SQL selection string arguments list.
225 *
226 * <p>Clients should never include user supplied values directly in the selection string,
227 * as this presents an avenue for SQL injection attacks. In lieu of this, a client
228 * should use standard placeholder notation to represent values in a selection string,
229 * then supply a corresponding value in {@value #QUERY_ARG_SQL_SELECTION_ARGS}.
230 *
231 * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
232 * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
233 *
234 * @see #QUERY_ARG_SORT_COLUMNS
235 * @see #QUERY_ARG_SORT_DIRECTION
236 * @see #QUERY_ARG_SORT_COLLATION
237 */
238 public static final String QUERY_ARG_SQL_SELECTION_ARGS =
239 "android:query-arg-sql-selection-args";
240
241 /**
242 * Key for an SQL style sort string that may be present in the query Bundle argument
243 * passed to {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}
244 * when called by a legacy client.
245 *
246 * <p><b>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
247 * encourage to use structured query arguments in lieu of opaque SQL query clauses.</b>
248 *
249 * @see #QUERY_ARG_SORT_COLUMNS
250 * @see #QUERY_ARG_SORT_DIRECTION
251 * @see #QUERY_ARG_SORT_COLLATION
252 */
253 public static final String QUERY_ARG_SQL_SORT_ORDER = "android:query-arg-sql-sort-order";
254
255 /**
256 * Specifies the list of columns against which to sort results. When first column values
257 * are identical, records are then sorted based on second column values, and so on.
258 *
259 * <p>Columns present in this list must also be included in the projection
260 * supplied to {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
261 *
262 * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher:
263 *
264 * <li>{@link ContentProvider} implementations: When preparing data in
265 * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}, if sort columns
266 * is reflected in the returned Cursor, it is strongly recommended that
267 * {@link #QUERY_ARG_SORT_COLUMNS} then be included in the array of honored arguments
268 * reflected in {@link Cursor} extras {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
269 *
270 * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in the
271 * arguments {@link Bundle}, the Content framework will attempt to synthesize
272 * an QUERY_ARG_SQL* argument using the corresponding QUERY_ARG_SORT* values.
273 */
274 public static final String QUERY_ARG_SORT_COLUMNS = "android:query-arg-sort-columns";
275
276 /**
277 * Specifies desired sort order. When unspecified a provider may provide a default
278 * sort direction, or choose to return unsorted results.
279 *
280 * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher:
281 *
282 * <li>{@link ContentProvider} implementations: When preparing data in
283 * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}, if sort direction
284 * is reflected in the returned Cursor, it is strongly recommended that
285 * {@link #QUERY_ARG_SORT_DIRECTION} then be included in the array of honored arguments
286 * reflected in {@link Cursor} extras {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
287 *
288 * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in the
289 * arguments {@link Bundle}, the Content framework will attempt to synthesize
290 * a QUERY_ARG_SQL* argument using the corresponding QUERY_ARG_SORT* values.
291 *
292 * @see #QUERY_SORT_DIRECTION_ASCENDING
293 * @see #QUERY_SORT_DIRECTION_DESCENDING
294 */
295 public static final String QUERY_ARG_SORT_DIRECTION = "android:query-arg-sort-direction";
296
297 /**
298 * Allows client to specify a hint to the provider declaring which collation
299 * to use when sorting text values.
300 *
301 * <p>Providers may support custom collators. When specifying a custom collator
302 * the value is determined by the Provider.
303 *
304 * <li>{@link ContentProvider} implementations: When preparing data in
305 * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}, if sort collation
306 * is reflected in the returned Cursor, it is strongly recommended that
307 * {@link #QUERY_ARG_SORT_COLLATION} then be included in the array of honored arguments
308 * reflected in {@link Cursor} extras {@link Bundle} under {@link #EXTRA_HONORED_ARGS}.
309 *
310 * <li>When querying a provider, where no QUERY_ARG_SQL* otherwise exists in the
311 * arguments {@link Bundle}, the Content framework will attempt to synthesize
312 * a QUERY_ARG_SQL* argument using the corresponding QUERY_ARG_SORT* values.
313 *
314 * @see java.text.Collator#PRIMARY
315 * @see java.text.Collator#SECONDARY
316 * @see java.text.Collator#TERTIARY
317 * @see java.text.Collator#IDENTICAL
318 */
319 public static final String QUERY_ARG_SORT_COLLATION = "android:query-arg-sort-collation";
320
321 /**
322 * Allows provider to report back to client which query keys are honored in a Cursor.
323 *
324 * <p>Key identifying a {@code String[]} containing all QUERY_ARG_SORT* arguments
325 * honored by the provider. Include this in {@link Cursor} extras {@link Bundle}
326 * when any QUERY_ARG_SORT* value was honored during the preparation of the
327 * results {@link Cursor}.
328 *
329 * <p>If present, ALL honored arguments are enumerated in this extra’s payload.
330 *
331 * @see #QUERY_ARG_SORT_COLUMNS
332 * @see #QUERY_ARG_SORT_DIRECTION
333 * @see #QUERY_ARG_SORT_COLLATION
334 */
335 public static final String EXTRA_HONORED_ARGS = "android.content.extra.HONORED_ARGS";
336
337 /** @hide */
338 @IntDef(flag = false, value = {
339 QUERY_SORT_DIRECTION_ASCENDING,
340 QUERY_SORT_DIRECTION_DESCENDING
341 })
342 @Retention(RetentionPolicy.SOURCE)
343 public @interface SortDirection {}
344 public static final int QUERY_SORT_DIRECTION_ASCENDING = 0;
345 public static final int QUERY_SORT_DIRECTION_DESCENDING = 1;
346
347 /**
348 * @see {@link java.text.Collector} for details on respective collation strength.
349 * @hide
350 */
351 @IntDef(flag = false, value = {
352 java.text.Collator.PRIMARY,
353 java.text.Collator.SECONDARY,
354 java.text.Collator.TERTIARY,
355 java.text.Collator.IDENTICAL
356 })
357 @Retention(RetentionPolicy.SOURCE)
358 public @interface QueryCollator {}
359
360 /**
361 * Specifies the offset row index within a Cursor.
362 */
363 public static final String QUERY_ARG_OFFSET = "android:query-arg-offset";
364
365 /**
366 * Specifies the max number of rows to include in a Cursor.
367 */
368 public static final String QUERY_ARG_LIMIT = "android:query-arg-limit";
369
370 /**
371 * Added to {@link Cursor} extras {@link Bundle} to indicate total row count of
372 * recordset when paging is supported. Providers must include this when
373 * implementing paging support.
374 *
375 * <p>A provider may return -1 that row count of the recordset is unknown.
376 *
377 * <p>Providers having returned -1 in a previous query are recommended to
378 * send content change notification once (if) full recordset size becomes
379 * known.
380 */
381 public static final String EXTRA_TOTAL_COUNT = "android.content.extra.TOTAL_COUNT";
382
383 /**
384 * This is the Android platform's base MIME type for a content: URI
385 * containing a Cursor of a single item. Applications should use this
386 * as the base type along with their own sub-type of their content: URIs
387 * that represent a particular item. For example, hypothetical IMAP email
388 * client may have a URI
389 * <code>content://com.company.provider.imap/inbox/1</code> for a particular
390 * message in the inbox, whose MIME type would be reported as
391 * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code>
392 *
393 * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}.
394 */
395 public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
396
397 /**
398 * This is the Android platform's base MIME type for a content: URI
399 * containing a Cursor of zero or more items. Applications should use this
400 * as the base type along with their own sub-type of their content: URIs
401 * that represent a directory of items. For example, hypothetical IMAP email
402 * client may have a URI
403 * <code>content://com.company.provider.imap/inbox</code> for all of the
404 * messages in its inbox, whose MIME type would be reported as
405 * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code>
406 *
407 * <p>Note how the base MIME type varies between this and
408 * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is
409 * one single item or multiple items in the data set, while the sub-type
410 * remains the same because in either case the data structure contained
411 * in the cursor is the same.
412 */
413 public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
414
415 /**
416 * This is the Android platform's generic MIME type to match any MIME
417 * type of the form "{@link #CURSOR_ITEM_BASE_TYPE}/{@code SUB_TYPE}".
418 * {@code SUB_TYPE} is the sub-type of the application-dependent
419 * content, e.g., "audio", "video", "playlist".
420 */
421 public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
422
423 /** @hide */
424 public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
425 /** @hide */
426 public static final int SYNC_ERROR_AUTHENTICATION = 2;
427 /** @hide */
428 public static final int SYNC_ERROR_IO = 3;
429 /** @hide */
430 public static final int SYNC_ERROR_PARSE = 4;
431 /** @hide */
432 public static final int SYNC_ERROR_CONFLICT = 5;
433 /** @hide */
434 public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;
435 /** @hide */
436 public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;
437 /** @hide */
438 public static final int SYNC_ERROR_INTERNAL = 8;
439
440 private static final String[] SYNC_ERROR_NAMES = new String[] {
441 "already-in-progress",
442 "authentication-error",
443 "io-error",
444 "parse-error",
445 "conflict",
446 "too-many-deletions",
447 "too-many-retries",
448 "internal-error",
449 };
450
451 /** @hide */
452 public static String syncErrorToString(int error) {
453 if (error < 1 || error > SYNC_ERROR_NAMES.length) {
454 return String.valueOf(error);
455 }
456 return SYNC_ERROR_NAMES[error - 1];
457 }
458
459 /** @hide */
460 public static int syncErrorStringToInt(String error) {
461 for (int i = 0, n = SYNC_ERROR_NAMES.length; i < n; i++) {
462 if (SYNC_ERROR_NAMES[i].equals(error)) {
463 return i + 1;
464 }
465 }
466 if (error != null) {
467 try {
468 return Integer.parseInt(error);
469 } catch (NumberFormatException e) {
470 Log.d(TAG, "error parsing sync error: " + error);
471 }
472 }
473 return 0;
474 }
475
476 public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
477 public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
478 public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
479 /** @hide */
480 public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3;
481 /** @hide */
482 public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
483
484 /** @hide */
485 @IntDef(flag = true,
486 value = {
487 NOTIFY_SYNC_TO_NETWORK,
488 NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS
489 })
490 @Retention(RetentionPolicy.SOURCE)
491 public @interface NotifyFlags {}
492
493 /**
494 * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: attempt to sync the change
495 * to the network.
496 */
497 public static final int NOTIFY_SYNC_TO_NETWORK = 1<<0;
498
499 /**
500 * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: if set, this notification
501 * will be skipped if it is being delivered to the root URI of a ContentObserver that is
502 * using "notify for descendants." The purpose of this is to allow the provide to send
503 * a general notification of "something under X" changed that observers of that specific
504 * URI can receive, while also sending a specific URI under X. It would use this flag
505 * when sending the former, so that observers of "X and descendants" only see the latter.
506 */
507 public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1;
508
509 // Always log queries which take 500ms+; shorter queries are
510 // sampled accordingly.
511 private static final boolean ENABLE_CONTENT_SAMPLE = false;
512 private static final int SLOW_THRESHOLD_MILLIS = 500;
513 private final Random mRandom = new Random(); // guarded by itself
514
515 public ContentResolver(Context context) {
516 mContext = context != null ? context : ActivityThread.currentApplication();
517 mPackageName = mContext.getOpPackageName();
518 mTargetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
519 }
520
521 /** @hide */
522 protected abstract IContentProvider acquireProvider(Context c, String name);
523
524 /**
525 * Providing a default implementation of this, to avoid having to change a
526 * lot of other things, but implementations of ContentResolver should
527 * implement it.
528 *
529 * @hide
530 */
531 protected IContentProvider acquireExistingProvider(Context c, String name) {
532 return acquireProvider(c, name);
533 }
534
535 /** @hide */
536 public abstract boolean releaseProvider(IContentProvider icp);
537 /** @hide */
538 protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
539 /** @hide */
540 public abstract boolean releaseUnstableProvider(IContentProvider icp);
541 /** @hide */
542 public abstract void unstableProviderDied(IContentProvider icp);
543
544 /** @hide */
545 public void appNotRespondingViaProvider(IContentProvider icp) {
546 throw new UnsupportedOperationException("appNotRespondingViaProvider");
547 }
548
549 /**
550 * Return the MIME type of the given content URL.
551 *
552 * @param url A Uri identifying content (either a list or specific type),
553 * using the content:// scheme.
554 * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
555 */
556 public final @Nullable String getType(@NonNull Uri url) {
557 Preconditions.checkNotNull(url, "url");
558
559 // XXX would like to have an acquireExistingUnstableProvider for this.
560 IContentProvider provider = acquireExistingProvider(url);
561 if (provider != null) {
562 try {
563 return provider.getType(url);
564 } catch (RemoteException e) {
565 return null;
566 } catch (java.lang.Exception e) {
567 Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
568 return null;
569 } finally {
570 releaseProvider(provider);
571 }
572 }
573
574 if (!SCHEME_CONTENT.equals(url.getScheme())) {
575 return null;
576 }
577
578 try {
579 String type = ActivityManager.getService().getProviderMimeType(
580 ContentProvider.getUriWithoutUserId(url), resolveUserId(url));
581 return type;
582 } catch (RemoteException e) {
583 // Arbitrary and not worth documenting, as Activity
584 // Manager will kill this process shortly anyway.
585 return null;
586 } catch (java.lang.Exception e) {
587 Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
588 return null;
589 }
590 }
591
592 /**
593 * Query for the possible MIME types for the representations the given
594 * content URL can be returned when opened as as stream with
595 * {@link #openTypedAssetFileDescriptor}. Note that the types here are
596 * not necessarily a superset of the type returned by {@link #getType} --
597 * many content providers cannot return a raw stream for the structured
598 * data that they contain.
599 *
600 * @param url A Uri identifying content (either a list or specific type),
601 * using the content:// scheme.
602 * @param mimeTypeFilter The desired MIME type. This may be a pattern,
603 * such as *&#47;*, to query for all available MIME types that match the
604 * pattern.
605 * @return Returns an array of MIME type strings for all available
606 * data streams that match the given mimeTypeFilter. If there are none,
607 * null is returned.
608 */
609 public @Nullable String[] getStreamTypes(@NonNull Uri url, @NonNull String mimeTypeFilter) {
610 Preconditions.checkNotNull(url, "url");
611 Preconditions.checkNotNull(mimeTypeFilter, "mimeTypeFilter");
612
613 IContentProvider provider = acquireProvider(url);
614 if (provider == null) {
615 return null;
616 }
617
618 try {
619 return provider.getStreamTypes(url, mimeTypeFilter);
620 } catch (RemoteException e) {
621 // Arbitrary and not worth documenting, as Activity
622 // Manager will kill this process shortly anyway.
623 return null;
624 } finally {
625 releaseProvider(provider);
626 }
627 }
628
629 /**
630 * Query the given URI, returning a {@link Cursor} over the result set.
631 * <p>
632 * For best performance, the caller should follow these guidelines:
633 * <ul>
634 * <li>Provide an explicit projection, to prevent
635 * reading data from storage that aren't going to be used.</li>
636 * <li>Use question mark parameter markers such as 'phone=?' instead of
637 * explicit values in the {@code selection} parameter, so that queries
638 * that differ only by those values will be recognized as the same
639 * for caching purposes.</li>
640 * </ul>
641 * </p>
642 *
643 * @param uri The URI, using the content:// scheme, for the content to
644 * retrieve.
645 * @param projection A list of which columns to return. Passing null will
646 * return all columns, which is inefficient.
647 * @param selection A filter declaring which rows to return, formatted as an
648 * SQL WHERE clause (excluding the WHERE itself). Passing null will
649 * return all rows for the given URI.
650 * @param selectionArgs You may include ?s in selection, which will be
651 * replaced by the values from selectionArgs, in the order that they
652 * appear in the selection. The values will be bound as Strings.
653 * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
654 * clause (excluding the ORDER BY itself). Passing null will use the
655 * default sort order, which may be unordered.
656 * @return A Cursor object, which is positioned before the first entry, or null
657 * @see Cursor
658 */
659 public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
660 @Nullable String[] projection, @Nullable String selection,
661 @Nullable String[] selectionArgs, @Nullable String sortOrder) {
662 return query(uri, projection, selection, selectionArgs, sortOrder, null);
663 }
664
665 /**
666 * Query the given URI, returning a {@link Cursor} over the result set
667 * with optional support for cancellation.
668 * <p>
669 * For best performance, the caller should follow these guidelines:
670 * <ul>
671 * <li>Provide an explicit projection, to prevent
672 * reading data from storage that aren't going to be used.</li>
673 * <li>Use question mark parameter markers such as 'phone=?' instead of
674 * explicit values in the {@code selection} parameter, so that queries
675 * that differ only by those values will be recognized as the same
676 * for caching purposes.</li>
677 * </ul>
678 * </p>
679 *
680 * @param uri The URI, using the content:// scheme, for the content to
681 * retrieve.
682 * @param projection A list of which columns to return. Passing null will
683 * return all columns, which is inefficient.
684 * @param selection A filter declaring which rows to return, formatted as an
685 * SQL WHERE clause (excluding the WHERE itself). Passing null will
686 * return all rows for the given URI.
687 * @param selectionArgs You may include ?s in selection, which will be
688 * replaced by the values from selectionArgs, in the order that they
689 * appear in the selection. The values will be bound as Strings.
690 * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
691 * clause (excluding the ORDER BY itself). Passing null will use the
692 * default sort order, which may be unordered.
693 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
694 * If the operation is canceled, then {@link OperationCanceledException} will be thrown
695 * when the query is executed.
696 * @return A Cursor object, which is positioned before the first entry, or null
697 * @see Cursor
698 */
699 public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
700 @Nullable String[] projection, @Nullable String selection,
701 @Nullable String[] selectionArgs, @Nullable String sortOrder,
702 @Nullable CancellationSignal cancellationSignal) {
703 Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
704 return query(uri, projection, queryArgs, cancellationSignal);
705 }
706
707 /**
708 * Query the given URI, returning a {@link Cursor} over the result set
709 * with support for cancellation.
710 *
711 * <p>For best performance, the caller should follow these guidelines:
712 *
713 * <li>Provide an explicit projection, to prevent reading data from storage
714 * that aren't going to be used.
715 *
716 * Provider must identify which QUERY_ARG_SORT* arguments were honored during
717 * the preparation of the result set by including the respective argument keys
718 * in the {@link Cursor} extras {@link Bundle}. See {@link #EXTRA_HONORED_ARGS}
719 * for details.
720 *
721 * @see #QUERY_ARG_SORT_COLUMNS, #QUERY_ARG_SORT_DIRECTION, #QUERY_ARG_SORT_COLLATION.
722 *
723 * @param uri The URI, using the content:// scheme, for the content to
724 * retrieve.
725 * @param projection A list of which columns to return. Passing null will
726 * return all columns, which is inefficient.
727 * @param queryArgs A Bundle containing any arguments to the query.
728 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
729 * If the operation is canceled, then {@link OperationCanceledException} will be thrown
730 * when the query is executed.
731 * @return A Cursor object, which is positioned before the first entry, or null
732 * @see Cursor
733 */
734 public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
735 @Nullable String[] projection, @Nullable Bundle queryArgs,
736 @Nullable CancellationSignal cancellationSignal) {
737 Preconditions.checkNotNull(uri, "uri");
738 IContentProvider unstableProvider = acquireUnstableProvider(uri);
739 if (unstableProvider == null) {
740 return null;
741 }
742 IContentProvider stableProvider = null;
743 Cursor qCursor = null;
744 try {
745 long startTime = SystemClock.uptimeMillis();
746
747 ICancellationSignal remoteCancellationSignal = null;
748 if (cancellationSignal != null) {
749 cancellationSignal.throwIfCanceled();
750 remoteCancellationSignal = unstableProvider.createCancellationSignal();
751 cancellationSignal.setRemote(remoteCancellationSignal);
752 }
753 try {
754 qCursor = unstableProvider.query(mPackageName, uri, projection,
755 queryArgs, remoteCancellationSignal);
756 } catch (DeadObjectException e) {
757 // The remote process has died... but we only hold an unstable
758 // reference though, so we might recover!!! Let's try!!!!
759 // This is exciting!!1!!1!!!!1
760 unstableProviderDied(unstableProvider);
761 stableProvider = acquireProvider(uri);
762 if (stableProvider == null) {
763 return null;
764 }
765 qCursor = stableProvider.query(
766 mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
767 }
768 if (qCursor == null) {
769 return null;
770 }
771
772 // Force query execution. Might fail and throw a runtime exception here.
773 qCursor.getCount();
774 long durationMillis = SystemClock.uptimeMillis() - startTime;
775 maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
776
777 // Wrap the cursor object into CursorWrapperInner object.
778 final IContentProvider provider = (stableProvider != null) ? stableProvider
779 : acquireProvider(uri);
780 final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
781 stableProvider = null;
782 qCursor = null;
783 return wrapper;
784 } catch (RemoteException e) {
785 // Arbitrary and not worth documenting, as Activity
786 // Manager will kill this process shortly anyway.
787 return null;
788 } finally {
789 if (qCursor != null) {
790 qCursor.close();
791 }
792 if (cancellationSignal != null) {
793 cancellationSignal.setRemote(null);
794 }
795 if (unstableProvider != null) {
796 releaseUnstableProvider(unstableProvider);
797 }
798 if (stableProvider != null) {
799 releaseProvider(stableProvider);
800 }
801 }
802 }
803
804 /**
805 * Transform the given <var>url</var> to a canonical representation of
806 * its referenced resource, which can be used across devices, persisted,
807 * backed up and restored, etc. The returned Uri is still a fully capable
808 * Uri for use with its content provider, allowing you to do all of the
809 * same content provider operations as with the original Uri --
810 * {@link #query}, {@link #openInputStream(android.net.Uri)}, etc. The
811 * only difference in behavior between the original and new Uris is that
812 * the content provider may need to do some additional work at each call
813 * using it to resolve it to the correct resource, especially if the
814 * canonical Uri has been moved to a different environment.
815 *
816 * <p>If you are moving a canonical Uri between environments, you should
817 * perform another call to {@link #canonicalize} with that original Uri to
818 * re-canonicalize it for the current environment. Alternatively, you may
819 * want to use {@link #uncanonicalize} to transform it to a non-canonical
820 * Uri that works only in the current environment but potentially more
821 * efficiently than the canonical representation.</p>
822 *
823 * @param url The {@link Uri} that is to be transformed to a canonical
824 * representation. Like all resolver calls, the input can be either
825 * a non-canonical or canonical Uri.
826 *
827 * @return Returns the official canonical representation of <var>url</var>,
828 * or null if the content provider does not support a canonical representation
829 * of the given Uri. Many providers may not support canonicalization of some
830 * or all of their Uris.
831 *
832 * @see #uncanonicalize
833 */
834 public final @Nullable Uri canonicalize(@NonNull Uri url) {
835 Preconditions.checkNotNull(url, "url");
836 IContentProvider provider = acquireProvider(url);
837 if (provider == null) {
838 return null;
839 }
840
841 try {
842 return provider.canonicalize(mPackageName, url);
843 } catch (RemoteException e) {
844 // Arbitrary and not worth documenting, as Activity
845 // Manager will kill this process shortly anyway.
846 return null;
847 } finally {
848 releaseProvider(provider);
849 }
850 }
851
852 /**
853 * Given a canonical Uri previously generated by {@link #canonicalize}, convert
854 * it to its local non-canonical form. This can be useful in some cases where
855 * you know that you will only be using the Uri in the current environment and
856 * want to avoid any possible overhead when using it with the content
857 * provider or want to verify that the referenced data exists at all in the
858 * new environment.
859 *
860 * @param url The canonical {@link Uri} that is to be convered back to its
861 * non-canonical form.
862 *
863 * @return Returns the non-canonical representation of <var>url</var>. This will
864 * return null if data identified by the canonical Uri can not be found in
865 * the current environment; callers must always check for null and deal with
866 * that by appropriately falling back to an alternative.
867 *
868 * @see #canonicalize
869 */
870 public final @Nullable Uri uncanonicalize(@NonNull Uri url) {
871 Preconditions.checkNotNull(url, "url");
872 IContentProvider provider = acquireProvider(url);
873 if (provider == null) {
874 return null;
875 }
876
877 try {
878 return provider.uncanonicalize(mPackageName, url);
879 } catch (RemoteException e) {
880 // Arbitrary and not worth documenting, as Activity
881 // Manager will kill this process shortly anyway.
882 return null;
883 } finally {
884 releaseProvider(provider);
885 }
886 }
887
888 /**
889 * This allows clients to request an explicit refresh of content identified by {@code uri}.
890 * <p>
891 * Client code should only invoke this method when there is a strong indication (such as a user
892 * initiated pull to refresh gesture) that the content is stale.
893 * <p>
894 *
895 * @param url The Uri identifying the data to refresh.
896 * @param args Additional options from the client. The definitions of these are specific to the
897 * content provider being called.
898 * @param cancellationSignal A signal to cancel the operation in progress, or {@code null} if
899 * none. For example, if you called refresh on a particular uri, you should call
900 * {@link CancellationSignal#throwIfCanceled()} to check whether the client has
901 * canceled the refresh request.
902 * @return true if the provider actually tried refreshing.
903 */
904 public final boolean refresh(@NonNull Uri url, @Nullable Bundle args,
905 @Nullable CancellationSignal cancellationSignal) {
906 Preconditions.checkNotNull(url, "url");
907 IContentProvider provider = acquireProvider(url);
908 if (provider == null) {
909 return false;
910 }
911
912 try {
913 ICancellationSignal remoteCancellationSignal = null;
914 if (cancellationSignal != null) {
915 cancellationSignal.throwIfCanceled();
916 remoteCancellationSignal = provider.createCancellationSignal();
917 cancellationSignal.setRemote(remoteCancellationSignal);
918 }
919 return provider.refresh(mPackageName, url, args, remoteCancellationSignal);
920 } catch (RemoteException e) {
921 // Arbitrary and not worth documenting, as Activity
922 // Manager will kill this process shortly anyway.
923 return false;
924 } finally {
925 releaseProvider(provider);
926 }
927 }
928
929 /**
930 * Open a stream on to the content associated with a content URI. If there
931 * is no data associated with the URI, FileNotFoundException is thrown.
932 *
933 * <h5>Accepts the following URI schemes:</h5>
934 * <ul>
935 * <li>content ({@link #SCHEME_CONTENT})</li>
936 * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
937 * <li>file ({@link #SCHEME_FILE})</li>
938 * </ul>
939 *
940 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
941 * on these schemes.
942 *
943 * @param uri The desired URI.
944 * @return InputStream
945 * @throws FileNotFoundException if the provided URI could not be opened.
946 * @see #openAssetFileDescriptor(Uri, String)
947 */
948 public final @Nullable InputStream openInputStream(@NonNull Uri uri)
949 throws FileNotFoundException {
950 Preconditions.checkNotNull(uri, "uri");
951 String scheme = uri.getScheme();
952 if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
953 // Note: left here to avoid breaking compatibility. May be removed
954 // with sufficient testing.
955 OpenResourceIdResult r = getResourceId(uri);
956 try {
957 InputStream stream = r.r.openRawResource(r.id);
958 return stream;
959 } catch (Resources.NotFoundException ex) {
960 throw new FileNotFoundException("Resource does not exist: " + uri);
961 }
962 } else if (SCHEME_FILE.equals(scheme)) {
963 // Note: left here to avoid breaking compatibility. May be removed
964 // with sufficient testing.
965 return new FileInputStream(uri.getPath());
966 } else {
967 AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r", null);
968 try {
969 return fd != null ? fd.createInputStream() : null;
970 } catch (IOException e) {
971 throw new FileNotFoundException("Unable to create stream");
972 }
973 }
974 }
975
976 /**
977 * Synonym for {@link #openOutputStream(Uri, String)
978 * openOutputStream(uri, "w")}.
979 * @throws FileNotFoundException if the provided URI could not be opened.
980 */
981 public final @Nullable OutputStream openOutputStream(@NonNull Uri uri)
982 throws FileNotFoundException {
983 return openOutputStream(uri, "w");
984 }
985
986 /**
987 * Open a stream on to the content associated with a content URI. If there
988 * is no data associated with the URI, FileNotFoundException is thrown.
989 *
990 * <h5>Accepts the following URI schemes:</h5>
991 * <ul>
992 * <li>content ({@link #SCHEME_CONTENT})</li>
993 * <li>file ({@link #SCHEME_FILE})</li>
994 * </ul>
995 *
996 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
997 * on these schemes.
998 *
999 * @param uri The desired URI.
1000 * @param mode May be "w", "wa", "rw", or "rwt".
1001 * @return OutputStream
1002 * @throws FileNotFoundException if the provided URI could not be opened.
1003 * @see #openAssetFileDescriptor(Uri, String)
1004 */
1005 public final @Nullable OutputStream openOutputStream(@NonNull Uri uri, @NonNull String mode)
1006 throws FileNotFoundException {
1007 AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
1008 try {
1009 return fd != null ? fd.createOutputStream() : null;
1010 } catch (IOException e) {
1011 throw new FileNotFoundException("Unable to create stream");
1012 }
1013 }
1014
1015 /**
1016 * Open a raw file descriptor to access data under a URI. This
1017 * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
1018 * underlying {@link ContentProvider#openFile}
1019 * ContentProvider.openFile()} method, so will <em>not</em> work with
1020 * providers that return sub-sections of files. If at all possible,
1021 * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
1022 * will receive a FileNotFoundException exception if the provider returns a
1023 * sub-section of a file.
1024 *
1025 * <h5>Accepts the following URI schemes:</h5>
1026 * <ul>
1027 * <li>content ({@link #SCHEME_CONTENT})</li>
1028 * <li>file ({@link #SCHEME_FILE})</li>
1029 * </ul>
1030 *
1031 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
1032 * on these schemes.
1033 * <p>
1034 * If opening with the exclusive "r" or "w" modes, the returned
1035 * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
1036 * of data. Opening with the "rw" mode implies a file on disk that supports
1037 * seeking. If possible, always use an exclusive mode to give the underlying
1038 * {@link ContentProvider} the most flexibility.
1039 * <p>
1040 * If you are writing a file, and need to communicate an error to the
1041 * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
1042 *
1043 * @param uri The desired URI to open.
1044 * @param mode The file mode to use, as per {@link ContentProvider#openFile
1045 * ContentProvider.openFile}.
1046 * @return Returns a new ParcelFileDescriptor pointing to the file. You
1047 * own this descriptor and are responsible for closing it when done.
1048 * @throws FileNotFoundException Throws FileNotFoundException if no
1049 * file exists under the URI or the mode is invalid.
1050 * @see #openAssetFileDescriptor(Uri, String)
1051 */
1052 public final @Nullable ParcelFileDescriptor openFileDescriptor(@NonNull Uri uri,
1053 @NonNull String mode) throws FileNotFoundException {
1054 return openFileDescriptor(uri, mode, null);
1055 }
1056
1057 /**
1058 * Open a raw file descriptor to access data under a URI. This
1059 * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
1060 * underlying {@link ContentProvider#openFile}
1061 * ContentProvider.openFile()} method, so will <em>not</em> work with
1062 * providers that return sub-sections of files. If at all possible,
1063 * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
1064 * will receive a FileNotFoundException exception if the provider returns a
1065 * sub-section of a file.
1066 *
1067 * <h5>Accepts the following URI schemes:</h5>
1068 * <ul>
1069 * <li>content ({@link #SCHEME_CONTENT})</li>
1070 * <li>file ({@link #SCHEME_FILE})</li>
1071 * </ul>
1072 *
1073 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
1074 * on these schemes.
1075 * <p>
1076 * If opening with the exclusive "r" or "w" modes, the returned
1077 * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
1078 * of data. Opening with the "rw" mode implies a file on disk that supports
1079 * seeking. If possible, always use an exclusive mode to give the underlying
1080 * {@link ContentProvider} the most flexibility.
1081 * <p>
1082 * If you are writing a file, and need to communicate an error to the
1083 * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
1084 *
1085 * @param uri The desired URI to open.
1086 * @param mode The file mode to use, as per {@link ContentProvider#openFile
1087 * ContentProvider.openFile}.
1088 * @param cancellationSignal A signal to cancel the operation in progress,
1089 * or null if none. If the operation is canceled, then
1090 * {@link OperationCanceledException} will be thrown.
1091 * @return Returns a new ParcelFileDescriptor pointing to the file. You
1092 * own this descriptor and are responsible for closing it when done.
1093 * @throws FileNotFoundException Throws FileNotFoundException if no
1094 * file exists under the URI or the mode is invalid.
1095 * @see #openAssetFileDescriptor(Uri, String)
1096 */
1097 public final @Nullable ParcelFileDescriptor openFileDescriptor(@NonNull Uri uri,
1098 @NonNull String mode, @Nullable CancellationSignal cancellationSignal)
1099 throws FileNotFoundException {
1100 AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode, cancellationSignal);
1101 if (afd == null) {
1102 return null;
1103 }
1104
1105 if (afd.getDeclaredLength() < 0) {
1106 // This is a full file!
1107 return afd.getParcelFileDescriptor();
1108 }
1109
1110 // Client can't handle a sub-section of a file, so close what
1111 // we got and bail with an exception.
1112 try {
1113 afd.close();
1114 } catch (IOException e) {
1115 }
1116
1117 throw new FileNotFoundException("Not a whole file");
1118 }
1119
1120 /**
1121 * Open a raw file descriptor to access data under a URI. This
1122 * interacts with the underlying {@link ContentProvider#openAssetFile}
1123 * method of the provider associated with the given URI, to retrieve any file stored there.
1124 *
1125 * <h5>Accepts the following URI schemes:</h5>
1126 * <ul>
1127 * <li>content ({@link #SCHEME_CONTENT})</li>
1128 * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
1129 * <li>file ({@link #SCHEME_FILE})</li>
1130 * </ul>
1131 * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
1132 * <p>
1133 * A Uri object can be used to reference a resource in an APK file. The
1134 * Uri should be one of the following formats:
1135 * <ul>
1136 * <li><code>android.resource://package_name/id_number</code><br/>
1137 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
1138 * For example <code>com.example.myapp</code><br/>
1139 * <code>id_number</code> is the int form of the ID.<br/>
1140 * The easiest way to construct this form is
1141 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
1142 * </li>
1143 * <li><code>android.resource://package_name/type/name</code><br/>
1144 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
1145 * For example <code>com.example.myapp</code><br/>
1146 * <code>type</code> is the string form of the resource type. For example, <code>raw</code>
1147 * or <code>drawable</code>.
1148 * <code>name</code> is the string form of the resource name. That is, whatever the file
1149 * name was in your res directory, without the type extension.
1150 * The easiest way to construct this form is
1151 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
1152 * </li>
1153 * </ul>
1154 *
1155 * <p>Note that if this function is called for read-only input (mode is "r")
1156 * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
1157 * for you with a MIME type of "*&#47;*". This allows such callers to benefit
1158 * from any built-in data conversion that a provider implements.
1159 *
1160 * @param uri The desired URI to open.
1161 * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
1162 * ContentProvider.openAssetFile}.
1163 * @return Returns a new ParcelFileDescriptor pointing to the file. You
1164 * own this descriptor and are responsible for closing it when done.
1165 * @throws FileNotFoundException Throws FileNotFoundException of no
1166 * file exists under the URI or the mode is invalid.
1167 */
1168 public final @Nullable AssetFileDescriptor openAssetFileDescriptor(@NonNull Uri uri,
1169 @NonNull String mode) throws FileNotFoundException {
1170 return openAssetFileDescriptor(uri, mode, null);
1171 }
1172
1173 /**
1174 * Open a raw file descriptor to access data under a URI. This
1175 * interacts with the underlying {@link ContentProvider#openAssetFile}
1176 * method of the provider associated with the given URI, to retrieve any file stored there.
1177 *
1178 * <h5>Accepts the following URI schemes:</h5>
1179 * <ul>
1180 * <li>content ({@link #SCHEME_CONTENT})</li>
1181 * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
1182 * <li>file ({@link #SCHEME_FILE})</li>
1183 * </ul>
1184 * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
1185 * <p>
1186 * A Uri object can be used to reference a resource in an APK file. The
1187 * Uri should be one of the following formats:
1188 * <ul>
1189 * <li><code>android.resource://package_name/id_number</code><br/>
1190 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
1191 * For example <code>com.example.myapp</code><br/>
1192 * <code>id_number</code> is the int form of the ID.<br/>
1193 * The easiest way to construct this form is
1194 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
1195 * </li>
1196 * <li><code>android.resource://package_name/type/name</code><br/>
1197 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
1198 * For example <code>com.example.myapp</code><br/>
1199 * <code>type</code> is the string form of the resource type. For example, <code>raw</code>
1200 * or <code>drawable</code>.
1201 * <code>name</code> is the string form of the resource name. That is, whatever the file
1202 * name was in your res directory, without the type extension.
1203 * The easiest way to construct this form is
1204 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
1205 * </li>
1206 * </ul>
1207 *
1208 * <p>Note that if this function is called for read-only input (mode is "r")
1209 * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
1210 * for you with a MIME type of "*&#47;*". This allows such callers to benefit
1211 * from any built-in data conversion that a provider implements.
1212 *
1213 * @param uri The desired URI to open.
1214 * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
1215 * ContentProvider.openAssetFile}.
1216 * @param cancellationSignal A signal to cancel the operation in progress, or null if
1217 * none. If the operation is canceled, then
1218 * {@link OperationCanceledException} will be thrown.
1219 * @return Returns a new ParcelFileDescriptor pointing to the file. You
1220 * own this descriptor and are responsible for closing it when done.
1221 * @throws FileNotFoundException Throws FileNotFoundException of no
1222 * file exists under the URI or the mode is invalid.
1223 */
1224 public final @Nullable AssetFileDescriptor openAssetFileDescriptor(@NonNull Uri uri,
1225 @NonNull String mode, @Nullable CancellationSignal cancellationSignal)
1226 throws FileNotFoundException {
1227 Preconditions.checkNotNull(uri, "uri");
1228 Preconditions.checkNotNull(mode, "mode");
1229
1230 String scheme = uri.getScheme();
1231 if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
1232 if (!"r".equals(mode)) {
1233 throw new FileNotFoundException("Can't write resources: " + uri);
1234 }
1235 OpenResourceIdResult r = getResourceId(uri);
1236 try {
1237 return r.r.openRawResourceFd(r.id);
1238 } catch (Resources.NotFoundException ex) {
1239 throw new FileNotFoundException("Resource does not exist: " + uri);
1240 }
1241 } else if (SCHEME_FILE.equals(scheme)) {
1242 ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
1243 new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
1244 return new AssetFileDescriptor(pfd, 0, -1);
1245 } else {
1246 if ("r".equals(mode)) {
1247 return openTypedAssetFileDescriptor(uri, "*/*", null, cancellationSignal);
1248 } else {
1249 IContentProvider unstableProvider = acquireUnstableProvider(uri);
1250 if (unstableProvider == null) {
1251 throw new FileNotFoundException("No content provider: " + uri);
1252 }
1253 IContentProvider stableProvider = null;
1254 AssetFileDescriptor fd = null;
1255
1256 try {
1257 ICancellationSignal remoteCancellationSignal = null;
1258 if (cancellationSignal != null) {
1259 cancellationSignal.throwIfCanceled();
1260 remoteCancellationSignal = unstableProvider.createCancellationSignal();
1261 cancellationSignal.setRemote(remoteCancellationSignal);
1262 }
1263
1264 try {
1265 fd = unstableProvider.openAssetFile(
1266 mPackageName, uri, mode, remoteCancellationSignal);
1267 if (fd == null) {
1268 // The provider will be released by the finally{} clause
1269 return null;
1270 }
1271 } catch (DeadObjectException e) {
1272 // The remote process has died... but we only hold an unstable
1273 // reference though, so we might recover!!! Let's try!!!!
1274 // This is exciting!!1!!1!!!!1
1275 unstableProviderDied(unstableProvider);
1276 stableProvider = acquireProvider(uri);
1277 if (stableProvider == null) {
1278 throw new FileNotFoundException("No content provider: " + uri);
1279 }
1280 fd = stableProvider.openAssetFile(
1281 mPackageName, uri, mode, remoteCancellationSignal);
1282 if (fd == null) {
1283 // The provider will be released by the finally{} clause
1284 return null;
1285 }
1286 }
1287
1288 if (stableProvider == null) {
1289 stableProvider = acquireProvider(uri);
1290 }
1291 releaseUnstableProvider(unstableProvider);
1292 unstableProvider = null;
1293 ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
1294 fd.getParcelFileDescriptor(), stableProvider);
1295
1296 // Success! Don't release the provider when exiting, let
1297 // ParcelFileDescriptorInner do that when it is closed.
1298 stableProvider = null;
1299
1300 return new AssetFileDescriptor(pfd, fd.getStartOffset(),
1301 fd.getDeclaredLength());
1302
1303 } catch (RemoteException e) {
1304 // Whatever, whatever, we'll go away.
1305 throw new FileNotFoundException(
1306 "Failed opening content provider: " + uri);
1307 } catch (FileNotFoundException e) {
1308 throw e;
1309 } finally {
1310 if (cancellationSignal != null) {
1311 cancellationSignal.setRemote(null);
1312 }
1313 if (stableProvider != null) {
1314 releaseProvider(stableProvider);
1315 }
1316 if (unstableProvider != null) {
1317 releaseUnstableProvider(unstableProvider);
1318 }
1319 }
1320 }
1321 }
1322 }
1323
1324 /**
1325 * Open a raw file descriptor to access (potentially type transformed)
1326 * data from a "content:" URI. This interacts with the underlying
1327 * {@link ContentProvider#openTypedAssetFile} method of the provider
1328 * associated with the given URI, to retrieve retrieve any appropriate
1329 * data stream for the data stored there.
1330 *
1331 * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
1332 * with "content:" URIs, because content providers are the only facility
1333 * with an associated MIME type to ensure that the returned data stream
1334 * is of the desired type.
1335 *
1336 * <p>All text/* streams are encoded in UTF-8.
1337 *
1338 * @param uri The desired URI to open.
1339 * @param mimeType The desired MIME type of the returned data. This can
1340 * be a pattern such as *&#47;*, which will allow the content provider to
1341 * select a type, though there is no way for you to determine what type
1342 * it is returning.
1343 * @param opts Additional provider-dependent options.
1344 * @return Returns a new ParcelFileDescriptor from which you can read the
1345 * data stream from the provider. Note that this may be a pipe, meaning
1346 * you can't seek in it. The only seek you should do is if the
1347 * AssetFileDescriptor contains an offset, to move to that offset before
1348 * reading. You own this descriptor and are responsible for closing it when done.
1349 * @throws FileNotFoundException Throws FileNotFoundException of no
1350 * data of the desired type exists under the URI.
1351 */
1352 public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
1353 @NonNull String mimeType, @Nullable Bundle opts) throws FileNotFoundException {
1354 return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
1355 }
1356
1357 /**
1358 * Open a raw file descriptor to access (potentially type transformed)
1359 * data from a "content:" URI. This interacts with the underlying
1360 * {@link ContentProvider#openTypedAssetFile} method of the provider
1361 * associated with the given URI, to retrieve retrieve any appropriate
1362 * data stream for the data stored there.
1363 *
1364 * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
1365 * with "content:" URIs, because content providers are the only facility
1366 * with an associated MIME type to ensure that the returned data stream
1367 * is of the desired type.
1368 *
1369 * <p>All text/* streams are encoded in UTF-8.
1370 *
1371 * @param uri The desired URI to open.
1372 * @param mimeType The desired MIME type of the returned data. This can
1373 * be a pattern such as *&#47;*, which will allow the content provider to
1374 * select a type, though there is no way for you to determine what type
1375 * it is returning.
1376 * @param opts Additional provider-dependent options.
1377 * @param cancellationSignal A signal to cancel the operation in progress,
1378 * or null if none. If the operation is canceled, then
1379 * {@link OperationCanceledException} will be thrown.
1380 * @return Returns a new ParcelFileDescriptor from which you can read the
1381 * data stream from the provider. Note that this may be a pipe, meaning
1382 * you can't seek in it. The only seek you should do is if the
1383 * AssetFileDescriptor contains an offset, to move to that offset before
1384 * reading. You own this descriptor and are responsible for closing it when done.
1385 * @throws FileNotFoundException Throws FileNotFoundException of no
1386 * data of the desired type exists under the URI.
1387 */
1388 public final @Nullable AssetFileDescriptor openTypedAssetFileDescriptor(@NonNull Uri uri,
1389 @NonNull String mimeType, @Nullable Bundle opts,
1390 @Nullable CancellationSignal cancellationSignal) throws FileNotFoundException {
1391 Preconditions.checkNotNull(uri, "uri");
1392 Preconditions.checkNotNull(mimeType, "mimeType");
1393
1394 IContentProvider unstableProvider = acquireUnstableProvider(uri);
1395 if (unstableProvider == null) {
1396 throw new FileNotFoundException("No content provider: " + uri);
1397 }
1398 IContentProvider stableProvider = null;
1399 AssetFileDescriptor fd = null;
1400
1401 try {
1402 ICancellationSignal remoteCancellationSignal = null;
1403 if (cancellationSignal != null) {
1404 cancellationSignal.throwIfCanceled();
1405 remoteCancellationSignal = unstableProvider.createCancellationSignal();
1406 cancellationSignal.setRemote(remoteCancellationSignal);
1407 }
1408
1409 try {
1410 fd = unstableProvider.openTypedAssetFile(
1411 mPackageName, uri, mimeType, opts, remoteCancellationSignal);
1412 if (fd == null) {
1413 // The provider will be released by the finally{} clause
1414 return null;
1415 }
1416 } catch (DeadObjectException e) {
1417 // The remote process has died... but we only hold an unstable
1418 // reference though, so we might recover!!! Let's try!!!!
1419 // This is exciting!!1!!1!!!!1
1420 unstableProviderDied(unstableProvider);
1421 stableProvider = acquireProvider(uri);
1422 if (stableProvider == null) {
1423 throw new FileNotFoundException("No content provider: " + uri);
1424 }
1425 fd = stableProvider.openTypedAssetFile(
1426 mPackageName, uri, mimeType, opts, remoteCancellationSignal);
1427 if (fd == null) {
1428 // The provider will be released by the finally{} clause
1429 return null;
1430 }
1431 }
1432
1433 if (stableProvider == null) {
1434 stableProvider = acquireProvider(uri);
1435 }
1436 releaseUnstableProvider(unstableProvider);
1437 unstableProvider = null;
1438 ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
1439 fd.getParcelFileDescriptor(), stableProvider);
1440
1441 // Success! Don't release the provider when exiting, let
1442 // ParcelFileDescriptorInner do that when it is closed.
1443 stableProvider = null;
1444
1445 return new AssetFileDescriptor(pfd, fd.getStartOffset(),
1446 fd.getDeclaredLength());
1447
1448 } catch (RemoteException e) {
1449 // Whatever, whatever, we'll go away.
1450 throw new FileNotFoundException(
1451 "Failed opening content provider: " + uri);
1452 } catch (FileNotFoundException e) {
1453 throw e;
1454 } finally {
1455 if (cancellationSignal != null) {
1456 cancellationSignal.setRemote(null);
1457 }
1458 if (stableProvider != null) {
1459 releaseProvider(stableProvider);
1460 }
1461 if (unstableProvider != null) {
1462 releaseUnstableProvider(unstableProvider);
1463 }
1464 }
1465 }
1466
1467 /**
1468 * A resource identified by the {@link Resources} that contains it, and a resource id.
1469 *
1470 * @hide
1471 */
1472 public class OpenResourceIdResult {
1473 public Resources r;
1474 public int id;
1475 }
1476
1477 /**
1478 * Resolves an android.resource URI to a {@link Resources} and a resource id.
1479 *
1480 * @hide
1481 */
1482 public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
1483 String authority = uri.getAuthority();
1484 Resources r;
1485 if (TextUtils.isEmpty(authority)) {
1486 throw new FileNotFoundException("No authority: " + uri);
1487 } else {
1488 try {
1489 r = mContext.getPackageManager().getResourcesForApplication(authority);
1490 } catch (NameNotFoundException ex) {
1491 throw new FileNotFoundException("No package found for authority: " + uri);
1492 }
1493 }
1494 List<String> path = uri.getPathSegments();
1495 if (path == null) {
1496 throw new FileNotFoundException("No path: " + uri);
1497 }
1498 int len = path.size();
1499 int id;
1500 if (len == 1) {
1501 try {
1502 id = Integer.parseInt(path.get(0));
1503 } catch (NumberFormatException e) {
1504 throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
1505 }
1506 } else if (len == 2) {
1507 id = r.getIdentifier(path.get(1), path.get(0), authority);
1508 } else {
1509 throw new FileNotFoundException("More than two path segments: " + uri);
1510 }
1511 if (id == 0) {
1512 throw new FileNotFoundException("No resource found for: " + uri);
1513 }
1514 OpenResourceIdResult res = new OpenResourceIdResult();
1515 res.r = r;
1516 res.id = id;
1517 return res;
1518 }
1519
1520 /**
1521 * Inserts a row into a table at the given URL.
1522 *
1523 * If the content provider supports transactions the insertion will be atomic.
1524 *
1525 * @param url The URL of the table to insert into.
1526 * @param values The initial values for the newly inserted row. The key is the column name for
1527 * the field. Passing an empty ContentValues will create an empty row.
1528 * @return the URL of the newly created row.
1529 */
1530 public final @Nullable Uri insert(@RequiresPermission.Write @NonNull Uri url,
1531 @Nullable ContentValues values) {
1532 Preconditions.checkNotNull(url, "url");
1533 IContentProvider provider = acquireProvider(url);
1534 if (provider == null) {
1535 throw new IllegalArgumentException("Unknown URL " + url);
1536 }
1537 try {
1538 long startTime = SystemClock.uptimeMillis();
1539 Uri createdRow = provider.insert(mPackageName, url, values);
1540 long durationMillis = SystemClock.uptimeMillis() - startTime;
1541 maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
1542 return createdRow;
1543 } catch (RemoteException e) {
1544 // Arbitrary and not worth documenting, as Activity
1545 // Manager will kill this process shortly anyway.
1546 return null;
1547 } finally {
1548 releaseProvider(provider);
1549 }
1550 }
1551
1552 /**
1553 * Applies each of the {@link ContentProviderOperation} objects and returns an array
1554 * of their results. Passes through OperationApplicationException, which may be thrown
1555 * by the call to {@link ContentProviderOperation#apply}.
1556 * If all the applications succeed then a {@link ContentProviderResult} array with the
1557 * same number of elements as the operations will be returned. It is implementation-specific
1558 * how many, if any, operations will have been successfully applied if a call to
1559 * apply results in a {@link OperationApplicationException}.
1560 * @param authority the authority of the ContentProvider to which this batch should be applied
1561 * @param operations the operations to apply
1562 * @return the results of the applications
1563 * @throws OperationApplicationException thrown if an application fails.
1564 * See {@link ContentProviderOperation#apply} for more information.
1565 * @throws RemoteException thrown if a RemoteException is encountered while attempting
1566 * to communicate with a remote provider.
1567 */
1568 public @NonNull ContentProviderResult[] applyBatch(@NonNull String authority,
1569 @NonNull ArrayList<ContentProviderOperation> operations)
1570 throws RemoteException, OperationApplicationException {
1571 Preconditions.checkNotNull(authority, "authority");
1572 Preconditions.checkNotNull(operations, "operations");
1573 ContentProviderClient provider = acquireContentProviderClient(authority);
1574 if (provider == null) {
1575 throw new IllegalArgumentException("Unknown authority " + authority);
1576 }
1577 try {
1578 return provider.applyBatch(operations);
1579 } finally {
1580 provider.release();
1581 }
1582 }
1583
1584 /**
1585 * Inserts multiple rows into a table at the given URL.
1586 *
1587 * This function make no guarantees about the atomicity of the insertions.
1588 *
1589 * @param url The URL of the table to insert into.
1590 * @param values The initial values for the newly inserted rows. The key is the column name for
1591 * the field. Passing null will create an empty row.
1592 * @return the number of newly created rows.
1593 */
1594 public final int bulkInsert(@RequiresPermission.Write @NonNull Uri url,
1595 @NonNull ContentValues[] values) {
1596 Preconditions.checkNotNull(url, "url");
1597 Preconditions.checkNotNull(values, "values");
1598 IContentProvider provider = acquireProvider(url);
1599 if (provider == null) {
1600 throw new IllegalArgumentException("Unknown URL " + url);
1601 }
1602 try {
1603 long startTime = SystemClock.uptimeMillis();
1604 int rowsCreated = provider.bulkInsert(mPackageName, url, values);
1605 long durationMillis = SystemClock.uptimeMillis() - startTime;
1606 maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
1607 return rowsCreated;
1608 } catch (RemoteException e) {
1609 // Arbitrary and not worth documenting, as Activity
1610 // Manager will kill this process shortly anyway.
1611 return 0;
1612 } finally {
1613 releaseProvider(provider);
1614 }
1615 }
1616
1617 /**
1618 * Deletes row(s) specified by a content URI.
1619 *
1620 * If the content provider supports transactions, the deletion will be atomic.
1621 *
1622 * @param url The URL of the row to delete.
1623 * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause
1624 (excluding the WHERE itself).
1625 * @return The number of rows deleted.
1626 */
1627 public final int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where,
1628 @Nullable String[] selectionArgs) {
1629 Preconditions.checkNotNull(url, "url");
1630 IContentProvider provider = acquireProvider(url);
1631 if (provider == null) {
1632 throw new IllegalArgumentException("Unknown URL " + url);
1633 }
1634 try {
1635 long startTime = SystemClock.uptimeMillis();
1636 int rowsDeleted = provider.delete(mPackageName, url, where, selectionArgs);
1637 long durationMillis = SystemClock.uptimeMillis() - startTime;
1638 maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
1639 return rowsDeleted;
1640 } catch (RemoteException e) {
1641 // Arbitrary and not worth documenting, as Activity
1642 // Manager will kill this process shortly anyway.
1643 return -1;
1644 } finally {
1645 releaseProvider(provider);
1646 }
1647 }
1648
1649 /**
1650 * Update row(s) in a content URI.
1651 *
1652 * If the content provider supports transactions the update will be atomic.
1653 *
1654 * @param uri The URI to modify.
1655 * @param values The new field values. The key is the column name for the field.
1656 A null value will remove an existing field value.
1657 * @param where A filter to apply to rows before updating, formatted as an SQL WHERE clause
1658 (excluding the WHERE itself).
1659 * @return the number of rows updated.
1660 * @throws NullPointerException if uri or values are null
1661 */
1662 public final int update(@RequiresPermission.Write @NonNull Uri uri,
1663 @Nullable ContentValues values, @Nullable String where,
1664 @Nullable String[] selectionArgs) {
1665 Preconditions.checkNotNull(uri, "uri");
1666 IContentProvider provider = acquireProvider(uri);
1667 if (provider == null) {
1668 throw new IllegalArgumentException("Unknown URI " + uri);
1669 }
1670 try {
1671 long startTime = SystemClock.uptimeMillis();
1672 int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs);
1673 long durationMillis = SystemClock.uptimeMillis() - startTime;
1674 maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
1675 return rowsUpdated;
1676 } catch (RemoteException e) {
1677 // Arbitrary and not worth documenting, as Activity
1678 // Manager will kill this process shortly anyway.
1679 return -1;
1680 } finally {
1681 releaseProvider(provider);
1682 }
1683 }
1684
1685 /**
1686 * Call a provider-defined method. This can be used to implement
1687 * read or write interfaces which are cheaper than using a Cursor and/or
1688 * do not fit into the traditional table model.
1689 *
1690 * @param method provider-defined method name to call. Opaque to
1691 * framework, but must be non-null.
1692 * @param arg provider-defined String argument. May be null.
1693 * @param extras provider-defined Bundle argument. May be null.
1694 * @return a result Bundle, possibly null. Will be null if the ContentProvider
1695 * does not implement call.
1696 * @throws NullPointerException if uri or method is null
1697 * @throws IllegalArgumentException if uri is not known
1698 */
1699 public final @Nullable Bundle call(@NonNull Uri uri, @NonNull String method,
1700 @Nullable String arg, @Nullable Bundle extras) {
1701 Preconditions.checkNotNull(uri, "uri");
1702 Preconditions.checkNotNull(method, "method");
1703 IContentProvider provider = acquireProvider(uri);
1704 if (provider == null) {
1705 throw new IllegalArgumentException("Unknown URI " + uri);
1706 }
1707 try {
1708 final Bundle res = provider.call(mPackageName, method, arg, extras);
1709 Bundle.setDefusable(res, true);
1710 return res;
1711 } catch (RemoteException e) {
1712 // Arbitrary and not worth documenting, as Activity
1713 // Manager will kill this process shortly anyway.
1714 return null;
1715 } finally {
1716 releaseProvider(provider);
1717 }
1718 }
1719
1720 /**
1721 * Returns the content provider for the given content URI.
1722 *
1723 * @param uri The URI to a content provider
1724 * @return The ContentProvider for the given URI, or null if no content provider is found.
1725 * @hide
1726 */
1727 public final IContentProvider acquireProvider(Uri uri) {
Justin Klaassen47ed54e2017-10-24 19:50:40 -04001728 if (!SCHEME_CONTENT.equals(uri.getScheme())) {
Justin Klaassen10d07c82017-09-15 17:58:39 -04001729 return null;
1730 }
1731 final String auth = uri.getAuthority();
1732 if (auth != null) {
1733 return acquireProvider(mContext, auth);
1734 }
1735 return null;
1736 }
1737
1738 /**
1739 * Returns the content provider for the given content URI if the process
1740 * already has a reference on it.
1741 *
1742 * @param uri The URI to a content provider
1743 * @return The ContentProvider for the given URI, or null if no content provider is found.
1744 * @hide
1745 */
1746 public final IContentProvider acquireExistingProvider(Uri uri) {
1747 if (!SCHEME_CONTENT.equals(uri.getScheme())) {
1748 return null;
1749 }
1750 final String auth = uri.getAuthority();
1751 if (auth != null) {
1752 return acquireExistingProvider(mContext, auth);
1753 }
1754 return null;
1755 }
1756
1757 /**
1758 * @hide
1759 */
1760 public final IContentProvider acquireProvider(String name) {
1761 if (name == null) {
1762 return null;
1763 }
1764 return acquireProvider(mContext, name);
1765 }
1766
1767 /**
1768 * Returns the content provider for the given content URI.
1769 *
1770 * @param uri The URI to a content provider
1771 * @return The ContentProvider for the given URI, or null if no content provider is found.
1772 * @hide
1773 */
1774 public final IContentProvider acquireUnstableProvider(Uri uri) {
1775 if (!SCHEME_CONTENT.equals(uri.getScheme())) {
1776 return null;
1777 }
1778 String auth = uri.getAuthority();
1779 if (auth != null) {
1780 return acquireUnstableProvider(mContext, uri.getAuthority());
1781 }
1782 return null;
1783 }
1784
1785 /**
1786 * @hide
1787 */
1788 public final IContentProvider acquireUnstableProvider(String name) {
1789 if (name == null) {
1790 return null;
1791 }
1792 return acquireUnstableProvider(mContext, name);
1793 }
1794
1795 /**
1796 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
1797 * that services the content at uri, starting the provider if necessary. Returns
1798 * null if there is no provider associated wih the uri. The caller must indicate that they are
1799 * done with the provider by calling {@link ContentProviderClient#release} which will allow
1800 * the system to release the provider it it determines that there is no other reason for
1801 * keeping it active.
1802 * @param uri specifies which provider should be acquired
1803 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
1804 * that services the content at uri or null if there isn't one.
1805 */
1806 public final @Nullable ContentProviderClient acquireContentProviderClient(@NonNull Uri uri) {
1807 Preconditions.checkNotNull(uri, "uri");
1808 IContentProvider provider = acquireProvider(uri);
1809 if (provider != null) {
1810 return new ContentProviderClient(this, provider, true);
1811 }
1812 return null;
1813 }
1814
1815 /**
1816 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
1817 * with the authority of name, starting the provider if necessary. Returns
1818 * null if there is no provider associated wih the uri. The caller must indicate that they are
1819 * done with the provider by calling {@link ContentProviderClient#release} which will allow
1820 * the system to release the provider it it determines that there is no other reason for
1821 * keeping it active.
1822 * @param name specifies which provider should be acquired
1823 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
1824 * with the authority of name or null if there isn't one.
1825 */
1826 public final @Nullable ContentProviderClient acquireContentProviderClient(
1827 @NonNull String name) {
1828 Preconditions.checkNotNull(name, "name");
1829 IContentProvider provider = acquireProvider(name);
1830 if (provider != null) {
1831 return new ContentProviderClient(this, provider, true);
1832 }
1833
1834 return null;
1835 }
1836
1837 /**
1838 * Like {@link #acquireContentProviderClient(Uri)}, but for use when you do
1839 * not trust the stability of the target content provider. This turns off
1840 * the mechanism in the platform clean up processes that are dependent on
1841 * a content provider if that content provider's process goes away. Normally
1842 * you can safely assume that once you have acquired a provider, you can freely
1843 * use it as needed and it won't disappear, even if your process is in the
1844 * background. If using this method, you need to take care to deal with any
1845 * failures when communicating with the provider, and be sure to close it
1846 * so that it can be re-opened later. In particular, catching a
1847 * {@link android.os.DeadObjectException} from the calls there will let you
1848 * know that the content provider has gone away; at that point the current
1849 * ContentProviderClient object is invalid, and you should release it. You
1850 * can acquire a new one if you would like to try to restart the provider
1851 * and perform new operations on it.
1852 */
1853 public final @Nullable ContentProviderClient acquireUnstableContentProviderClient(
1854 @NonNull Uri uri) {
1855 Preconditions.checkNotNull(uri, "uri");
1856 IContentProvider provider = acquireUnstableProvider(uri);
1857 if (provider != null) {
1858 return new ContentProviderClient(this, provider, false);
1859 }
1860
1861 return null;
1862 }
1863
1864 /**
1865 * Like {@link #acquireContentProviderClient(String)}, but for use when you do
1866 * not trust the stability of the target content provider. This turns off
1867 * the mechanism in the platform clean up processes that are dependent on
1868 * a content provider if that content provider's process goes away. Normally
1869 * you can safely assume that once you have acquired a provider, you can freely
1870 * use it as needed and it won't disappear, even if your process is in the
1871 * background. If using this method, you need to take care to deal with any
1872 * failures when communicating with the provider, and be sure to close it
1873 * so that it can be re-opened later. In particular, catching a
1874 * {@link android.os.DeadObjectException} from the calls there will let you
1875 * know that the content provider has gone away; at that point the current
1876 * ContentProviderClient object is invalid, and you should release it. You
1877 * can acquire a new one if you would like to try to restart the provider
1878 * and perform new operations on it.
1879 */
1880 public final @Nullable ContentProviderClient acquireUnstableContentProviderClient(
1881 @NonNull String name) {
1882 Preconditions.checkNotNull(name, "name");
1883 IContentProvider provider = acquireUnstableProvider(name);
1884 if (provider != null) {
1885 return new ContentProviderClient(this, provider, false);
1886 }
1887
1888 return null;
1889 }
1890
1891 /**
1892 * Register an observer class that gets callbacks when data identified by a
1893 * given content URI changes.
1894 * <p>
1895 * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
1896 * notifications must be backed by a valid {@link ContentProvider}.
1897 *
1898 * @param uri The URI to watch for changes. This can be a specific row URI,
1899 * or a base URI for a whole class of content.
1900 * @param notifyForDescendants When false, the observer will be notified
1901 * whenever a change occurs to the exact URI specified by
1902 * <code>uri</code> or to one of the URI's ancestors in the path
1903 * hierarchy. When true, the observer will also be notified
1904 * whenever a change occurs to the URI's descendants in the path
1905 * hierarchy.
1906 * @param observer The object that receives callbacks when changes occur.
1907 * @see #unregisterContentObserver
1908 */
1909 public final void registerContentObserver(@NonNull Uri uri, boolean notifyForDescendants,
1910 @NonNull ContentObserver observer) {
1911 Preconditions.checkNotNull(uri, "uri");
1912 Preconditions.checkNotNull(observer, "observer");
1913 registerContentObserver(
1914 ContentProvider.getUriWithoutUserId(uri),
1915 notifyForDescendants,
1916 observer,
1917 ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
1918 }
1919
1920 /** @hide - designated user version */
1921 public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
1922 ContentObserver observer, @UserIdInt int userHandle) {
1923 try {
1924 getContentService().registerContentObserver(uri, notifyForDescendents,
1925 observer.getContentObserver(), userHandle, mTargetSdkVersion);
1926 } catch (RemoteException e) {
1927 }
1928 }
1929
1930 /**
1931 * Unregisters a change observer.
1932 *
1933 * @param observer The previously registered observer that is no longer needed.
1934 * @see #registerContentObserver
1935 */
1936 public final void unregisterContentObserver(@NonNull ContentObserver observer) {
1937 Preconditions.checkNotNull(observer, "observer");
1938 try {
1939 IContentObserver contentObserver = observer.releaseContentObserver();
1940 if (contentObserver != null) {
1941 getContentService().unregisterContentObserver(
1942 contentObserver);
1943 }
1944 } catch (RemoteException e) {
1945 }
1946 }
1947
1948 /**
1949 * Notify registered observers that a row was updated and attempt to sync
1950 * changes to the network.
1951 * <p>
1952 * To observe events sent through this call, use
1953 * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
1954 * <p>
1955 * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
1956 * notifications must be backed by a valid {@link ContentProvider}.
1957 *
1958 * @param uri The uri of the content that was changed.
1959 * @param observer The observer that originated the change, may be
1960 * <code>null</null>. The observer that originated the change
1961 * will only receive the notification if it has requested to
1962 * receive self-change notifications by implementing
1963 * {@link ContentObserver#deliverSelfNotifications()} to return
1964 * true.
1965 */
1966 public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer) {
1967 notifyChange(uri, observer, true /* sync to network */);
1968 }
1969
1970 /**
1971 * Notify registered observers that a row was updated.
1972 * <p>
1973 * To observe events sent through this call, use
1974 * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
1975 * <p>
1976 * If syncToNetwork is true, this will attempt to schedule a local sync
1977 * using the sync adapter that's registered for the authority of the
1978 * provided uri. No account will be passed to the sync adapter, so all
1979 * matching accounts will be synchronized.
1980 * <p>
1981 * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
1982 * notifications must be backed by a valid {@link ContentProvider}.
1983 *
1984 * @param uri The uri of the content that was changed.
1985 * @param observer The observer that originated the change, may be
1986 * <code>null</null>. The observer that originated the change
1987 * will only receive the notification if it has requested to
1988 * receive self-change notifications by implementing
1989 * {@link ContentObserver#deliverSelfNotifications()} to return
1990 * true.
1991 * @param syncToNetwork If true, same as {@link #NOTIFY_SYNC_TO_NETWORK}.
1992 * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
1993 */
1994 public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
1995 boolean syncToNetwork) {
1996 Preconditions.checkNotNull(uri, "uri");
1997 notifyChange(
1998 ContentProvider.getUriWithoutUserId(uri),
1999 observer,
2000 syncToNetwork,
2001 ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
2002 }
2003
2004 /**
2005 * Notify registered observers that a row was updated.
2006 * <p>
2007 * To observe events sent through this call, use
2008 * {@link #registerContentObserver(Uri, boolean, ContentObserver)}.
2009 * <p>
2010 * If syncToNetwork is true, this will attempt to schedule a local sync
2011 * using the sync adapter that's registered for the authority of the
2012 * provided uri. No account will be passed to the sync adapter, so all
2013 * matching accounts will be synchronized.
2014 * <p>
2015 * Starting in {@link android.os.Build.VERSION_CODES#O}, all content
2016 * notifications must be backed by a valid {@link ContentProvider}.
2017 *
2018 * @param uri The uri of the content that was changed.
2019 * @param observer The observer that originated the change, may be
2020 * <code>null</null>. The observer that originated the change
2021 * will only receive the notification if it has requested to
2022 * receive self-change notifications by implementing
2023 * {@link ContentObserver#deliverSelfNotifications()} to return
2024 * true.
2025 * @param flags Additional flags: {@link #NOTIFY_SYNC_TO_NETWORK}.
2026 * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
2027 */
2028 public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
2029 @NotifyFlags int flags) {
2030 Preconditions.checkNotNull(uri, "uri");
2031 notifyChange(
2032 ContentProvider.getUriWithoutUserId(uri),
2033 observer,
2034 flags,
2035 ContentProvider.getUserIdFromUri(uri, mContext.getUserId()));
2036 }
2037
2038 /**
2039 * Notify registered observers within the designated user(s) that a row was updated.
2040 *
2041 * @hide
2042 */
2043 public void notifyChange(@NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,
2044 @UserIdInt int userHandle) {
2045 try {
2046 getContentService().notifyChange(
2047 uri, observer == null ? null : observer.getContentObserver(),
2048 observer != null && observer.deliverSelfNotifications(),
2049 syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
2050 userHandle, mTargetSdkVersion);
2051 } catch (RemoteException e) {
2052 }
2053 }
2054
2055 /**
2056 * Notify registered observers within the designated user(s) that a row was updated.
2057 *
2058 * @hide
2059 */
2060 public void notifyChange(@NonNull Uri uri, ContentObserver observer, @NotifyFlags int flags,
2061 @UserIdInt int userHandle) {
2062 try {
2063 getContentService().notifyChange(
2064 uri, observer == null ? null : observer.getContentObserver(),
2065 observer != null && observer.deliverSelfNotifications(), flags,
2066 userHandle, mTargetSdkVersion);
2067 } catch (RemoteException e) {
2068 }
2069 }
2070
2071 /**
2072 * Take a persistable URI permission grant that has been offered. Once
2073 * taken, the permission grant will be remembered across device reboots.
2074 * Only URI permissions granted with
2075 * {@link Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} can be persisted. If
2076 * the grant has already been persisted, taking it again will touch
2077 * {@link UriPermission#getPersistedTime()}.
2078 *
2079 * @see #getPersistedUriPermissions()
2080 */
2081 public void takePersistableUriPermission(@NonNull Uri uri,
2082 @Intent.AccessUriMode int modeFlags) {
2083 Preconditions.checkNotNull(uri, "uri");
2084 try {
2085 ActivityManager.getService().takePersistableUriPermission(
2086 ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
2087 } catch (RemoteException e) {
2088 }
2089 }
2090
2091 /**
2092 * Relinquish a persisted URI permission grant. The URI must have been
2093 * previously made persistent with
2094 * {@link #takePersistableUriPermission(Uri, int)}. Any non-persistent
2095 * grants to the calling package will remain intact.
2096 *
2097 * @see #getPersistedUriPermissions()
2098 */
2099 public void releasePersistableUriPermission(@NonNull Uri uri,
2100 @Intent.AccessUriMode int modeFlags) {
2101 Preconditions.checkNotNull(uri, "uri");
2102 try {
2103 ActivityManager.getService().releasePersistableUriPermission(
2104 ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri));
2105 } catch (RemoteException e) {
2106 }
2107 }
2108
2109 /**
2110 * Return list of all URI permission grants that have been persisted by the
2111 * calling app. That is, the returned permissions have been granted
2112 * <em>to</em> the calling app. Only persistable grants taken with
2113 * {@link #takePersistableUriPermission(Uri, int)} are returned.
2114 * <p>Note: Some of the returned URIs may not be usable until after the user is unlocked.
2115 *
2116 * @see #takePersistableUriPermission(Uri, int)
2117 * @see #releasePersistableUriPermission(Uri, int)
2118 */
2119 public @NonNull List<UriPermission> getPersistedUriPermissions() {
2120 try {
2121 return ActivityManager.getService()
2122 .getPersistedUriPermissions(mPackageName, true).getList();
2123 } catch (RemoteException e) {
2124 throw new RuntimeException("Activity manager has died", e);
2125 }
2126 }
2127
2128 /**
2129 * Return list of all persisted URI permission grants that are hosted by the
2130 * calling app. That is, the returned permissions have been granted
2131 * <em>from</em> the calling app. Only grants taken with
2132 * {@link #takePersistableUriPermission(Uri, int)} are returned.
2133 * <p>Note: Some of the returned URIs may not be usable until after the user is unlocked.
2134 */
2135 public @NonNull List<UriPermission> getOutgoingPersistedUriPermissions() {
2136 try {
2137 return ActivityManager.getService()
2138 .getPersistedUriPermissions(mPackageName, false).getList();
2139 } catch (RemoteException e) {
2140 throw new RuntimeException("Activity manager has died", e);
2141 }
2142 }
2143
2144 /**
2145 * Start an asynchronous sync operation. If you want to monitor the progress
2146 * of the sync you may register a SyncObserver. Only values of the following
2147 * types may be used in the extras bundle:
2148 * <ul>
2149 * <li>Integer</li>
2150 * <li>Long</li>
2151 * <li>Boolean</li>
2152 * <li>Float</li>
2153 * <li>Double</li>
2154 * <li>String</li>
2155 * <li>Account</li>
2156 * <li>null</li>
2157 * </ul>
2158 *
2159 * @param uri the uri of the provider to sync or null to sync all providers.
2160 * @param extras any extras to pass to the SyncAdapter.
2161 * @deprecated instead use
2162 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
2163 */
2164 @Deprecated
2165 public void startSync(Uri uri, Bundle extras) {
2166 Account account = null;
2167 if (extras != null) {
2168 String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
2169 if (!TextUtils.isEmpty(accountName)) {
2170 // TODO: No references to Google in AOSP
2171 account = new Account(accountName, "com.google");
2172 }
2173 extras.remove(SYNC_EXTRAS_ACCOUNT);
2174 }
2175 requestSync(account, uri != null ? uri.getAuthority() : null, extras);
2176 }
2177
2178 /**
2179 * Start an asynchronous sync operation. If you want to monitor the progress
2180 * of the sync you may register a SyncObserver. Only values of the following
2181 * types may be used in the extras bundle:
2182 * <ul>
2183 * <li>Integer</li>
2184 * <li>Long</li>
2185 * <li>Boolean</li>
2186 * <li>Float</li>
2187 * <li>Double</li>
2188 * <li>String</li>
2189 * <li>Account</li>
2190 * <li>null</li>
2191 * </ul>
2192 *
2193 * @param account which account should be synced
2194 * @param authority which authority should be synced
2195 * @param extras any extras to pass to the SyncAdapter.
2196 */
2197 public static void requestSync(Account account, String authority, Bundle extras) {
2198 requestSyncAsUser(account, authority, UserHandle.myUserId(), extras);
2199 }
2200
2201 /**
2202 * @see #requestSync(Account, String, Bundle)
2203 * @hide
2204 */
2205 public static void requestSyncAsUser(Account account, String authority, @UserIdInt int userId,
2206 Bundle extras) {
2207 if (extras == null) {
2208 throw new IllegalArgumentException("Must specify extras.");
2209 }
2210 SyncRequest request =
2211 new SyncRequest.Builder()
2212 .setSyncAdapter(account, authority)
2213 .setExtras(extras)
2214 .syncOnce() // Immediate sync.
2215 .build();
2216 try {
2217 getContentService().syncAsUser(request, userId);
2218 } catch(RemoteException e) {
2219 // Shouldn't happen.
2220 }
2221 }
2222
2223 /**
2224 * Register a sync with the SyncManager. These requests are built using the
2225 * {@link SyncRequest.Builder}.
2226 */
2227 public static void requestSync(SyncRequest request) {
2228 try {
2229 getContentService().sync(request);
2230 } catch(RemoteException e) {
2231 // Shouldn't happen.
2232 }
2233 }
2234
2235 /**
2236 * Check that only values of the following types are in the Bundle:
2237 * <ul>
2238 * <li>Integer</li>
2239 * <li>Long</li>
2240 * <li>Boolean</li>
2241 * <li>Float</li>
2242 * <li>Double</li>
2243 * <li>String</li>
2244 * <li>Account</li>
2245 * <li>null</li>
2246 * </ul>
2247 * @param extras the Bundle to check
2248 */
2249 public static void validateSyncExtrasBundle(Bundle extras) {
2250 try {
2251 for (String key : extras.keySet()) {
2252 Object value = extras.get(key);
2253 if (value == null) continue;
2254 if (value instanceof Long) continue;
2255 if (value instanceof Integer) continue;
2256 if (value instanceof Boolean) continue;
2257 if (value instanceof Float) continue;
2258 if (value instanceof Double) continue;
2259 if (value instanceof String) continue;
2260 if (value instanceof Account) continue;
2261 throw new IllegalArgumentException("unexpected value type: "
2262 + value.getClass().getName());
2263 }
2264 } catch (IllegalArgumentException e) {
2265 throw e;
2266 } catch (RuntimeException exc) {
2267 throw new IllegalArgumentException("error unparceling Bundle", exc);
2268 }
2269 }
2270
2271 /**
2272 * Cancel any active or pending syncs that match the Uri. If the uri is null then
2273 * all syncs will be canceled.
2274 *
2275 * @param uri the uri of the provider to sync or null to sync all providers.
2276 * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)}
2277 */
2278 @Deprecated
2279 public void cancelSync(Uri uri) {
2280 cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null);
2281 }
2282
2283 /**
2284 * Cancel any active or pending syncs that match account and authority. The account and
2285 * authority can each independently be set to null, which means that syncs with any account
2286 * or authority, respectively, will match.
2287 *
2288 * @param account filters the syncs that match by this account
2289 * @param authority filters the syncs that match by this authority
2290 */
2291 public static void cancelSync(Account account, String authority) {
2292 try {
2293 getContentService().cancelSync(account, authority, null);
2294 } catch (RemoteException e) {
2295 }
2296 }
2297
2298 /**
2299 * @see #cancelSync(Account, String)
2300 * @hide
2301 */
2302 public static void cancelSyncAsUser(Account account, String authority, @UserIdInt int userId) {
2303 try {
2304 getContentService().cancelSyncAsUser(account, authority, null, userId);
2305 } catch (RemoteException e) {
2306 }
2307 }
2308
2309 /**
2310 * Get information about the SyncAdapters that are known to the system.
2311 * @return an array of SyncAdapters that have registered with the system
2312 */
2313 public static SyncAdapterType[] getSyncAdapterTypes() {
2314 try {
2315 return getContentService().getSyncAdapterTypes();
2316 } catch (RemoteException e) {
2317 throw new RuntimeException("the ContentService should always be reachable", e);
2318 }
2319 }
2320
2321 /**
2322 * @see #getSyncAdapterTypes()
2323 * @hide
2324 */
2325 public static SyncAdapterType[] getSyncAdapterTypesAsUser(@UserIdInt int userId) {
2326 try {
2327 return getContentService().getSyncAdapterTypesAsUser(userId);
2328 } catch (RemoteException e) {
2329 throw new RuntimeException("the ContentService should always be reachable", e);
2330 }
2331 }
2332
2333 /**
2334 * @hide
2335 * Returns the package names of syncadapters that match a given user and authority.
2336 */
2337 @TestApi
2338 public static String[] getSyncAdapterPackagesForAuthorityAsUser(String authority,
2339 @UserIdInt int userId) {
2340 try {
2341 return getContentService().getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
2342 } catch (RemoteException e) {
2343 }
2344 return ArrayUtils.emptyArray(String.class);
2345 }
2346
2347 /**
2348 * Check if the provider should be synced when a network tickle is received
2349 * <p>This method requires the caller to hold the permission
2350 * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
2351 *
2352 * @param account the account whose setting we are querying
2353 * @param authority the provider whose setting we are querying
2354 * @return true if the provider should be synced when a network tickle is received
2355 */
2356 public static boolean getSyncAutomatically(Account account, String authority) {
2357 try {
2358 return getContentService().getSyncAutomatically(account, authority);
2359 } catch (RemoteException e) {
2360 throw new RuntimeException("the ContentService should always be reachable", e);
2361 }
2362 }
2363
2364 /**
2365 * @see #getSyncAutomatically(Account, String)
2366 * @hide
2367 */
2368 public static boolean getSyncAutomaticallyAsUser(Account account, String authority,
2369 @UserIdInt int userId) {
2370 try {
2371 return getContentService().getSyncAutomaticallyAsUser(account, authority, userId);
2372 } catch (RemoteException e) {
2373 throw new RuntimeException("the ContentService should always be reachable", e);
2374 }
2375 }
2376
2377 /**
2378 * Set whether or not the provider is synced when it receives a network tickle.
2379 * <p>This method requires the caller to hold the permission
2380 * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
2381 *
2382 * @param account the account whose setting we are querying
2383 * @param authority the provider whose behavior is being controlled
2384 * @param sync true if the provider should be synced when tickles are received for it
2385 */
2386 public static void setSyncAutomatically(Account account, String authority, boolean sync) {
2387 setSyncAutomaticallyAsUser(account, authority, sync, UserHandle.myUserId());
2388 }
2389
2390 /**
2391 * @see #setSyncAutomatically(Account, String, boolean)
2392 * @hide
2393 */
2394 public static void setSyncAutomaticallyAsUser(Account account, String authority, boolean sync,
2395 @UserIdInt int userId) {
2396 try {
2397 getContentService().setSyncAutomaticallyAsUser(account, authority, sync, userId);
2398 } catch (RemoteException e) {
2399 // exception ignored; if this is thrown then it means the runtime is in the midst of
2400 // being restarted
2401 }
2402 }
2403
2404 /**
2405 * Specifies that a sync should be requested with the specified the account, authority,
2406 * and extras at the given frequency. If there is already another periodic sync scheduled
2407 * with the account, authority and extras then a new periodic sync won't be added, instead
2408 * the frequency of the previous one will be updated.
2409 * <p>
2410 * These periodic syncs honor the "syncAutomatically" and "masterSyncAutomatically" settings.
2411 * Although these sync are scheduled at the specified frequency, it may take longer for it to
2412 * actually be started if other syncs are ahead of it in the sync operation queue. This means
2413 * that the actual start time may drift.
2414 * <p>
2415 * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY},
2416 * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS},
2417 * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE},
2418 * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true.
2419 * If any are supplied then an {@link IllegalArgumentException} will be thrown.
2420 *
2421 * <p>This method requires the caller to hold the permission
2422 * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
2423 * <p>The bundle for a periodic sync can be queried by applications with the correct
2424 * permissions using
2425 * {@link ContentResolver#getPeriodicSyncs(Account account, String provider)}, so no
2426 * sensitive data should be transferred here.
2427 *
2428 * @param account the account to specify in the sync
2429 * @param authority the provider to specify in the sync request
2430 * @param extras extra parameters to go along with the sync request
2431 * @param pollFrequency how frequently the sync should be performed, in seconds. A minimum value
2432 * of 1 hour is enforced.
2433 * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
2434 * are null.
2435 */
2436 public static void addPeriodicSync(Account account, String authority, Bundle extras,
2437 long pollFrequency) {
2438 validateSyncExtrasBundle(extras);
2439 if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false)
2440 || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false)
2441 || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false)
2442 || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false)
2443 || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false)
2444 || extras.getBoolean(SYNC_EXTRAS_FORCE, false)
2445 || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) {
2446 throw new IllegalArgumentException("illegal extras were set");
2447 }
2448 try {
2449 getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
2450 } catch (RemoteException e) {
2451 // exception ignored; if this is thrown then it means the runtime is in the midst of
2452 // being restarted
2453 }
2454 }
2455
2456 /**
2457 * {@hide}
2458 * Helper function to throw an <code>IllegalArgumentException</code> if any illegal
2459 * extras were set for a periodic sync.
2460 *
2461 * @param extras bundle to validate.
2462 */
2463 public static boolean invalidPeriodicExtras(Bundle extras) {
2464 if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)
2465 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)
2466 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)
2467 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
2468 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
2469 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false)
2470 || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
2471 return true;
2472 }
2473 return false;
2474 }
2475
2476 /**
2477 * Remove a periodic sync. Has no affect if account, authority and extras don't match
2478 * an existing periodic sync.
2479 * <p>This method requires the caller to hold the permission
2480 * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
2481 *
2482 * @param account the account of the periodic sync to remove
2483 * @param authority the provider of the periodic sync to remove
2484 * @param extras the extras of the periodic sync to remove
2485 */
2486 public static void removePeriodicSync(Account account, String authority, Bundle extras) {
2487 validateSyncExtrasBundle(extras);
2488 try {
2489 getContentService().removePeriodicSync(account, authority, extras);
2490 } catch (RemoteException e) {
2491 throw new RuntimeException("the ContentService should always be reachable", e);
2492 }
2493 }
2494
2495 /**
2496 * Remove the specified sync. This will cancel any pending or active syncs. If the request is
2497 * for a periodic sync, this call will remove any future occurrences.
2498 * <p>
2499 * If a periodic sync is specified, the caller must hold the permission
2500 * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
2501 *</p>
2502 * It is possible to cancel a sync using a SyncRequest object that is not the same object
2503 * with which you requested the sync. Do so by building a SyncRequest with the same
2504 * adapter, frequency, <b>and</b> extras bundle.
2505 *
2506 * @param request SyncRequest object containing information about sync to cancel.
2507 */
2508 public static void cancelSync(SyncRequest request) {
2509 if (request == null) {
2510 throw new IllegalArgumentException("request cannot be null");
2511 }
2512 try {
2513 getContentService().cancelRequest(request);
2514 } catch (RemoteException e) {
2515 // exception ignored; if this is thrown then it means the runtime is in the midst of
2516 // being restarted
2517 }
2518 }
2519
2520 /**
2521 * Get the list of information about the periodic syncs for the given account and authority.
2522 * <p>This method requires the caller to hold the permission
2523 * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
2524 *
2525 * @param account the account whose periodic syncs we are querying
2526 * @param authority the provider whose periodic syncs we are querying
2527 * @return a list of PeriodicSync objects. This list may be empty but will never be null.
2528 */
2529 public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) {
2530 try {
2531 return getContentService().getPeriodicSyncs(account, authority, null);
2532 } catch (RemoteException e) {
2533 throw new RuntimeException("the ContentService should always be reachable", e);
2534 }
2535 }
2536
2537 /**
2538 * Check if this account/provider is syncable.
2539 * <p>This method requires the caller to hold the permission
2540 * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
2541 * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
2542 */
2543 public static int getIsSyncable(Account account, String authority) {
2544 try {
2545 return getContentService().getIsSyncable(account, authority);
2546 } catch (RemoteException e) {
2547 throw new RuntimeException("the ContentService should always be reachable", e);
2548 }
2549 }
2550
2551 /**
2552 * @see #getIsSyncable(Account, String)
2553 * @hide
2554 */
2555 public static int getIsSyncableAsUser(Account account, String authority,
2556 @UserIdInt int userId) {
2557 try {
2558 return getContentService().getIsSyncableAsUser(account, authority, userId);
2559 } catch (RemoteException e) {
2560 throw new RuntimeException("the ContentService should always be reachable", e);
2561 }
2562 }
2563
2564 /**
2565 * Set whether this account/provider is syncable.
2566 * <p>This method requires the caller to hold the permission
2567 * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
2568 * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown
2569 */
2570 public static void setIsSyncable(Account account, String authority, int syncable) {
2571 try {
2572 getContentService().setIsSyncable(account, authority, syncable);
2573 } catch (RemoteException e) {
2574 // exception ignored; if this is thrown then it means the runtime is in the midst of
2575 // being restarted
2576 }
2577 }
2578
2579 /**
2580 * Gets the master auto-sync setting that applies to all the providers and accounts.
2581 * If this is false then the per-provider auto-sync setting is ignored.
2582 * <p>This method requires the caller to hold the permission
2583 * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
2584 *
2585 * @return the master auto-sync setting that applies to all the providers and accounts
2586 */
2587 public static boolean getMasterSyncAutomatically() {
2588 try {
2589 return getContentService().getMasterSyncAutomatically();
2590 } catch (RemoteException e) {
2591 throw new RuntimeException("the ContentService should always be reachable", e);
2592 }
2593 }
2594
2595 /**
2596 * @see #getMasterSyncAutomatically()
2597 * @hide
2598 */
2599 public static boolean getMasterSyncAutomaticallyAsUser(@UserIdInt int userId) {
2600 try {
2601 return getContentService().getMasterSyncAutomaticallyAsUser(userId);
2602 } catch (RemoteException e) {
2603 throw new RuntimeException("the ContentService should always be reachable", e);
2604 }
2605 }
2606
2607 /**
2608 * Sets the master auto-sync setting that applies to all the providers and accounts.
2609 * If this is false then the per-provider auto-sync setting is ignored.
2610 * <p>This method requires the caller to hold the permission
2611 * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
2612 *
2613 * @param sync the master auto-sync setting that applies to all the providers and accounts
2614 */
2615 public static void setMasterSyncAutomatically(boolean sync) {
2616 setMasterSyncAutomaticallyAsUser(sync, UserHandle.myUserId());
2617 }
2618
2619 /**
2620 * @see #setMasterSyncAutomatically(boolean)
2621 * @hide
2622 */
2623 public static void setMasterSyncAutomaticallyAsUser(boolean sync, @UserIdInt int userId) {
2624 try {
2625 getContentService().setMasterSyncAutomaticallyAsUser(sync, userId);
2626 } catch (RemoteException e) {
2627 // exception ignored; if this is thrown then it means the runtime is in the midst of
2628 // being restarted
2629 }
2630 }
2631
2632 /**
2633 * Returns true if there is currently a sync operation for the given account or authority
2634 * actively being processed.
2635 * <p>This method requires the caller to hold the permission
2636 * {@link android.Manifest.permission#READ_SYNC_STATS}.
2637 * @param account the account whose setting we are querying
2638 * @param authority the provider whose behavior is being queried
2639 * @return true if a sync is active for the given account or authority.
2640 */
2641 public static boolean isSyncActive(Account account, String authority) {
2642 if (account == null) {
2643 throw new IllegalArgumentException("account must not be null");
2644 }
2645 if (authority == null) {
2646 throw new IllegalArgumentException("authority must not be null");
2647 }
2648
2649 try {
2650 return getContentService().isSyncActive(account, authority, null);
2651 } catch (RemoteException e) {
2652 throw new RuntimeException("the ContentService should always be reachable", e);
2653 }
2654 }
2655
2656 /**
2657 * If a sync is active returns the information about it, otherwise returns null.
2658 * <p>
2659 * This method requires the caller to hold the permission
2660 * {@link android.Manifest.permission#READ_SYNC_STATS}.
2661 * <p>
2662 * @return the SyncInfo for the currently active sync or null if one is not active.
2663 * @deprecated
2664 * Since multiple concurrent syncs are now supported you should use
2665 * {@link #getCurrentSyncs()} to get the accurate list of current syncs.
2666 * This method returns the first item from the list of current syncs
2667 * or null if there are none.
2668 */
2669 @Deprecated
2670 public static SyncInfo getCurrentSync() {
2671 try {
2672 final List<SyncInfo> syncs = getContentService().getCurrentSyncs();
2673 if (syncs.isEmpty()) {
2674 return null;
2675 }
2676 return syncs.get(0);
2677 } catch (RemoteException e) {
2678 throw new RuntimeException("the ContentService should always be reachable", e);
2679 }
2680 }
2681
2682 /**
2683 * Returns a list with information about all the active syncs. This list will be empty
2684 * if there are no active syncs.
2685 * <p>
2686 * This method requires the caller to hold the permission
2687 * {@link android.Manifest.permission#READ_SYNC_STATS}.
2688 * <p>
2689 * @return a List of SyncInfo objects for the currently active syncs.
2690 */
2691 public static List<SyncInfo> getCurrentSyncs() {
2692 try {
2693 return getContentService().getCurrentSyncs();
2694 } catch (RemoteException e) {
2695 throw new RuntimeException("the ContentService should always be reachable", e);
2696 }
2697 }
2698
2699 /**
2700 * @see #getCurrentSyncs()
2701 * @hide
2702 */
2703 public static List<SyncInfo> getCurrentSyncsAsUser(@UserIdInt int userId) {
2704 try {
2705 return getContentService().getCurrentSyncsAsUser(userId);
2706 } catch (RemoteException e) {
2707 throw new RuntimeException("the ContentService should always be reachable", e);
2708 }
2709 }
2710
2711 /**
2712 * Returns the status that matches the authority.
2713 * @param account the account whose setting we are querying
2714 * @param authority the provider whose behavior is being queried
2715 * @return the SyncStatusInfo for the authority, or null if none exists
2716 * @hide
2717 */
2718 public static SyncStatusInfo getSyncStatus(Account account, String authority) {
2719 try {
2720 return getContentService().getSyncStatus(account, authority, null);
2721 } catch (RemoteException e) {
2722 throw new RuntimeException("the ContentService should always be reachable", e);
2723 }
2724 }
2725
2726 /**
2727 * @see #getSyncStatus(Account, String)
2728 * @hide
2729 */
2730 public static SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
2731 @UserIdInt int userId) {
2732 try {
2733 return getContentService().getSyncStatusAsUser(account, authority, null, userId);
2734 } catch (RemoteException e) {
2735 throw new RuntimeException("the ContentService should always be reachable", e);
2736 }
2737 }
2738
2739 /**
2740 * Return true if the pending status is true of any matching authorities.
2741 * <p>This method requires the caller to hold the permission
2742 * {@link android.Manifest.permission#READ_SYNC_STATS}.
2743 * @param account the account whose setting we are querying
2744 * @param authority the provider whose behavior is being queried
2745 * @return true if there is a pending sync with the matching account and authority
2746 */
2747 public static boolean isSyncPending(Account account, String authority) {
2748 return isSyncPendingAsUser(account, authority, UserHandle.myUserId());
2749 }
2750
2751 /**
2752 * @see #requestSync(Account, String, Bundle)
2753 * @hide
2754 */
2755 public static boolean isSyncPendingAsUser(Account account, String authority,
2756 @UserIdInt int userId) {
2757 try {
2758 return getContentService().isSyncPendingAsUser(account, authority, null, userId);
2759 } catch (RemoteException e) {
2760 throw new RuntimeException("the ContentService should always be reachable", e);
2761 }
2762 }
2763
2764 /**
2765 * Request notifications when the different aspects of the SyncManager change. The
2766 * different items that can be requested are:
2767 * <ul>
2768 * <li> {@link #SYNC_OBSERVER_TYPE_PENDING}
2769 * <li> {@link #SYNC_OBSERVER_TYPE_ACTIVE}
2770 * <li> {@link #SYNC_OBSERVER_TYPE_SETTINGS}
2771 * </ul>
2772 * The caller can set one or more of the status types in the mask for any
2773 * given listener registration.
2774 * @param mask the status change types that will cause the callback to be invoked
2775 * @param callback observer to be invoked when the status changes
2776 * @return a handle that can be used to remove the listener at a later time
2777 */
2778 public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) {
2779 if (callback == null) {
2780 throw new IllegalArgumentException("you passed in a null callback");
2781 }
2782 try {
2783 ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() {
2784 @Override
2785 public void onStatusChanged(int which) throws RemoteException {
2786 callback.onStatusChanged(which);
2787 }
2788 };
2789 getContentService().addStatusChangeListener(mask, observer);
2790 return observer;
2791 } catch (RemoteException e) {
2792 throw new RuntimeException("the ContentService should always be reachable", e);
2793 }
2794 }
2795
2796 /**
2797 * Remove a previously registered status change listener.
2798 * @param handle the handle that was returned by {@link #addStatusChangeListener}
2799 */
2800 public static void removeStatusChangeListener(Object handle) {
2801 if (handle == null) {
2802 throw new IllegalArgumentException("you passed in a null handle");
2803 }
2804 try {
2805 getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
2806 } catch (RemoteException e) {
2807 // exception ignored; if this is thrown then it means the runtime is in the midst of
2808 // being restarted
2809 }
2810 }
2811
2812 /** {@hide} */
2813 public void putCache(Uri key, Bundle value) {
2814 try {
2815 getContentService().putCache(mContext.getPackageName(), key, value,
2816 mContext.getUserId());
2817 } catch (RemoteException e) {
2818 throw e.rethrowFromSystemServer();
2819 }
2820 }
2821
2822 /** {@hide} */
2823 public Bundle getCache(Uri key) {
2824 try {
2825 final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key,
2826 mContext.getUserId());
2827 if (bundle != null) bundle.setClassLoader(mContext.getClassLoader());
2828 return bundle;
2829 } catch (RemoteException e) {
2830 throw e.rethrowFromSystemServer();
2831 }
2832 }
2833
2834 /** {@hide} */
2835 public int getTargetSdkVersion() {
2836 return mTargetSdkVersion;
2837 }
2838
2839 /**
2840 * Returns sampling percentage for a given duration.
2841 *
2842 * Always returns at least 1%.
2843 */
2844 private int samplePercentForDuration(long durationMillis) {
2845 if (durationMillis >= SLOW_THRESHOLD_MILLIS) {
2846 return 100;
2847 }
2848 return (int) (100 * durationMillis / SLOW_THRESHOLD_MILLIS) + 1;
2849 }
2850
2851 private void maybeLogQueryToEventLog(
2852 long durationMillis, Uri uri, String[] projection, @Nullable Bundle queryArgs) {
2853 if (!ENABLE_CONTENT_SAMPLE) return;
2854 int samplePercent = samplePercentForDuration(durationMillis);
2855 if (samplePercent < 100) {
2856 synchronized (mRandom) {
2857 if (mRandom.nextInt(100) >= samplePercent) {
2858 return;
2859 }
2860 }
2861 }
2862
2863 // Ensure a non-null bundle.
2864 queryArgs = (queryArgs != null) ? queryArgs : Bundle.EMPTY;
2865
2866 StringBuilder projectionBuffer = new StringBuilder(100);
2867 if (projection != null) {
2868 for (int i = 0; i < projection.length; ++i) {
2869 // Note: not using a comma delimiter here, as the
2870 // multiple arguments to EventLog.writeEvent later
2871 // stringify with a comma delimiter, which would make
2872 // parsing uglier later.
2873 if (i != 0) projectionBuffer.append('/');
2874 projectionBuffer.append(projection[i]);
2875 }
2876 }
2877
2878 // ActivityThread.currentPackageName() only returns non-null if the
2879 // current thread is an application main thread. This parameter tells
2880 // us whether an event loop is blocked, and if so, which app it is.
2881 String blockingPackage = AppGlobals.getInitialPackage();
2882
2883 EventLog.writeEvent(
2884 EventLogTags.CONTENT_QUERY_SAMPLE,
2885 uri.toString(),
2886 projectionBuffer.toString(),
2887 queryArgs.getString(QUERY_ARG_SQL_SELECTION, ""),
2888 queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER, ""),
2889 durationMillis,
2890 blockingPackage != null ? blockingPackage : "",
2891 samplePercent);
2892 }
2893
2894 private void maybeLogUpdateToEventLog(
2895 long durationMillis, Uri uri, String operation, String selection) {
2896 if (!ENABLE_CONTENT_SAMPLE) return;
2897 int samplePercent = samplePercentForDuration(durationMillis);
2898 if (samplePercent < 100) {
2899 synchronized (mRandom) {
2900 if (mRandom.nextInt(100) >= samplePercent) {
2901 return;
2902 }
2903 }
2904 }
2905 String blockingPackage = AppGlobals.getInitialPackage();
2906 EventLog.writeEvent(
2907 EventLogTags.CONTENT_UPDATE_SAMPLE,
2908 uri.toString(),
2909 operation,
2910 selection != null ? selection : "",
2911 durationMillis,
2912 blockingPackage != null ? blockingPackage : "",
2913 samplePercent);
2914 }
2915
2916 private final class CursorWrapperInner extends CrossProcessCursorWrapper {
2917 private final IContentProvider mContentProvider;
2918 private final AtomicBoolean mProviderReleased = new AtomicBoolean();
2919
2920 private final CloseGuard mCloseGuard = CloseGuard.get();
2921
2922 CursorWrapperInner(Cursor cursor, IContentProvider contentProvider) {
2923 super(cursor);
2924 mContentProvider = contentProvider;
2925 mCloseGuard.open("close");
2926 }
2927
2928 @Override
2929 public void close() {
2930 mCloseGuard.close();
2931 super.close();
2932
2933 if (mProviderReleased.compareAndSet(false, true)) {
2934 ContentResolver.this.releaseProvider(mContentProvider);
2935 }
2936 }
2937
2938 @Override
2939 protected void finalize() throws Throwable {
2940 try {
2941 if (mCloseGuard != null) {
2942 mCloseGuard.warnIfOpen();
2943 }
2944
2945 close();
2946 } finally {
2947 super.finalize();
2948 }
2949 }
2950 }
2951
2952 private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
2953 private final IContentProvider mContentProvider;
2954 private final AtomicBoolean mProviderReleased = new AtomicBoolean();
2955
2956 ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
2957 super(pfd);
2958 mContentProvider = icp;
2959 }
2960
2961 @Override
2962 public void releaseResources() {
2963 if (mProviderReleased.compareAndSet(false, true)) {
2964 ContentResolver.this.releaseProvider(mContentProvider);
2965 }
2966 }
2967 }
2968
2969 /** @hide */
2970 public static final String CONTENT_SERVICE_NAME = "content";
2971
2972 /** @hide */
2973 public static IContentService getContentService() {
2974 if (sContentService != null) {
2975 return sContentService;
2976 }
2977 IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
2978 if (false) Log.v("ContentService", "default service binder = " + b);
2979 sContentService = IContentService.Stub.asInterface(b);
2980 if (false) Log.v("ContentService", "default service = " + sContentService);
2981 return sContentService;
2982 }
2983
2984 /** @hide */
2985 public String getPackageName() {
2986 return mPackageName;
2987 }
2988
2989 private static IContentService sContentService;
2990 private final Context mContext;
2991
2992 final String mPackageName;
2993 final int mTargetSdkVersion;
2994
2995 private static final String TAG = "ContentResolver";
2996
2997 /** @hide */
2998 public int resolveUserId(Uri uri) {
2999 return ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
3000 }
3001
3002 /** @hide */
3003 public Drawable getTypeDrawable(String mimeType) {
3004 return MimeIconUtils.loadMimeIcon(mContext, mimeType);
3005 }
3006
3007 /**
3008 * @hide
3009 */
3010 public static @Nullable Bundle createSqlQueryBundle(
3011 @Nullable String selection,
3012 @Nullable String[] selectionArgs,
3013 @Nullable String sortOrder) {
3014
3015 if (selection == null && selectionArgs == null && sortOrder == null) {
3016 return null;
3017 }
3018
3019 Bundle queryArgs = new Bundle();
3020 if (selection != null) {
3021 queryArgs.putString(QUERY_ARG_SQL_SELECTION, selection);
3022 }
3023 if (selectionArgs != null) {
3024 queryArgs.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
3025 }
3026 if (sortOrder != null) {
3027 queryArgs.putString(QUERY_ARG_SQL_SORT_ORDER, sortOrder);
3028 }
3029 return queryArgs;
3030 }
3031
3032 /**
3033 * Returns structured sort args formatted as an SQL sort clause.
3034 *
3035 * NOTE: Collator clauses are suitable for use with non text fields. We might
3036 * choose to omit any collation clause since we don't know the underlying
3037 * type of data to be collated. Imperical testing shows that sqlite3 doesn't
3038 * appear to care much about the presence of collate clauses in queries
3039 * when ordering by numeric fields. For this reason we include collate
3040 * clause unilaterally when {@link #QUERY_ARG_SORT_COLLATION} is present
3041 * in query args bundle.
3042 *
3043 * TODO: Would be nice to explicitly validate that colums referenced in
3044 * {@link #QUERY_ARG_SORT_COLUMNS} are present in the associated projection.
3045 *
3046 * @hide
3047 */
3048 public static String createSqlSortClause(Bundle queryArgs) {
3049 String[] columns = queryArgs.getStringArray(QUERY_ARG_SORT_COLUMNS);
3050 if (columns == null || columns.length == 0) {
3051 throw new IllegalArgumentException("Can't create sort clause without columns.");
3052 }
3053
3054 String query = TextUtils.join(", ", columns);
3055
3056 // Interpret PRIMARY and SECONDARY collation strength as no-case collation based
3057 // on their javadoc descriptions.
3058 int collation = queryArgs.getInt(
3059 ContentResolver.QUERY_ARG_SORT_COLLATION, java.text.Collator.IDENTICAL);
3060 if (collation == java.text.Collator.PRIMARY || collation == java.text.Collator.SECONDARY) {
3061 query += " COLLATE NOCASE";
3062 }
3063
3064 int sortDir = queryArgs.getInt(QUERY_ARG_SORT_DIRECTION, Integer.MIN_VALUE);
3065 if (sortDir != Integer.MIN_VALUE) {
3066 switch (sortDir) {
3067 case QUERY_SORT_DIRECTION_ASCENDING:
3068 query += " ASC";
3069 break;
3070 case QUERY_SORT_DIRECTION_DESCENDING:
3071 query += " DESC";
3072 break;
3073 default:
3074 throw new IllegalArgumentException("Unsupported sort direction value."
3075 + " See ContentResolver documentation for details.");
3076 }
3077 }
3078 return query;
3079 }
3080}