PageRenderTime 58ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/core/java/android/content/ContentResolver.java

https://github.com/PapaDocta/android_frameworks_base
Java | 1203 lines | 578 code | 66 blank | 559 comment | 105 complexity | 76fbd0cd680a55e6a1b1d1835c02e30c MD5 | raw file
  1. /*
  2. * Copyright (C) 2006 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package android.content;
  17. import android.accounts.Account;
  18. import android.app.ActivityThread;
  19. import android.content.pm.PackageManager.NameNotFoundException;
  20. import android.content.res.AssetFileDescriptor;
  21. import android.content.res.Resources;
  22. import android.database.ContentObserver;
  23. import android.database.Cursor;
  24. import android.database.CursorWrapper;
  25. import android.database.IContentObserver;
  26. import android.net.Uri;
  27. import android.os.Bundle;
  28. import android.os.IBinder;
  29. import android.os.ParcelFileDescriptor;
  30. import android.os.RemoteException;
  31. import android.os.ServiceManager;
  32. import android.os.SystemClock;
  33. import android.text.TextUtils;
  34. import android.util.Config;
  35. import android.util.EventLog;
  36. import android.util.Log;
  37. import java.io.File;
  38. import java.io.FileInputStream;
  39. import java.io.FileNotFoundException;
  40. import java.io.IOException;
  41. import java.io.InputStream;
  42. import java.io.OutputStream;
  43. import java.util.List;
  44. import java.util.Random;
  45. import java.util.ArrayList;
  46. /**
  47. * This class provides applications access to the content model.
  48. */
  49. public abstract class ContentResolver {
  50. /**
  51. * @deprecated instead use
  52. * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
  53. */
  54. @Deprecated
  55. public static final String SYNC_EXTRAS_ACCOUNT = "account";
  56. public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
  57. /**
  58. * @deprecated instead use
  59. * {@link #SYNC_EXTRAS_MANUAL}
  60. */
  61. @Deprecated
  62. public static final String SYNC_EXTRAS_FORCE = "force";
  63. /**
  64. * If this extra is set to true then the sync settings (like getSyncAutomatically())
  65. * are ignored by the sync scheduler.
  66. */
  67. public static final String SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings";
  68. /**
  69. * If this extra is set to true then any backoffs for the initial attempt (e.g. due to retries)
  70. * are ignored by the sync scheduler. If this request fails and gets rescheduled then the
  71. * retries will still honor the backoff.
  72. */
  73. public static final String SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff";
  74. /**
  75. * If this extra is set to true then the request will not be retried if it fails.
  76. */
  77. public static final String SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry";
  78. /**
  79. * Setting this extra is the equivalent of setting both {@link #SYNC_EXTRAS_IGNORE_SETTINGS}
  80. * and {@link #SYNC_EXTRAS_IGNORE_BACKOFF}
  81. */
  82. public static final String SYNC_EXTRAS_MANUAL = "force";
  83. public static final String SYNC_EXTRAS_UPLOAD = "upload";
  84. public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
  85. public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
  86. /**
  87. * Set by the SyncManager to request that the SyncAdapter initialize itself for
  88. * the given account/authority pair. One required initialization step is to
  89. * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
  90. * called with a >= 0 value. When this flag is set the SyncAdapter does not need to
  91. * do a full sync, though it is allowed to do so.
  92. */
  93. public static final String SYNC_EXTRAS_INITIALIZE = "initialize";
  94. public static final String SCHEME_CONTENT = "content";
  95. public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
  96. public static final String SCHEME_FILE = "file";
  97. /**
  98. * This is the Android platform's base MIME type for a content: URI
  99. * containing a Cursor of a single item. Applications should use this
  100. * as the base type along with their own sub-type of their content: URIs
  101. * that represent a particular item. For example, hypothetical IMAP email
  102. * client may have a URI
  103. * <code>content://com.company.provider.imap/inbox/1</code> for a particular
  104. * message in the inbox, whose MIME type would be reported as
  105. * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code>
  106. *
  107. * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}.
  108. */
  109. public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
  110. /**
  111. * This is the Android platform's base MIME type for a content: URI
  112. * containing a Cursor of zero or more items. Applications should use this
  113. * as the base type along with their own sub-type of their content: URIs
  114. * that represent a directory of items. For example, hypothetical IMAP email
  115. * client may have a URI
  116. * <code>content://com.company.provider.imap/inbox</code> for all of the
  117. * messages in its inbox, whose MIME type would be reported as
  118. * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code>
  119. *
  120. * <p>Note how the base MIME type varies between this and
  121. * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is
  122. * one single item or multiple items in the data set, while the sub-type
  123. * remains the same because in either case the data structure contained
  124. * in the cursor is the same.
  125. */
  126. public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
  127. /** @hide */
  128. public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
  129. /** @hide */
  130. public static final int SYNC_ERROR_AUTHENTICATION = 2;
  131. /** @hide */
  132. public static final int SYNC_ERROR_IO = 3;
  133. /** @hide */
  134. public static final int SYNC_ERROR_PARSE = 4;
  135. /** @hide */
  136. public static final int SYNC_ERROR_CONFLICT = 5;
  137. /** @hide */
  138. public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;
  139. /** @hide */
  140. public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;
  141. /** @hide */
  142. public static final int SYNC_ERROR_INTERNAL = 8;
  143. public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
  144. public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
  145. public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
  146. /** @hide */
  147. public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3;
  148. /** @hide */
  149. public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
  150. // Always log queries which take 500ms+; shorter queries are
  151. // sampled accordingly.
  152. private static final int SLOW_THRESHOLD_MILLIS = 500;
  153. private final Random mRandom = new Random(); // guarded by itself
  154. public ContentResolver(Context context) {
  155. mContext = context;
  156. }
  157. /** @hide */
  158. protected abstract IContentProvider acquireProvider(Context c, String name);
  159. /** @hide */
  160. public abstract boolean releaseProvider(IContentProvider icp);
  161. /**
  162. * Return the MIME type of the given content URL.
  163. *
  164. * @param url A Uri identifying content (either a list or specific type),
  165. * using the content:// scheme.
  166. * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
  167. */
  168. public final String getType(Uri url)
  169. {
  170. IContentProvider provider = acquireProvider(url);
  171. if (provider == null) {
  172. return null;
  173. }
  174. try {
  175. return provider.getType(url);
  176. } catch (RemoteException e) {
  177. return null;
  178. } catch (java.lang.Exception e) {
  179. return null;
  180. } finally {
  181. releaseProvider(provider);
  182. }
  183. }
  184. /**
  185. * <p>
  186. * Query the given URI, returning a {@link Cursor} over the result set.
  187. * </p>
  188. * <p>
  189. * For best performance, the caller should follow these guidelines:
  190. * <ul>
  191. * <li>Provide an explicit projection, to prevent
  192. * reading data from storage that aren't going to be used.</li>
  193. * <li>Use question mark parameter markers such as 'phone=?' instead of
  194. * explicit values in the {@code selection} parameter, so that queries
  195. * that differ only by those values will be recognized as the same
  196. * for caching purposes.</li>
  197. * </ul>
  198. * </p>
  199. *
  200. * @param uri The URI, using the content:// scheme, for the content to
  201. * retrieve.
  202. * @param projection A list of which columns to return. Passing null will
  203. * return all columns, which is inefficient.
  204. * @param selection A filter declaring which rows to return, formatted as an
  205. * SQL WHERE clause (excluding the WHERE itself). Passing null will
  206. * return all rows for the given URI.
  207. * @param selectionArgs You may include ?s in selection, which will be
  208. * replaced by the values from selectionArgs, in the order that they
  209. * appear in the selection. The values will be bound as Strings.
  210. * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
  211. * clause (excluding the ORDER BY itself). Passing null will use the
  212. * default sort order, which may be unordered.
  213. * @return A Cursor object, which is positioned before the first entry, or null
  214. * @see Cursor
  215. */
  216. public final Cursor query(Uri uri, String[] projection,
  217. String selection, String[] selectionArgs, String sortOrder) {
  218. IContentProvider provider = acquireProvider(uri);
  219. if (provider == null) {
  220. return null;
  221. }
  222. try {
  223. long startTime = SystemClock.uptimeMillis();
  224. Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder);
  225. if (qCursor == null) {
  226. releaseProvider(provider);
  227. return null;
  228. }
  229. // force query execution
  230. qCursor.getCount();
  231. long durationMillis = SystemClock.uptimeMillis() - startTime;
  232. maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
  233. // Wrap the cursor object into CursorWrapperInner object
  234. return new CursorWrapperInner(qCursor, provider);
  235. } catch (RemoteException e) {
  236. releaseProvider(provider);
  237. return null;
  238. } catch(RuntimeException e) {
  239. releaseProvider(provider);
  240. throw e;
  241. }
  242. }
  243. /**
  244. * Open a stream on to the content associated with a content URI. If there
  245. * is no data associated with the URI, FileNotFoundException is thrown.
  246. *
  247. * <h5>Accepts the following URI schemes:</h5>
  248. * <ul>
  249. * <li>content ({@link #SCHEME_CONTENT})</li>
  250. * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
  251. * <li>file ({@link #SCHEME_FILE})</li>
  252. * </ul>
  253. *
  254. * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
  255. * on these schemes.
  256. *
  257. * @param uri The desired URI.
  258. * @return InputStream
  259. * @throws FileNotFoundException if the provided URI could not be opened.
  260. * @see #openAssetFileDescriptor(Uri, String)
  261. */
  262. public final InputStream openInputStream(Uri uri)
  263. throws FileNotFoundException {
  264. String scheme = uri.getScheme();
  265. if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
  266. // Note: left here to avoid breaking compatibility. May be removed
  267. // with sufficient testing.
  268. OpenResourceIdResult r = getResourceId(uri);
  269. try {
  270. InputStream stream = r.r.openRawResource(r.id);
  271. return stream;
  272. } catch (Resources.NotFoundException ex) {
  273. throw new FileNotFoundException("Resource does not exist: " + uri);
  274. }
  275. } else if (SCHEME_FILE.equals(scheme)) {
  276. // Note: left here to avoid breaking compatibility. May be removed
  277. // with sufficient testing.
  278. return new FileInputStream(uri.getPath());
  279. } else {
  280. AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r");
  281. try {
  282. return fd != null ? fd.createInputStream() : null;
  283. } catch (IOException e) {
  284. throw new FileNotFoundException("Unable to create stream");
  285. }
  286. }
  287. }
  288. /**
  289. * Synonym for {@link #openOutputStream(Uri, String)
  290. * openOutputStream(uri, "w")}.
  291. * @throws FileNotFoundException if the provided URI could not be opened.
  292. */
  293. public final OutputStream openOutputStream(Uri uri)
  294. throws FileNotFoundException {
  295. return openOutputStream(uri, "w");
  296. }
  297. /**
  298. * Open a stream on to the content associated with a content URI. If there
  299. * is no data associated with the URI, FileNotFoundException is thrown.
  300. *
  301. * <h5>Accepts the following URI schemes:</h5>
  302. * <ul>
  303. * <li>content ({@link #SCHEME_CONTENT})</li>
  304. * <li>file ({@link #SCHEME_FILE})</li>
  305. * </ul>
  306. *
  307. * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
  308. * on these schemes.
  309. *
  310. * @param uri The desired URI.
  311. * @param mode May be "w", "wa", "rw", or "rwt".
  312. * @return OutputStream
  313. * @throws FileNotFoundException if the provided URI could not be opened.
  314. * @see #openAssetFileDescriptor(Uri, String)
  315. */
  316. public final OutputStream openOutputStream(Uri uri, String mode)
  317. throws FileNotFoundException {
  318. AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode);
  319. try {
  320. return fd != null ? fd.createOutputStream() : null;
  321. } catch (IOException e) {
  322. throw new FileNotFoundException("Unable to create stream");
  323. }
  324. }
  325. /**
  326. * Open a raw file descriptor to access data under a "content:" URI. This
  327. * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
  328. * underlying {@link ContentProvider#openFile}
  329. * ContentProvider.openFile()} method, so will <em>not</em> work with
  330. * providers that return sub-sections of files. If at all possible,
  331. * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
  332. * will receive a FileNotFoundException exception if the provider returns a
  333. * sub-section of a file.
  334. *
  335. * <h5>Accepts the following URI schemes:</h5>
  336. * <ul>
  337. * <li>content ({@link #SCHEME_CONTENT})</li>
  338. * <li>file ({@link #SCHEME_FILE})</li>
  339. * </ul>
  340. *
  341. * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
  342. * on these schemes.
  343. *
  344. * @param uri The desired URI to open.
  345. * @param mode The file mode to use, as per {@link ContentProvider#openFile
  346. * ContentProvider.openFile}.
  347. * @return Returns a new ParcelFileDescriptor pointing to the file. You
  348. * own this descriptor and are responsible for closing it when done.
  349. * @throws FileNotFoundException Throws FileNotFoundException of no
  350. * file exists under the URI or the mode is invalid.
  351. * @see #openAssetFileDescriptor(Uri, String)
  352. */
  353. public final ParcelFileDescriptor openFileDescriptor(Uri uri,
  354. String mode) throws FileNotFoundException {
  355. AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode);
  356. if (afd == null) {
  357. return null;
  358. }
  359. if (afd.getDeclaredLength() < 0) {
  360. // This is a full file!
  361. return afd.getParcelFileDescriptor();
  362. }
  363. // Client can't handle a sub-section of a file, so close what
  364. // we got and bail with an exception.
  365. try {
  366. afd.close();
  367. } catch (IOException e) {
  368. }
  369. throw new FileNotFoundException("Not a whole file");
  370. }
  371. /**
  372. * Open a raw file descriptor to access data under a "content:" URI. This
  373. * interacts with the underlying {@link ContentProvider#openAssetFile}
  374. * ContentProvider.openAssetFile()} method of the provider associated with the
  375. * given URI, to retrieve any file stored there.
  376. *
  377. * <h5>Accepts the following URI schemes:</h5>
  378. * <ul>
  379. * <li>content ({@link #SCHEME_CONTENT})</li>
  380. * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
  381. * <li>file ({@link #SCHEME_FILE})</li>
  382. * </ul>
  383. * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
  384. * <p>
  385. * A Uri object can be used to reference a resource in an APK file. The
  386. * Uri should be one of the following formats:
  387. * <ul>
  388. * <li><code>android.resource://package_name/id_number</code><br/>
  389. * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
  390. * For example <code>com.example.myapp</code><br/>
  391. * <code>id_number</code> is the int form of the ID.<br/>
  392. * The easiest way to construct this form is
  393. * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
  394. * </li>
  395. * <li><code>android.resource://package_name/type/name</code><br/>
  396. * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
  397. * For example <code>com.example.myapp</code><br/>
  398. * <code>type</code> is the string form of the resource type. For example, <code>raw</code>
  399. * or <code>drawable</code>.
  400. * <code>name</code> is the string form of the resource name. That is, whatever the file
  401. * name was in your res directory, without the type extension.
  402. * The easiest way to construct this form is
  403. * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
  404. * </li>
  405. * </ul>
  406. *
  407. * @param uri The desired URI to open.
  408. * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
  409. * ContentProvider.openAssetFile}.
  410. * @return Returns a new ParcelFileDescriptor pointing to the file. You
  411. * own this descriptor and are responsible for closing it when done.
  412. * @throws FileNotFoundException Throws FileNotFoundException of no
  413. * file exists under the URI or the mode is invalid.
  414. */
  415. public final AssetFileDescriptor openAssetFileDescriptor(Uri uri,
  416. String mode) throws FileNotFoundException {
  417. String scheme = uri.getScheme();
  418. if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
  419. if (!"r".equals(mode)) {
  420. throw new FileNotFoundException("Can't write resources: " + uri);
  421. }
  422. OpenResourceIdResult r = getResourceId(uri);
  423. try {
  424. return r.r.openRawResourceFd(r.id);
  425. } catch (Resources.NotFoundException ex) {
  426. throw new FileNotFoundException("Resource does not exist: " + uri);
  427. }
  428. } else if (SCHEME_FILE.equals(scheme)) {
  429. ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
  430. new File(uri.getPath()), modeToMode(uri, mode));
  431. return new AssetFileDescriptor(pfd, 0, -1);
  432. } else {
  433. IContentProvider provider = acquireProvider(uri);
  434. if (provider == null) {
  435. throw new FileNotFoundException("No content provider: " + uri);
  436. }
  437. try {
  438. AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
  439. if(fd == null) {
  440. releaseProvider(provider);
  441. return null;
  442. }
  443. ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
  444. fd.getParcelFileDescriptor(), provider);
  445. return new AssetFileDescriptor(pfd, fd.getStartOffset(),
  446. fd.getDeclaredLength());
  447. } catch (RemoteException e) {
  448. releaseProvider(provider);
  449. throw new FileNotFoundException("Dead content provider: " + uri);
  450. } catch (FileNotFoundException e) {
  451. releaseProvider(provider);
  452. throw e;
  453. } catch (RuntimeException e) {
  454. releaseProvider(provider);
  455. throw e;
  456. }
  457. }
  458. }
  459. /**
  460. * A resource identified by the {@link Resources} that contains it, and a resource id.
  461. *
  462. * @hide
  463. */
  464. public class OpenResourceIdResult {
  465. public Resources r;
  466. public int id;
  467. }
  468. /**
  469. * Resolves an android.resource URI to a {@link Resources} and a resource id.
  470. *
  471. * @hide
  472. */
  473. public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
  474. String authority = uri.getAuthority();
  475. Resources r;
  476. if (TextUtils.isEmpty(authority)) {
  477. throw new FileNotFoundException("No authority: " + uri);
  478. } else {
  479. try {
  480. r = mContext.getPackageManager().getResourcesForApplication(authority);
  481. } catch (NameNotFoundException ex) {
  482. throw new FileNotFoundException("No package found for authority: " + uri);
  483. }
  484. }
  485. List<String> path = uri.getPathSegments();
  486. if (path == null) {
  487. throw new FileNotFoundException("No path: " + uri);
  488. }
  489. int len = path.size();
  490. int id;
  491. if (len == 1) {
  492. try {
  493. id = Integer.parseInt(path.get(0));
  494. } catch (NumberFormatException e) {
  495. throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
  496. }
  497. } else if (len == 2) {
  498. id = r.getIdentifier(path.get(1), path.get(0), authority);
  499. } else {
  500. throw new FileNotFoundException("More than two path segments: " + uri);
  501. }
  502. if (id == 0) {
  503. throw new FileNotFoundException("No resource found for: " + uri);
  504. }
  505. OpenResourceIdResult res = new OpenResourceIdResult();
  506. res.r = r;
  507. res.id = id;
  508. return res;
  509. }
  510. /** @hide */
  511. static public int modeToMode(Uri uri, String mode) throws FileNotFoundException {
  512. int modeBits;
  513. if ("r".equals(mode)) {
  514. modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
  515. } else if ("w".equals(mode) || "wt".equals(mode)) {
  516. modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
  517. | ParcelFileDescriptor.MODE_CREATE
  518. | ParcelFileDescriptor.MODE_TRUNCATE;
  519. } else if ("wa".equals(mode)) {
  520. modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
  521. | ParcelFileDescriptor.MODE_CREATE
  522. | ParcelFileDescriptor.MODE_APPEND;
  523. } else if ("rw".equals(mode)) {
  524. modeBits = ParcelFileDescriptor.MODE_READ_WRITE
  525. | ParcelFileDescriptor.MODE_CREATE;
  526. } else if ("rwt".equals(mode)) {
  527. modeBits = ParcelFileDescriptor.MODE_READ_WRITE
  528. | ParcelFileDescriptor.MODE_CREATE
  529. | ParcelFileDescriptor.MODE_TRUNCATE;
  530. } else {
  531. throw new FileNotFoundException("Bad mode for " + uri + ": "
  532. + mode);
  533. }
  534. return modeBits;
  535. }
  536. /**
  537. * Inserts a row into a table at the given URL.
  538. *
  539. * If the content provider supports transactions the insertion will be atomic.
  540. *
  541. * @param url The URL of the table to insert into.
  542. * @param values The initial values for the newly inserted row. The key is the column name for
  543. * the field. Passing an empty ContentValues will create an empty row.
  544. * @return the URL of the newly created row.
  545. */
  546. public final Uri insert(Uri url, ContentValues values)
  547. {
  548. IContentProvider provider = acquireProvider(url);
  549. if (provider == null) {
  550. throw new IllegalArgumentException("Unknown URL " + url);
  551. }
  552. try {
  553. long startTime = SystemClock.uptimeMillis();
  554. Uri createdRow = provider.insert(url, values);
  555. long durationMillis = SystemClock.uptimeMillis() - startTime;
  556. maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
  557. return createdRow;
  558. } catch (RemoteException e) {
  559. return null;
  560. } finally {
  561. releaseProvider(provider);
  562. }
  563. }
  564. /**
  565. * Applies each of the {@link ContentProviderOperation} objects and returns an array
  566. * of their results. Passes through OperationApplicationException, which may be thrown
  567. * by the call to {@link ContentProviderOperation#apply}.
  568. * If all the applications succeed then a {@link ContentProviderResult} array with the
  569. * same number of elements as the operations will be returned. It is implementation-specific
  570. * how many, if any, operations will have been successfully applied if a call to
  571. * apply results in a {@link OperationApplicationException}.
  572. * @param authority the authority of the ContentProvider to which this batch should be applied
  573. * @param operations the operations to apply
  574. * @return the results of the applications
  575. * @throws OperationApplicationException thrown if an application fails.
  576. * See {@link ContentProviderOperation#apply} for more information.
  577. * @throws RemoteException thrown if a RemoteException is encountered while attempting
  578. * to communicate with a remote provider.
  579. */
  580. public ContentProviderResult[] applyBatch(String authority,
  581. ArrayList<ContentProviderOperation> operations)
  582. throws RemoteException, OperationApplicationException {
  583. ContentProviderClient provider = acquireContentProviderClient(authority);
  584. if (provider == null) {
  585. throw new IllegalArgumentException("Unknown authority " + authority);
  586. }
  587. try {
  588. return provider.applyBatch(operations);
  589. } finally {
  590. provider.release();
  591. }
  592. }
  593. /**
  594. * Inserts multiple rows into a table at the given URL.
  595. *
  596. * This function make no guarantees about the atomicity of the insertions.
  597. *
  598. * @param url The URL of the table to insert into.
  599. * @param values The initial values for the newly inserted rows. The key is the column name for
  600. * the field. Passing null will create an empty row.
  601. * @return the number of newly created rows.
  602. */
  603. public final int bulkInsert(Uri url, ContentValues[] values)
  604. {
  605. IContentProvider provider = acquireProvider(url);
  606. if (provider == null) {
  607. throw new IllegalArgumentException("Unknown URL " + url);
  608. }
  609. try {
  610. long startTime = SystemClock.uptimeMillis();
  611. int rowsCreated = provider.bulkInsert(url, values);
  612. long durationMillis = SystemClock.uptimeMillis() - startTime;
  613. maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
  614. return rowsCreated;
  615. } catch (RemoteException e) {
  616. return 0;
  617. } finally {
  618. releaseProvider(provider);
  619. }
  620. }
  621. /**
  622. * Deletes row(s) specified by a content URI.
  623. *
  624. * If the content provider supports transactions, the deletion will be atomic.
  625. *
  626. * @param url The URL of the row to delete.
  627. * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause
  628. (excluding the WHERE itself).
  629. * @return The number of rows deleted.
  630. */
  631. public final int delete(Uri url, String where, String[] selectionArgs)
  632. {
  633. IContentProvider provider = acquireProvider(url);
  634. if (provider == null) {
  635. throw new IllegalArgumentException("Unknown URL " + url);
  636. }
  637. try {
  638. long startTime = SystemClock.uptimeMillis();
  639. int rowsDeleted = provider.delete(url, where, selectionArgs);
  640. long durationMillis = SystemClock.uptimeMillis() - startTime;
  641. maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
  642. return rowsDeleted;
  643. } catch (RemoteException e) {
  644. return -1;
  645. } finally {
  646. releaseProvider(provider);
  647. }
  648. }
  649. /**
  650. * Update row(s) in a content URI.
  651. *
  652. * If the content provider supports transactions the update will be atomic.
  653. *
  654. * @param uri The URI to modify.
  655. * @param values The new field values. The key is the column name for the field.
  656. A null value will remove an existing field value.
  657. * @param where A filter to apply to rows before updating, formatted as an SQL WHERE clause
  658. (excluding the WHERE itself).
  659. * @return The number of rows updated.
  660. * @throws NullPointerException if uri or values are null
  661. */
  662. public final int update(Uri uri, ContentValues values, String where,
  663. String[] selectionArgs) {
  664. IContentProvider provider = acquireProvider(uri);
  665. if (provider == null) {
  666. throw new IllegalArgumentException("Unknown URI " + uri);
  667. }
  668. try {
  669. long startTime = SystemClock.uptimeMillis();
  670. int rowsUpdated = provider.update(uri, values, where, selectionArgs);
  671. long durationMillis = SystemClock.uptimeMillis() - startTime;
  672. maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
  673. return rowsUpdated;
  674. } catch (RemoteException e) {
  675. return -1;
  676. } finally {
  677. releaseProvider(provider);
  678. }
  679. }
  680. /**
  681. * Returns the content provider for the given content URI..
  682. *
  683. * @param uri The URI to a content provider
  684. * @return The ContentProvider for the given URI, or null if no content provider is found.
  685. * @hide
  686. */
  687. public final IContentProvider acquireProvider(Uri uri)
  688. {
  689. if (!SCHEME_CONTENT.equals(uri.getScheme())) {
  690. return null;
  691. }
  692. String auth = uri.getAuthority();
  693. if (auth != null) {
  694. return acquireProvider(mContext, uri.getAuthority());
  695. }
  696. return null;
  697. }
  698. /**
  699. * @hide
  700. */
  701. public final IContentProvider acquireProvider(String name) {
  702. if (name == null) {
  703. return null;
  704. }
  705. return acquireProvider(mContext, name);
  706. }
  707. /**
  708. * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
  709. * that services the content at uri, starting the provider if necessary. Returns
  710. * null if there is no provider associated wih the uri. The caller must indicate that they are
  711. * done with the provider by calling {@link ContentProviderClient#release} which will allow
  712. * the system to release the provider it it determines that there is no other reason for
  713. * keeping it active.
  714. * @param uri specifies which provider should be acquired
  715. * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
  716. * that services the content at uri or null if there isn't one.
  717. */
  718. public final ContentProviderClient acquireContentProviderClient(Uri uri) {
  719. IContentProvider provider = acquireProvider(uri);
  720. if (provider != null) {
  721. return new ContentProviderClient(this, provider);
  722. }
  723. return null;
  724. }
  725. /**
  726. * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
  727. * with the authority of name, starting the provider if necessary. Returns
  728. * null if there is no provider associated wih the uri. The caller must indicate that they are
  729. * done with the provider by calling {@link ContentProviderClient#release} which will allow
  730. * the system to release the provider it it determines that there is no other reason for
  731. * keeping it active.
  732. * @param name specifies which provider should be acquired
  733. * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
  734. * with the authority of name or null if there isn't one.
  735. */
  736. public final ContentProviderClient acquireContentProviderClient(String name) {
  737. IContentProvider provider = acquireProvider(name);
  738. if (provider != null) {
  739. return new ContentProviderClient(this, provider);
  740. }
  741. return null;
  742. }
  743. /**
  744. * Register an observer class that gets callbacks when data identified by a
  745. * given content URI changes.
  746. *
  747. * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
  748. * for a whole class of content.
  749. * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code>
  750. * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI
  751. * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values
  752. * at or below the specified URI will also trigger a match.
  753. * @param observer The object that receives callbacks when changes occur.
  754. * @see #unregisterContentObserver
  755. */
  756. public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
  757. ContentObserver observer)
  758. {
  759. try {
  760. getContentService().registerContentObserver(uri, notifyForDescendents,
  761. observer.getContentObserver());
  762. } catch (RemoteException e) {
  763. }
  764. }
  765. /**
  766. * Unregisters a change observer.
  767. *
  768. * @param observer The previously registered observer that is no longer needed.
  769. * @see #registerContentObserver
  770. */
  771. public final void unregisterContentObserver(ContentObserver observer) {
  772. try {
  773. IContentObserver contentObserver = observer.releaseContentObserver();
  774. if (contentObserver != null) {
  775. getContentService().unregisterContentObserver(
  776. contentObserver);
  777. }
  778. } catch (RemoteException e) {
  779. }
  780. }
  781. /**
  782. * Notify registered observers that a row was updated.
  783. * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
  784. * By default, CursorAdapter objects will get this notification.
  785. *
  786. * @param uri
  787. * @param observer The observer that originated the change, may be <code>null</null>
  788. */
  789. public void notifyChange(Uri uri, ContentObserver observer) {
  790. notifyChange(uri, observer, true /* sync to network */);
  791. }
  792. /**
  793. * Notify registered observers that a row was updated.
  794. * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
  795. * By default, CursorAdapter objects will get this notification.
  796. *
  797. * @param uri
  798. * @param observer The observer that originated the change, may be <code>null</null>
  799. * @param syncToNetwork If true, attempt to sync the change to the network.
  800. */
  801. public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
  802. try {
  803. getContentService().notifyChange(
  804. uri, observer == null ? null : observer.getContentObserver(),
  805. observer != null && observer.deliverSelfNotifications(), syncToNetwork);
  806. } catch (RemoteException e) {
  807. }
  808. }
  809. /**
  810. * Start an asynchronous sync operation. If you want to monitor the progress
  811. * of the sync you may register a SyncObserver. Only values of the following
  812. * types may be used in the extras bundle:
  813. * <ul>
  814. * <li>Integer</li>
  815. * <li>Long</li>
  816. * <li>Boolean</li>
  817. * <li>Float</li>
  818. * <li>Double</li>
  819. * <li>String</li>
  820. * </ul>
  821. *
  822. * @param uri the uri of the provider to sync or null to sync all providers.
  823. * @param extras any extras to pass to the SyncAdapter.
  824. * @deprecated instead use
  825. * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
  826. */
  827. @Deprecated
  828. public void startSync(Uri uri, Bundle extras) {
  829. Account account = null;
  830. if (extras != null) {
  831. String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
  832. if (!TextUtils.isEmpty(accountName)) {
  833. account = new Account(accountName, "com.google");
  834. }
  835. extras.remove(SYNC_EXTRAS_ACCOUNT);
  836. }
  837. requestSync(account, uri != null ? uri.getAuthority() : null, extras);
  838. }
  839. /**
  840. * Start an asynchronous sync operation. If you want to monitor the progress
  841. * of the sync you may register a SyncObserver. Only values of the following
  842. * types may be used in the extras bundle:
  843. * <ul>
  844. * <li>Integer</li>
  845. * <li>Long</li>
  846. * <li>Boolean</li>
  847. * <li>Float</li>
  848. * <li>Double</li>
  849. * <li>String</li>
  850. * </ul>
  851. *
  852. * @param account which account should be synced
  853. * @param authority which authority should be synced
  854. * @param extras any extras to pass to the SyncAdapter.
  855. */
  856. public static void requestSync(Account account, String authority, Bundle extras) {
  857. validateSyncExtrasBundle(extras);
  858. try {
  859. getContentService().requestSync(account, authority, extras);
  860. } catch (RemoteException e) {
  861. }
  862. }
  863. /**
  864. * Check that only values of the following types are in the Bundle:
  865. * <ul>
  866. * <li>Integer</li>
  867. * <li>Long</li>
  868. * <li>Boolean</li>
  869. * <li>Float</li>
  870. * <li>Double</li>
  871. * <li>String</li>
  872. * <li>Account</li>
  873. * <li>null</li>
  874. * </ul>
  875. * @param extras the Bundle to check
  876. */
  877. public static void validateSyncExtrasBundle(Bundle extras) {
  878. try {
  879. for (String key : extras.keySet()) {
  880. Object value = extras.get(key);
  881. if (value == null) continue;
  882. if (value instanceof Long) continue;
  883. if (value instanceof Integer) continue;
  884. if (value instanceof Boolean) continue;
  885. if (value instanceof Float) continue;
  886. if (value instanceof Double) continue;
  887. if (value instanceof String) continue;
  888. if (value instanceof Account) continue;
  889. throw new IllegalArgumentException("unexpected value type: "
  890. + value.getClass().getName());
  891. }
  892. } catch (IllegalArgumentException e) {
  893. throw e;
  894. } catch (RuntimeException exc) {
  895. throw new IllegalArgumentException("error unparceling Bundle", exc);
  896. }
  897. }
  898. /**
  899. * Cancel any active or pending syncs that match the Uri. If the uri is null then
  900. * all syncs will be canceled.
  901. *
  902. * @param uri the uri of the provider to sync or null to sync all providers.
  903. * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)}
  904. */
  905. @Deprecated
  906. public void cancelSync(Uri uri) {
  907. cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null);
  908. }
  909. /**
  910. * Cancel any active or pending syncs that match account and authority. The account and
  911. * authority can each independently be set to null, which means that syncs with any account
  912. * or authority, respectively, will match.
  913. *
  914. * @param account filters the syncs that match by this account
  915. * @param authority filters the syncs that match by this authority
  916. */
  917. public static void cancelSync(Account account, String authority) {
  918. try {
  919. getContentService().cancelSync(account, authority);
  920. } catch (RemoteException e) {
  921. }
  922. }
  923. /**
  924. * Get information about the SyncAdapters that are known to the system.
  925. * @return an array of SyncAdapters that have registered with the system
  926. */
  927. public static SyncAdapterType[] getSyncAdapterTypes() {
  928. try {
  929. return getContentService().getSyncAdapterTypes();
  930. } catch (RemoteException e) {
  931. throw new RuntimeException("the ContentService should always be reachable", e);
  932. }
  933. }
  934. /**
  935. * Check if the provider should be synced when a network tickle is received
  936. *
  937. * @param account the account whose setting we are querying
  938. * @param authority the provider whose setting we are querying
  939. * @return true if the provider should be synced when a network tickle is received
  940. */
  941. public static boolean getSyncAutomatically(Account account, String authority) {
  942. try {
  943. return getContentService().getSyncAutomatically(account, authority);
  944. } catch (RemoteException e) {
  945. throw new RuntimeException("the ContentService should always be reachable", e);
  946. }
  947. }
  948. /**
  949. * Set whether or not the provider is synced when it receives a network tickle.
  950. *
  951. * @param account the account whose setting we are querying
  952. * @param authority the provider whose behavior is being controlled
  953. * @param sync true if the provider should be synced when tickles are received for it
  954. */
  955. public static void setSyncAutomatically(Account account, String authority, boolean sync) {
  956. try {
  957. getContentService().setSyncAutomatically(account, authority, sync);
  958. } catch (RemoteException e) {
  959. // exception ignored; if this is thrown then it means the runtime is in the midst of
  960. // being restarted
  961. }
  962. }
  963. /**
  964. * Specifies that a sync should be requested with the specified the account, authority,
  965. * and extras at the given frequency. If there is already another periodic sync scheduled
  966. * with the account, authority and extras then a new periodic sync won't be added, instead
  967. * the frequency of the previous one will be updated.
  968. * <p>
  969. * These periodic syncs honor the "syncAutomatically" and "masterSyncAutomatically" settings.
  970. * Although these sync are scheduled at the specified frequency, it may take longer for it to
  971. * actually be started if other syncs are ahead of it in the sync operation queue. This means
  972. * that the actual start time may drift.
  973. * <p>
  974. * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY},
  975. * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS},
  976. * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE},
  977. * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true.
  978. * If any are supplied then an {@link IllegalArgumentException} will be thrown.
  979. *
  980. * @param account the account to specify in the sync
  981. * @param authority the provider to specify in the sync request
  982. * @param extras extra parameters to go along with the sync request
  983. * @param pollFrequency how frequently the sync should be performed, in seconds.
  984. * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
  985. * are null.
  986. */
  987. public static void addPeriodicSync(Account account, String authority, Bundle extras,
  988. long pollFrequency) {
  989. validateSyncExtrasBundle(extras);
  990. if (account == null) {
  991. throw new IllegalArgumentException("account must not be null");
  992. }
  993. if (authority == null) {
  994. throw new IllegalArgumentException("authority must not be null");
  995. }
  996. if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false)
  997. || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false)
  998. || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false)
  999. || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false)
  1000. || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false)
  1001. || extras.getBoolean(SYNC_EXTRAS_FORCE, false)
  1002. || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) {
  1003. throw new IllegalArgumentException("illegal extras were set");
  1004. }
  1005. try {
  1006. getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
  1007. } catch (RemoteException e) {
  1008. // exception ignored; if this is thrown then it means the runtime is in the midst of
  1009. // being restarted
  1010. }
  1011. }
  1012. /**
  1013. * Remove a periodic sync. Has no affect if account, authority and extras don't match
  1014. * an existing periodic sync.
  1015. *
  1016. * @param account the account of the periodic sync to remove
  1017. * @param authority the provider of the periodic sync to remove
  1018. * @param extras the extras of the periodic sync to remove
  1019. */
  1020. public static void removePeriodicSync(Account account, String authority, Bundle extras) {
  1021. validateSyncExtrasBundle(extras);
  1022. if (account == null) {
  1023. throw new IllegalArgumentException("account must not be null");
  1024. }
  1025. if (authority == null) {
  1026. throw new IllegalArgumentException("authority must not be null");
  1027. }
  1028. try {
  1029. getContentService().removePeriodicSync(account, authority, extras);
  1030. } catch (RemoteException e) {
  1031. throw new RuntimeException("the ContentService should always be reachable", e);
  1032. }
  1033. }
  1034. /**
  1035. * Get the list of information about the periodic syncs for the given account and authority.
  1036. *
  1037. * @param account the account whose periodic syncs we are querying
  1038. * @param authority the provider whose periodic syncs we are querying
  1039. * @return a list of PeriodicSync objects. This list may be empty but will never be null.
  1040. */
  1041. public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) {
  1042. if (account == null) {
  1043. throw new IllegalArgumentException("account must not be null");
  1044. }
  1045. if (authority == null) {
  1046. throw new IllegalArgumentException("authority must not be null");
  1047. }
  1048. try {
  1049. return getContentService().getPeriodicSyncs(account, authority);
  1050. } catch (RemoteException e) {
  1051. throw new RuntimeException("the ContentService should always be reachable", e);
  1052. }
  1053. }
  1054. /**
  1055. * Check if this account/provider is syncable.
  1056. * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
  1057. */
  1058. public static int getIsSyncable(Account account, String authority) {
  1059. try {
  1060. return getContentService().getIsSyncable(account, authority);
  1061. } catch (RemoteException e) {
  1062. throw new RuntimeException("the ContentService should always be reachable", e);
  1063. }
  1064. }
  1065. /**
  1066. * Set whether this account/provider is syncable.
  1067. * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown
  1068. */
  1069. public static void setIsSyncable(Account account, String authority, int syncable) {
  1070. try {
  1071. getContentService().setIsSyncable(account, authority, syncable);
  1072. } catch (RemoteException e) {
  1073. // exception ignored; if this is thrown then it means the runtime is in the midst of
  1074. // being restarted
  1075. }
  1076. }
  1077. /**
  1078. * Gets the master auto-sync setting that applies to all the providers and accounts.
  1079. * If this is false then the per-provider auto-sync setting is ignored.
  1080. *
  1081. * @return the master auto-sync setting that applies to all the providers and accounts
  1082. */
  1083. public static boolean getMasterSyncAutomatically() {
  1084. try {
  1085. return getContentService().getMasterSyncAutomatically();
  1086. } catch (RemoteException e) {
  1087. throw new RuntimeException("the ContentService should always be reachable", e);
  1088. }
  1089. }
  1090. /**
  1091. * Sets the master auto-sync setting that applies to all the providers and accounts.
  1092. * If this is false then the per-provider auto-sync setting is ignored.
  1093. *
  1094. * @param sync the master auto-sync setting that applies to all the providers and accounts
  1095. */
  1096. public static void setMasterSyncAutomatically(boolean sync) {
  1097. try {
  1098. getContentService().setMasterSyncAutomatically(sync);
  1099. } catch (RemoteException e) {
  1100. // exception ignored; if this is thrown then it means the runtime is in the midst of
  1101. // being restarted
  1102. }
  1103. }
  1104. /**
  1105. * Returns true if there is currently a sync operation for the given
  1106. * account or authority in the pending list, or actively being processed.
  1107. * @param account the account whose setting we are querying
  1108. * @param authority the provider whose behavior is being queried
  1109. * @return true if a sync is active for the given account or authority.
  1110. */
  1111. public static boolean isSyncActive(Account account, String authority) {
  1112. try {
  1113. return getContentService().isSyncActive(account, authority);
  1114. } catch (RemoteException e) {
  1115. throw new RuntimeException("the ContentService should always be reachable", e);
  1116. }
  1117. }
  1118. /**
  1119. * If a sync is active returns the information about it, otherwise returns false.
  1120. * @return the SyncInfo for the currently active sync or null if one is not active.
  1121. */
  1122. public static SyncInfo getCurrentSync() {
  1123. try {
  1124. return getContentService().getCurrentSync();
  1125. } catch (RemoteException e) {
  1126. throw new RuntimeException("the ContentService should always be reachable", e);
  1127. }
  1128. }
  1129. /**
  1130. * Returns the status that matches the authority.
  1131. * @param account the account whose setting we are querying
  1132. * @param authority the provider whose behavior is being queried
  1133. * @return the SyncStatusInfo for the authority, or null if none exists
  1134. * @hide
  1135. */
  1136. public static SyncStatusInfo getSyncStatus(Account account, String authority) {
  1137. try {