blob: 2d03e7c961470ea08de34c8df9217292adfab3e0 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Brad Fitzpatricka63730d2010-02-07 22:25:34 -080019import android.accounts.Account;
Dianne Hackborncca1f0e2010-09-26 18:34:53 -070020import android.app.ActivityManagerNative;
Brad Fitzpatricka63730d2010-02-07 22:25:34 -080021import android.app.ActivityThread;
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -070022import android.app.AppGlobals;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.pm.PackageManager.NameNotFoundException;
24import android.content.res.AssetFileDescriptor;
25import android.content.res.Resources;
26import android.database.ContentObserver;
27import android.database.Cursor;
28import android.database.CursorWrapper;
29import android.database.IContentObserver;
30import android.net.Uri;
31import android.os.Bundle;
Dianne Hackborn231cc602009-04-27 17:10:36 -070032import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.os.ParcelFileDescriptor;
34import android.os.RemoteException;
Dianne Hackborn231cc602009-04-27 17:10:36 -070035import android.os.ServiceManager;
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -080036import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.text.TextUtils;
Dianne Hackborn231cc602009-04-27 17:10:36 -070038import android.util.Config;
Brad Fitzpatricka63730d2010-02-07 22:25:34 -080039import android.util.EventLog;
Dianne Hackborn231cc602009-04-27 17:10:36 -070040import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
42import java.io.File;
43import java.io.FileInputStream;
44import java.io.FileNotFoundException;
45import java.io.IOException;
46import java.io.InputStream;
47import java.io.OutputStream;
Gilles Debunne03f02922010-06-09 14:11:45 -070048import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.util.List;
Brad Fitzpatricka63730d2010-02-07 22:25:34 -080050import java.util.Random;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051
52
53/**
54 * This class provides applications access to the content model.
55 */
56public abstract class ContentResolver {
Fred Quintanaac9385e2009-06-22 18:00:59 -070057 /**
58 * @deprecated instead use
59 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
60 */
Fred Quintana4a6679b2009-08-17 13:05:39 -070061 @Deprecated
Fred Quintanaac9385e2009-06-22 18:00:59 -070062 public static final String SYNC_EXTRAS_ACCOUNT = "account";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
Fred Quintanaac9385e2009-06-22 18:00:59 -070064 /**
65 * @deprecated instead use
66 * {@link #SYNC_EXTRAS_MANUAL}
67 */
Fred Quintana4a6679b2009-08-17 13:05:39 -070068 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 public static final String SYNC_EXTRAS_FORCE = "force";
Fred Quintana53bd2522010-02-05 15:28:12 -080070
71 /**
72 * If this extra is set to true then the sync settings (like getSyncAutomatically())
73 * are ignored by the sync scheduler.
74 */
75 public static final String SYNC_EXTRAS_IGNORE_SETTINGS = "ignore_settings";
76
77 /**
78 * If this extra is set to true then any backoffs for the initial attempt (e.g. due to retries)
79 * are ignored by the sync scheduler. If this request fails and gets rescheduled then the
80 * retries will still honor the backoff.
81 */
82 public static final String SYNC_EXTRAS_IGNORE_BACKOFF = "ignore_backoff";
83
84 /**
85 * If this extra is set to true then the request will not be retried if it fails.
86 */
87 public static final String SYNC_EXTRAS_DO_NOT_RETRY = "do_not_retry";
88
89 /**
90 * Setting this extra is the equivalent of setting both {@link #SYNC_EXTRAS_IGNORE_SETTINGS}
91 * and {@link #SYNC_EXTRAS_IGNORE_BACKOFF}
92 */
Fred Quintanaac9385e2009-06-22 18:00:59 -070093 public static final String SYNC_EXTRAS_MANUAL = "force";
Fred Quintana53bd2522010-02-05 15:28:12 -080094
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 public static final String SYNC_EXTRAS_UPLOAD = "upload";
96 public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
97 public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
98
Fred Quintana4a6679b2009-08-17 13:05:39 -070099 /**
100 * Set by the SyncManager to request that the SyncAdapter initialize itself for
101 * the given account/authority pair. One required initialization step is to
102 * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
103 * called with a >= 0 value. When this flag is set the SyncAdapter does not need to
104 * do a full sync, though it is allowed to do so.
105 */
106 public static final String SYNC_EXTRAS_INITIALIZE = "initialize";
107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 public static final String SCHEME_CONTENT = "content";
109 public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
110 public static final String SCHEME_FILE = "file";
111
112 /**
113 * This is the Android platform's base MIME type for a content: URI
114 * containing a Cursor of a single item. Applications should use this
115 * as the base type along with their own sub-type of their content: URIs
116 * that represent a particular item. For example, hypothetical IMAP email
117 * client may have a URI
118 * <code>content://com.company.provider.imap/inbox/1</code> for a particular
119 * message in the inbox, whose MIME type would be reported as
120 * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code>
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800121 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}.
123 */
124 public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800125
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 /**
127 * This is the Android platform's base MIME type for a content: URI
128 * containing a Cursor of zero or more items. Applications should use this
129 * as the base type along with their own sub-type of their content: URIs
130 * that represent a directory of items. For example, hypothetical IMAP email
131 * client may have a URI
132 * <code>content://com.company.provider.imap/inbox</code> for all of the
133 * messages in its inbox, whose MIME type would be reported as
134 * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code>
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800135 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 * <p>Note how the base MIME type varies between this and
137 * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is
138 * one single item or multiple items in the data set, while the sub-type
139 * remains the same because in either case the data structure contained
140 * in the cursor is the same.
141 */
142 public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
Fred Quintanaac9385e2009-06-22 18:00:59 -0700143
144 /** @hide */
145 public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
146 /** @hide */
147 public static final int SYNC_ERROR_AUTHENTICATION = 2;
148 /** @hide */
149 public static final int SYNC_ERROR_IO = 3;
150 /** @hide */
151 public static final int SYNC_ERROR_PARSE = 4;
152 /** @hide */
153 public static final int SYNC_ERROR_CONFLICT = 5;
154 /** @hide */
155 public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;
156 /** @hide */
157 public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;
158 /** @hide */
159 public static final int SYNC_ERROR_INTERNAL = 8;
160
Fred Quintanaac9385e2009-06-22 18:00:59 -0700161 public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700162 public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
Fred Quintanaac9385e2009-06-22 18:00:59 -0700163 public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
164 /** @hide */
165 public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3;
166 /** @hide */
167 public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
168
Brad Fitzpatrick25880962010-02-22 15:17:49 -0800169 // Always log queries which take 500ms+; shorter queries are
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800170 // sampled accordingly.
Brad Fitzpatrick25880962010-02-22 15:17:49 -0800171 private static final int SLOW_THRESHOLD_MILLIS = 500;
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800172 private final Random mRandom = new Random(); // guarded by itself
173
Dianne Hackborn231cc602009-04-27 17:10:36 -0700174 public ContentResolver(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 mContext = context;
176 }
177
178 /** @hide */
179 protected abstract IContentProvider acquireProvider(Context c, String name);
Dianne Hackborncca1f0e2010-09-26 18:34:53 -0700180 /** Providing a default implementation of this, to avoid having to change
181 * a lot of other things, but implementations of ContentResolver should
182 * implement it. @hide */
183 protected IContentProvider acquireExistingProvider(Context c, String name) {
184 return acquireProvider(c, name);
185 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 /** @hide */
187 public abstract boolean releaseProvider(IContentProvider icp);
188
189 /**
190 * Return the MIME type of the given content URL.
191 *
192 * @param url A Uri identifying content (either a list or specific type),
193 * using the content:// scheme.
194 * @return A MIME type for the content, or null if the URL is invalid or the type is unknown
195 */
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700196 public final String getType(Uri url) {
Dianne Hackborncca1f0e2010-09-26 18:34:53 -0700197 IContentProvider provider = acquireExistingProvider(url);
198 if (provider != null) {
199 try {
200 return provider.getType(url);
201 } catch (RemoteException e) {
202 return null;
203 } catch (java.lang.Exception e) {
Ola Olsson145e6c42010-12-20 16:45:35 +0100204 Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
Dianne Hackborncca1f0e2010-09-26 18:34:53 -0700205 return null;
206 } finally {
207 releaseProvider(provider);
208 }
209 }
210
211 if (!SCHEME_CONTENT.equals(url.getScheme())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 return null;
213 }
Dianne Hackborncca1f0e2010-09-26 18:34:53 -0700214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 try {
Dianne Hackborncca1f0e2010-09-26 18:34:53 -0700216 String type = ActivityManagerNative.getDefault().getProviderMimeType(url);
217 return type;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 } catch (RemoteException e) {
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800219 // Arbitrary and not worth documenting, as Activity
220 // Manager will kill this process shortly anyway.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 return null;
Ola Olsson145e6c42010-12-20 16:45:35 +0100222 } catch (java.lang.Exception e) {
223 Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
224 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 }
226 }
227
228 /**
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700229 * Query for the possible MIME types for the representations the given
230 * content URL can be returned when opened as as stream with
231 * {@link #openTypedAssetFileDescriptor}. Note that the types here are
232 * not necessarily a superset of the type returned by {@link #getType} --
233 * many content providers can not return a raw stream for the structured
234 * data that they contain.
235 *
236 * @param url A Uri identifying content (either a list or specific type),
237 * using the content:// scheme.
238 * @param mimeTypeFilter The desired MIME type. This may be a pattern,
239 * such as *\/*, to query for all available MIME types that match the
240 * pattern.
241 * @return Returns an array of MIME type strings for all availablle
242 * data streams that match the given mimeTypeFilter. If there are none,
243 * null is returned.
244 */
245 public String[] getStreamTypes(Uri url, String mimeTypeFilter) {
246 IContentProvider provider = acquireProvider(url);
247 if (provider == null) {
248 return null;
249 }
Dianne Hackborn64bbbb42010-09-27 20:25:20 -0700250
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700251 try {
252 return provider.getStreamTypes(url, mimeTypeFilter);
253 } catch (RemoteException e) {
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800254 // Arbitrary and not worth documenting, as Activity
255 // Manager will kill this process shortly anyway.
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700256 return null;
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700257 } finally {
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800258 releaseProvider(provider);
259 }
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700260 }
261
262 /**
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800263 * <p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 * Query the given URI, returning a {@link Cursor} over the result set.
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800265 * </p>
266 * <p>
267 * For best performance, the caller should follow these guidelines:
268 * <ul>
269 * <li>Provide an explicit projection, to prevent
270 * reading data from storage that aren't going to be used.</li>
271 * <li>Use question mark parameter markers such as 'phone=?' instead of
272 * explicit values in the {@code selection} parameter, so that queries
273 * that differ only by those values will be recognized as the same
274 * for caching purposes.</li>
275 * </ul>
276 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 *
278 * @param uri The URI, using the content:// scheme, for the content to
279 * retrieve.
280 * @param projection A list of which columns to return. Passing null will
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800281 * return all columns, which is inefficient.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 * @param selection A filter declaring which rows to return, formatted as an
283 * SQL WHERE clause (excluding the WHERE itself). Passing null will
284 * return all rows for the given URI.
285 * @param selectionArgs You may include ?s in selection, which will be
286 * replaced by the values from selectionArgs, in the order that they
287 * appear in the selection. The values will be bound as Strings.
288 * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
289 * clause (excluding the ORDER BY itself). Passing null will use the
290 * default sort order, which may be unordered.
291 * @return A Cursor object, which is positioned before the first entry, or null
292 * @see Cursor
293 */
294 public final Cursor query(Uri uri, String[] projection,
295 String selection, String[] selectionArgs, String sortOrder) {
296 IContentProvider provider = acquireProvider(uri);
297 if (provider == null) {
298 return null;
299 }
300 try {
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800301 long startTime = SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder);
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800303 if (qCursor == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 releaseProvider(provider);
305 return null;
306 }
Vasu Nori020e5342010-04-28 14:22:38 -0700307 // force query execution
308 qCursor.getCount();
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800309 long durationMillis = SystemClock.uptimeMillis() - startTime;
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800310 maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
311 // Wrap the cursor object into CursorWrapperInner object
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 return new CursorWrapperInner(qCursor, provider);
313 } catch (RemoteException e) {
314 releaseProvider(provider);
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800315
316 // Arbitrary and not worth documenting, as Activity
317 // Manager will kill this process shortly anyway.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 return null;
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800319 } catch (RuntimeException e) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 releaseProvider(provider);
321 throw e;
322 }
323 }
324
Fred Quintana89437372009-05-15 15:10:40 -0700325 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 * Open a stream on to the content associated with a content URI. If there
327 * is no data associated with the URI, FileNotFoundException is thrown.
328 *
329 * <h5>Accepts the following URI schemes:</h5>
330 * <ul>
331 * <li>content ({@link #SCHEME_CONTENT})</li>
332 * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
333 * <li>file ({@link #SCHEME_FILE})</li>
334 * </ul>
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800335 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
337 * on these schemes.
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800338 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 * @param uri The desired URI.
340 * @return InputStream
341 * @throws FileNotFoundException if the provided URI could not be opened.
342 * @see #openAssetFileDescriptor(Uri, String)
343 */
344 public final InputStream openInputStream(Uri uri)
345 throws FileNotFoundException {
346 String scheme = uri.getScheme();
347 if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
348 // Note: left here to avoid breaking compatibility. May be removed
349 // with sufficient testing.
350 OpenResourceIdResult r = getResourceId(uri);
351 try {
352 InputStream stream = r.r.openRawResource(r.id);
353 return stream;
354 } catch (Resources.NotFoundException ex) {
355 throw new FileNotFoundException("Resource does not exist: " + uri);
356 }
357 } else if (SCHEME_FILE.equals(scheme)) {
358 // Note: left here to avoid breaking compatibility. May be removed
359 // with sufficient testing.
360 return new FileInputStream(uri.getPath());
361 } else {
362 AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r");
363 try {
364 return fd != null ? fd.createInputStream() : null;
365 } catch (IOException e) {
366 throw new FileNotFoundException("Unable to create stream");
367 }
368 }
369 }
370
371 /**
372 * Synonym for {@link #openOutputStream(Uri, String)
373 * openOutputStream(uri, "w")}.
374 * @throws FileNotFoundException if the provided URI could not be opened.
375 */
376 public final OutputStream openOutputStream(Uri uri)
377 throws FileNotFoundException {
378 return openOutputStream(uri, "w");
379 }
380
381 /**
382 * Open a stream on to the content associated with a content URI. If there
383 * is no data associated with the URI, FileNotFoundException is thrown.
384 *
385 * <h5>Accepts the following URI schemes:</h5>
386 * <ul>
387 * <li>content ({@link #SCHEME_CONTENT})</li>
388 * <li>file ({@link #SCHEME_FILE})</li>
389 * </ul>
390 *
391 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
392 * on these schemes.
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800393 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 * @param uri The desired URI.
395 * @param mode May be "w", "wa", "rw", or "rwt".
396 * @return OutputStream
397 * @throws FileNotFoundException if the provided URI could not be opened.
398 * @see #openAssetFileDescriptor(Uri, String)
399 */
400 public final OutputStream openOutputStream(Uri uri, String mode)
401 throws FileNotFoundException {
402 AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode);
403 try {
404 return fd != null ? fd.createOutputStream() : null;
405 } catch (IOException e) {
406 throw new FileNotFoundException("Unable to create stream");
407 }
408 }
409
410 /**
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700411 * Open a raw file descriptor to access data under a URI. This
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
413 * underlying {@link ContentProvider#openFile}
414 * ContentProvider.openFile()} method, so will <em>not</em> work with
415 * providers that return sub-sections of files. If at all possible,
416 * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
417 * will receive a FileNotFoundException exception if the provider returns a
418 * sub-section of a file.
419 *
420 * <h5>Accepts the following URI schemes:</h5>
421 * <ul>
422 * <li>content ({@link #SCHEME_CONTENT})</li>
423 * <li>file ({@link #SCHEME_FILE})</li>
424 * </ul>
425 *
426 * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
427 * on these schemes.
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800428 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 * @param uri The desired URI to open.
430 * @param mode The file mode to use, as per {@link ContentProvider#openFile
431 * ContentProvider.openFile}.
432 * @return Returns a new ParcelFileDescriptor pointing to the file. You
433 * own this descriptor and are responsible for closing it when done.
434 * @throws FileNotFoundException Throws FileNotFoundException of no
435 * file exists under the URI or the mode is invalid.
436 * @see #openAssetFileDescriptor(Uri, String)
437 */
438 public final ParcelFileDescriptor openFileDescriptor(Uri uri,
439 String mode) throws FileNotFoundException {
440 AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode);
441 if (afd == null) {
442 return null;
443 }
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800444
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 if (afd.getDeclaredLength() < 0) {
446 // This is a full file!
447 return afd.getParcelFileDescriptor();
448 }
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 // Client can't handle a sub-section of a file, so close what
451 // we got and bail with an exception.
452 try {
453 afd.close();
454 } catch (IOException e) {
455 }
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800456
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 throw new FileNotFoundException("Not a whole file");
458 }
459
460 /**
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700461 * Open a raw file descriptor to access data under a URI. This
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 * interacts with the underlying {@link ContentProvider#openAssetFile}
Gilles Debunne03f02922010-06-09 14:11:45 -0700463 * method of the provider associated with the given URI, to retrieve any file stored there.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 *
465 * <h5>Accepts the following URI schemes:</h5>
466 * <ul>
467 * <li>content ({@link #SCHEME_CONTENT})</li>
468 * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
469 * <li>file ({@link #SCHEME_FILE})</li>
470 * </ul>
471 * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
472 * <p>
473 * A Uri object can be used to reference a resource in an APK file. The
474 * Uri should be one of the following formats:
475 * <ul>
476 * <li><code>android.resource://package_name/id_number</code><br/>
477 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
478 * For example <code>com.example.myapp</code><br/>
479 * <code>id_number</code> is the int form of the ID.<br/>
480 * The easiest way to construct this form is
481 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
482 * </li>
483 * <li><code>android.resource://package_name/type/name</code><br/>
484 * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
485 * For example <code>com.example.myapp</code><br/>
486 * <code>type</code> is the string form of the resource type. For example, <code>raw</code>
487 * or <code>drawable</code>.
488 * <code>name</code> is the string form of the resource name. That is, whatever the file
489 * name was in your res directory, without the type extension.
490 * The easiest way to construct this form is
491 * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
492 * </li>
493 * </ul>
494 *
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700495 * <p>Note that if this function is called for read-only input (mode is "r")
496 * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
497 * for you with a MIME type of "*\/*". This allows such callers to benefit
498 * from any built-in data conversion that a provider implements.
499 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 * @param uri The desired URI to open.
501 * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
502 * ContentProvider.openAssetFile}.
503 * @return Returns a new ParcelFileDescriptor pointing to the file. You
504 * own this descriptor and are responsible for closing it when done.
505 * @throws FileNotFoundException Throws FileNotFoundException of no
506 * file exists under the URI or the mode is invalid.
507 */
508 public final AssetFileDescriptor openAssetFileDescriptor(Uri uri,
509 String mode) throws FileNotFoundException {
510 String scheme = uri.getScheme();
511 if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
512 if (!"r".equals(mode)) {
513 throw new FileNotFoundException("Can't write resources: " + uri);
514 }
515 OpenResourceIdResult r = getResourceId(uri);
516 try {
517 return r.r.openRawResourceFd(r.id);
518 } catch (Resources.NotFoundException ex) {
519 throw new FileNotFoundException("Resource does not exist: " + uri);
520 }
521 } else if (SCHEME_FILE.equals(scheme)) {
522 ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
523 new File(uri.getPath()), modeToMode(uri, mode));
524 return new AssetFileDescriptor(pfd, 0, -1);
525 } else {
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700526 if ("r".equals(mode)) {
527 return openTypedAssetFileDescriptor(uri, "*/*", null);
528 } else {
529 IContentProvider provider = acquireProvider(uri);
530 if (provider == null) {
531 throw new FileNotFoundException("No content provider: " + uri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 }
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700533 try {
534 AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
535 if(fd == null) {
536 releaseProvider(provider);
537 return null;
538 }
539 ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
540 fd.getParcelFileDescriptor(), provider);
541
542 // Success! Don't release the provider when exiting, let
543 // ParcelFileDescriptorInner do that when it is closed.
544 provider = null;
545
546 return new AssetFileDescriptor(pfd, fd.getStartOffset(),
547 fd.getDeclaredLength());
548 } catch (RemoteException e) {
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800549 // Somewhat pointless, as Activity Manager will kill this
550 // process shortly anyway if the depdendent ContentProvider dies.
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700551 throw new FileNotFoundException("Dead content provider: " + uri);
552 } catch (FileNotFoundException e) {
553 throw e;
554 } finally {
555 if (provider != null) {
556 releaseProvider(provider);
557 }
558 }
559 }
560 }
561 }
562
563 /**
564 * Open a raw file descriptor to access (potentially type transformed)
565 * data from a "content:" URI. This interacts with the underlying
566 * {@link ContentProvider#openTypedAssetFile} method of the provider
567 * associated with the given URI, to retrieve retrieve any appropriate
568 * data stream for the data stored there.
569 *
570 * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
571 * with "content:" URIs, because content providers are the only facility
572 * with an associated MIME type to ensure that the returned data stream
573 * is of the desired type.
574 *
575 * <p>All text/* streams are encoded in UTF-8.
576 *
577 * @param uri The desired URI to open.
578 * @param mimeType The desired MIME type of the returned data. This can
579 * be a pattern such as *\/*, which will allow the content provider to
580 * select a type, though there is no way for you to determine what type
581 * it is returning.
582 * @param opts Additional provider-dependent options.
583 * @return Returns a new ParcelFileDescriptor from which you can read the
584 * data stream from the provider. Note that this may be a pipe, meaning
585 * you can't seek in it. The only seek you should do is if the
586 * AssetFileDescriptor contains an offset, to move to that offset before
587 * reading. You own this descriptor and are responsible for closing it when done.
588 * @throws FileNotFoundException Throws FileNotFoundException of no
589 * data of the desired type exists under the URI.
590 */
591 public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
592 String mimeType, Bundle opts) throws FileNotFoundException {
593 IContentProvider provider = acquireProvider(uri);
594 if (provider == null) {
595 throw new FileNotFoundException("No content provider: " + uri);
596 }
597 try {
598 AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts);
599 if (fd == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 releaseProvider(provider);
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700601 return null;
602 }
603 ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
604 fd.getParcelFileDescriptor(), provider);
605
606 // Success! Don't release the provider when exiting, let
607 // ParcelFileDescriptorInner do that when it is closed.
608 provider = null;
609
610 return new AssetFileDescriptor(pfd, fd.getStartOffset(),
611 fd.getDeclaredLength());
612 } catch (RemoteException e) {
613 throw new FileNotFoundException("Dead content provider: " + uri);
614 } catch (FileNotFoundException e) {
615 throw e;
616 } finally {
617 if (provider != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 releaseProvider(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 }
620 }
621 }
622
Bjorn Bringert4c87a3f2009-09-16 15:59:37 +0100623 /**
624 * A resource identified by the {@link Resources} that contains it, and a resource id.
625 *
626 * @hide
627 */
628 public class OpenResourceIdResult {
629 public Resources r;
630 public int id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 }
Bjorn Bringert4c87a3f2009-09-16 15:59:37 +0100632
633 /**
634 * Resolves an android.resource URI to a {@link Resources} and a resource id.
635 *
636 * @hide
637 */
638 public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 String authority = uri.getAuthority();
640 Resources r;
641 if (TextUtils.isEmpty(authority)) {
642 throw new FileNotFoundException("No authority: " + uri);
643 } else {
644 try {
645 r = mContext.getPackageManager().getResourcesForApplication(authority);
646 } catch (NameNotFoundException ex) {
647 throw new FileNotFoundException("No package found for authority: " + uri);
648 }
649 }
650 List<String> path = uri.getPathSegments();
651 if (path == null) {
652 throw new FileNotFoundException("No path: " + uri);
653 }
654 int len = path.size();
655 int id;
656 if (len == 1) {
657 try {
658 id = Integer.parseInt(path.get(0));
659 } catch (NumberFormatException e) {
660 throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
661 }
662 } else if (len == 2) {
663 id = r.getIdentifier(path.get(1), path.get(0), authority);
664 } else {
665 throw new FileNotFoundException("More than two path segments: " + uri);
666 }
667 if (id == 0) {
668 throw new FileNotFoundException("No resource found for: " + uri);
669 }
670 OpenResourceIdResult res = new OpenResourceIdResult();
671 res.r = r;
672 res.id = id;
673 return res;
674 }
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800675
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 /** @hide */
677 static public int modeToMode(Uri uri, String mode) throws FileNotFoundException {
678 int modeBits;
679 if ("r".equals(mode)) {
680 modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
681 } else if ("w".equals(mode) || "wt".equals(mode)) {
682 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
683 | ParcelFileDescriptor.MODE_CREATE
684 | ParcelFileDescriptor.MODE_TRUNCATE;
685 } else if ("wa".equals(mode)) {
686 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
687 | ParcelFileDescriptor.MODE_CREATE
688 | ParcelFileDescriptor.MODE_APPEND;
689 } else if ("rw".equals(mode)) {
690 modeBits = ParcelFileDescriptor.MODE_READ_WRITE
691 | ParcelFileDescriptor.MODE_CREATE;
692 } else if ("rwt".equals(mode)) {
693 modeBits = ParcelFileDescriptor.MODE_READ_WRITE
694 | ParcelFileDescriptor.MODE_CREATE
695 | ParcelFileDescriptor.MODE_TRUNCATE;
696 } else {
697 throw new FileNotFoundException("Bad mode for " + uri + ": "
698 + mode);
699 }
700 return modeBits;
701 }
Tom O'Neill0ba1cb02010-01-08 12:53:50 -0800702
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 /**
704 * Inserts a row into a table at the given URL.
705 *
706 * If the content provider supports transactions the insertion will be atomic.
707 *
708 * @param url The URL of the table to insert into.
709 * @param values The initial values for the newly inserted row. The key is the column name for
710 * the field. Passing an empty ContentValues will create an empty row.
711 * @return the URL of the newly created row.
712 */
713 public final Uri insert(Uri url, ContentValues values)
714 {
715 IContentProvider provider = acquireProvider(url);
716 if (provider == null) {
717 throw new IllegalArgumentException("Unknown URL " + url);
718 }
719 try {
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800720 long startTime = SystemClock.uptimeMillis();
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800721 Uri createdRow = provider.insert(url, values);
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800722 long durationMillis = SystemClock.uptimeMillis() - startTime;
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800723 maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
724 return createdRow;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 } catch (RemoteException e) {
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800726 // Arbitrary and not worth documenting, as Activity
727 // Manager will kill this process shortly anyway.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 return null;
729 } finally {
730 releaseProvider(provider);
731 }
732 }
733
Fred Quintana89437372009-05-15 15:10:40 -0700734 /**
Fred Quintana89437372009-05-15 15:10:40 -0700735 * Applies each of the {@link ContentProviderOperation} objects and returns an array
736 * of their results. Passes through OperationApplicationException, which may be thrown
737 * by the call to {@link ContentProviderOperation#apply}.
738 * If all the applications succeed then a {@link ContentProviderResult} array with the
739 * same number of elements as the operations will be returned. It is implementation-specific
740 * how many, if any, operations will have been successfully applied if a call to
741 * apply results in a {@link OperationApplicationException}.
742 * @param authority the authority of the ContentProvider to which this batch should be applied
743 * @param operations the operations to apply
744 * @return the results of the applications
745 * @throws OperationApplicationException thrown if an application fails.
746 * See {@link ContentProviderOperation#apply} for more information.
747 * @throws RemoteException thrown if a RemoteException is encountered while attempting
748 * to communicate with a remote provider.
749 */
750 public ContentProviderResult[] applyBatch(String authority,
Fred Quintana03d94902009-05-22 14:23:31 -0700751 ArrayList<ContentProviderOperation> operations)
Fred Quintana89437372009-05-15 15:10:40 -0700752 throws RemoteException, OperationApplicationException {
753 ContentProviderClient provider = acquireContentProviderClient(authority);
Fred Quintana6a8d5332009-05-07 17:35:38 -0700754 if (provider == null) {
Fred Quintana89437372009-05-15 15:10:40 -0700755 throw new IllegalArgumentException("Unknown authority " + authority);
Fred Quintana6a8d5332009-05-07 17:35:38 -0700756 }
757 try {
Fred Quintana89437372009-05-15 15:10:40 -0700758 return provider.applyBatch(operations);
Fred Quintana6a8d5332009-05-07 17:35:38 -0700759 } finally {
760 provider.release();
761 }
762 }
763
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 /**
765 * Inserts multiple rows into a table at the given URL.
766 *
767 * This function make no guarantees about the atomicity of the insertions.
768 *
769 * @param url The URL of the table to insert into.
770 * @param values The initial values for the newly inserted rows. The key is the column name for
771 * the field. Passing null will create an empty row.
772 * @return the number of newly created rows.
773 */
774 public final int bulkInsert(Uri url, ContentValues[] values)
775 {
776 IContentProvider provider = acquireProvider(url);
777 if (provider == null) {
778 throw new IllegalArgumentException("Unknown URL " + url);
779 }
780 try {
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800781 long startTime = SystemClock.uptimeMillis();
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800782 int rowsCreated = provider.bulkInsert(url, values);
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800783 long durationMillis = SystemClock.uptimeMillis() - startTime;
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800784 maybeLogUpdateToEventLog(durationMillis, url, "bulkinsert", null /* where */);
785 return rowsCreated;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 } catch (RemoteException e) {
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800787 // Arbitrary and not worth documenting, as Activity
788 // Manager will kill this process shortly anyway.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 return 0;
790 } finally {
791 releaseProvider(provider);
792 }
793 }
794
795 /**
796 * Deletes row(s) specified by a content URI.
797 *
798 * If the content provider supports transactions, the deletion will be atomic.
799 *
800 * @param url The URL of the row to delete.
801 * @param where A filter to apply to rows before deleting, formatted as an SQL WHERE clause
802 (excluding the WHERE itself).
803 * @return The number of rows deleted.
804 */
805 public final int delete(Uri url, String where, String[] selectionArgs)
806 {
807 IContentProvider provider = acquireProvider(url);
808 if (provider == null) {
809 throw new IllegalArgumentException("Unknown URL " + url);
810 }
811 try {
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800812 long startTime = SystemClock.uptimeMillis();
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800813 int rowsDeleted = provider.delete(url, where, selectionArgs);
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800814 long durationMillis = SystemClock.uptimeMillis() - startTime;
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800815 maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
816 return rowsDeleted;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 } catch (RemoteException e) {
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800818 // Arbitrary and not worth documenting, as Activity
819 // Manager will kill this process shortly anyway.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820 return -1;
821 } finally {
822 releaseProvider(provider);
823 }
824 }
825
826 /**
827 * Update row(s) in a content URI.
828 *
829 * If the content provider supports transactions the update will be atomic.
830 *
831 * @param uri The URI to modify.
832 * @param values The new field values. The key is the column name for the field.
833 A null value will remove an existing field value.
Omari Stephensd2a2daa2010-03-10 18:53:54 -0800834 * @param where A filter to apply to rows before updating, formatted as an SQL WHERE clause
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835 (excluding the WHERE itself).
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800836 * @return the number of rows updated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 * @throws NullPointerException if uri or values are null
838 */
839 public final int update(Uri uri, ContentValues values, String where,
840 String[] selectionArgs) {
841 IContentProvider provider = acquireProvider(uri);
842 if (provider == null) {
843 throw new IllegalArgumentException("Unknown URI " + uri);
844 }
845 try {
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800846 long startTime = SystemClock.uptimeMillis();
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800847 int rowsUpdated = provider.update(uri, values, where, selectionArgs);
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -0800848 long durationMillis = SystemClock.uptimeMillis() - startTime;
Brad Fitzpatricka63730d2010-02-07 22:25:34 -0800849 maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
850 return rowsUpdated;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 } catch (RemoteException e) {
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800852 // Arbitrary and not worth documenting, as Activity
853 // Manager will kill this process shortly anyway.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 return -1;
855 } finally {
856 releaseProvider(provider);
857 }
858 }
859
860 /**
Brad Fitzpatrick534c84c2011-01-12 14:06:30 -0800861 * Call an provider-defined method. This can be used to implement
862 * read or write interfaces which are cheaper than using a Cursor and/or
863 * do not fit into the traditional table model.
864 *
865 * @param method provider-defined method name to call. Opaque to
866 * framework, but must be non-null.
867 * @param arg provider-defined String argument. May be null.
868 * @param extras provider-defined Bundle argument. May be null.
869 * @return a result Bundle, possibly null. Will be null if the ContentProvider
870 * does not implement call.
871 * @throws NullPointerException if uri or method is null
872 * @throws IllegalArgumentException if uri is not known
873 */
874 public final Bundle call(Uri uri, String method, String arg, Bundle extras) {
875 if (uri == null) {
876 throw new NullPointerException("uri == null");
877 }
878 if (method == null) {
879 throw new NullPointerException("method == null");
880 }
881 IContentProvider provider = acquireProvider(uri);
882 if (provider == null) {
883 throw new IllegalArgumentException("Unknown URI " + uri);
884 }
885 try {
886 return provider.call(method, arg, extras);
887 } catch (RemoteException e) {
888 // Arbitrary and not worth documenting, as Activity
889 // Manager will kill this process shortly anyway.
890 return null;
891 } finally {
892 releaseProvider(provider);
893 }
894 }
895
896 /**
Dianne Hackborncca1f0e2010-09-26 18:34:53 -0700897 * Returns the content provider for the given content URI.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 *
899 * @param uri The URI to a content provider
900 * @return The ContentProvider for the given URI, or null if no content provider is found.
901 * @hide
902 */
Dianne Hackborncca1f0e2010-09-26 18:34:53 -0700903 public final IContentProvider acquireProvider(Uri uri) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 if (!SCHEME_CONTENT.equals(uri.getScheme())) {
905 return null;
906 }
907 String auth = uri.getAuthority();
908 if (auth != null) {
909 return acquireProvider(mContext, uri.getAuthority());
910 }
911 return null;
912 }
913
914 /**
Dianne Hackborncca1f0e2010-09-26 18:34:53 -0700915 * Returns the content provider for the given content URI if the process
916 * already has a reference on it.
917 *
918 * @param uri The URI to a content provider
919 * @return The ContentProvider for the given URI, or null if no content provider is found.
920 * @hide
921 */
922 public final IContentProvider acquireExistingProvider(Uri uri) {
923 if (!SCHEME_CONTENT.equals(uri.getScheme())) {
924 return null;
925 }
926 String auth = uri.getAuthority();
927 if (auth != null) {
928 return acquireExistingProvider(mContext, uri.getAuthority());
929 }
930 return null;
931 }
932
933 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 * @hide
935 */
936 public final IContentProvider acquireProvider(String name) {
Brad Fitzpatrick1877d012010-03-04 17:48:13 -0800937 if (name == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 return null;
939 }
940 return acquireProvider(mContext, name);
941 }
942
943 /**
Fred Quintana718d8a22009-04-29 17:53:20 -0700944 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
945 * that services the content at uri, starting the provider if necessary. Returns
946 * null if there is no provider associated wih the uri. The caller must indicate that they are
947 * done with the provider by calling {@link ContentProviderClient#release} which will allow
948 * the system to release the provider it it determines that there is no other reason for
949 * keeping it active.
950 * @param uri specifies which provider should be acquired
951 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
952 * that services the content at uri or null if there isn't one.
953 */
954 public final ContentProviderClient acquireContentProviderClient(Uri uri) {
955 IContentProvider provider = acquireProvider(uri);
956 if (provider != null) {
957 return new ContentProviderClient(this, provider);
958 }
959
960 return null;
961 }
962
963 /**
964 * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
965 * with the authority of name, starting the provider if necessary. Returns
966 * null if there is no provider associated wih the uri. The caller must indicate that they are
967 * done with the provider by calling {@link ContentProviderClient#release} which will allow
968 * the system to release the provider it it determines that there is no other reason for
969 * keeping it active.
970 * @param name specifies which provider should be acquired
971 * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
972 * with the authority of name or null if there isn't one.
973 */
974 public final ContentProviderClient acquireContentProviderClient(String name) {
975 IContentProvider provider = acquireProvider(name);
976 if (provider != null) {
977 return new ContentProviderClient(this, provider);
978 }
979
980 return null;
981 }
982
983 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 * Register an observer class that gets callbacks when data identified by a
985 * given content URI changes.
986 *
987 * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
988 * for a whole class of content.
989 * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code>
990 * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI
991 * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values
992 * at or below the specified URI will also trigger a match.
993 * @param observer The object that receives callbacks when changes occur.
994 * @see #unregisterContentObserver
995 */
996 public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
997 ContentObserver observer)
998 {
999 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001000 getContentService().registerContentObserver(uri, notifyForDescendents,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 observer.getContentObserver());
1002 } catch (RemoteException e) {
1003 }
1004 }
1005
1006 /**
1007 * Unregisters a change observer.
1008 *
1009 * @param observer The previously registered observer that is no longer needed.
1010 * @see #registerContentObserver
1011 */
1012 public final void unregisterContentObserver(ContentObserver observer) {
1013 try {
1014 IContentObserver contentObserver = observer.releaseContentObserver();
1015 if (contentObserver != null) {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001016 getContentService().unregisterContentObserver(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 contentObserver);
1018 }
1019 } catch (RemoteException e) {
1020 }
1021 }
1022
1023 /**
1024 * Notify registered observers that a row was updated.
1025 * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
1026 * By default, CursorAdapter objects will get this notification.
1027 *
1028 * @param uri
1029 * @param observer The observer that originated the change, may be <code>null</null>
1030 */
1031 public void notifyChange(Uri uri, ContentObserver observer) {
1032 notifyChange(uri, observer, true /* sync to network */);
1033 }
1034
1035 /**
1036 * Notify registered observers that a row was updated.
1037 * To register, call {@link #registerContentObserver(android.net.Uri , boolean, android.database.ContentObserver) registerContentObserver()}.
1038 * By default, CursorAdapter objects will get this notification.
1039 *
1040 * @param uri
1041 * @param observer The observer that originated the change, may be <code>null</null>
1042 * @param syncToNetwork If true, attempt to sync the change to the network.
1043 */
1044 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
1045 try {
Dianne Hackborn231cc602009-04-27 17:10:36 -07001046 getContentService().notifyChange(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 uri, observer == null ? null : observer.getContentObserver(),
1048 observer != null && observer.deliverSelfNotifications(), syncToNetwork);
1049 } catch (RemoteException e) {
1050 }
1051 }
1052
1053 /**
1054 * Start an asynchronous sync operation. If you want to monitor the progress
1055 * of the sync you may register a SyncObserver. Only values of the following
1056 * types may be used in the extras bundle:
1057 * <ul>
1058 * <li>Integer</li>
1059 * <li>Long</li>
1060 * <li>Boolean</li>
1061 * <li>Float</li>
1062 * <li>Double</li>
1063 * <li>String</li>
1064 * </ul>
1065 *
1066 * @param uri the uri of the provider to sync or null to sync all providers.
1067 * @param extras any extras to pass to the SyncAdapter.
Fred Quintanaac9385e2009-06-22 18:00:59 -07001068 * @deprecated instead use
1069 * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 */
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001071 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 public void startSync(Uri uri, Bundle extras) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001073 Account account = null;
1074 if (extras != null) {
1075 String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
1076 if (!TextUtils.isEmpty(accountName)) {
Costin Manolache3348f142009-09-29 18:58:36 -07001077 account = new Account(accountName, "com.google");
Fred Quintanaac9385e2009-06-22 18:00:59 -07001078 }
1079 extras.remove(SYNC_EXTRAS_ACCOUNT);
1080 }
1081 requestSync(account, uri != null ? uri.getAuthority() : null, extras);
1082 }
1083
1084 /**
1085 * Start an asynchronous sync operation. If you want to monitor the progress
1086 * of the sync you may register a SyncObserver. Only values of the following
1087 * types may be used in the extras bundle:
1088 * <ul>
1089 * <li>Integer</li>
1090 * <li>Long</li>
1091 * <li>Boolean</li>
1092 * <li>Float</li>
1093 * <li>Double</li>
1094 * <li>String</li>
1095 * </ul>
1096 *
1097 * @param account which account should be synced
1098 * @param authority which authority should be synced
1099 * @param extras any extras to pass to the SyncAdapter.
1100 */
1101 public static void requestSync(Account account, String authority, Bundle extras) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 validateSyncExtrasBundle(extras);
1103 try {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001104 getContentService().requestSync(account, authority, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 } catch (RemoteException e) {
1106 }
1107 }
1108
1109 /**
1110 * Check that only values of the following types are in the Bundle:
1111 * <ul>
1112 * <li>Integer</li>
1113 * <li>Long</li>
1114 * <li>Boolean</li>
1115 * <li>Float</li>
1116 * <li>Double</li>
1117 * <li>String</li>
Fred Quintanad9d2f112009-04-23 13:36:27 -07001118 * <li>Account</li>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 * <li>null</li>
1120 * </ul>
1121 * @param extras the Bundle to check
1122 */
1123 public static void validateSyncExtrasBundle(Bundle extras) {
1124 try {
1125 for (String key : extras.keySet()) {
1126 Object value = extras.get(key);
1127 if (value == null) continue;
1128 if (value instanceof Long) continue;
1129 if (value instanceof Integer) continue;
1130 if (value instanceof Boolean) continue;
1131 if (value instanceof Float) continue;
1132 if (value instanceof Double) continue;
1133 if (value instanceof String) continue;
Fred Quintanad9d2f112009-04-23 13:36:27 -07001134 if (value instanceof Account) continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 throw new IllegalArgumentException("unexpected value type: "
1136 + value.getClass().getName());
1137 }
1138 } catch (IllegalArgumentException e) {
1139 throw e;
1140 } catch (RuntimeException exc) {
1141 throw new IllegalArgumentException("error unparceling Bundle", exc);
1142 }
1143 }
1144
Fred Quintanaac9385e2009-06-22 18:00:59 -07001145 /**
1146 * Cancel any active or pending syncs that match the Uri. If the uri is null then
1147 * all syncs will be canceled.
1148 *
1149 * @param uri the uri of the provider to sync or null to sync all providers.
1150 * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)}
1151 */
Dianne Hackborn4a51c202009-08-21 15:14:02 -07001152 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 public void cancelSync(Uri uri) {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001154 cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null);
1155 }
1156
1157 /**
1158 * Cancel any active or pending syncs that match account and authority. The account and
1159 * authority can each independently be set to null, which means that syncs with any account
1160 * or authority, respectively, will match.
1161 *
1162 * @param account filters the syncs that match by this account
1163 * @param authority filters the syncs that match by this authority
1164 */
1165 public static void cancelSync(Account account, String authority) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 try {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001167 getContentService().cancelSync(account, authority);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 } catch (RemoteException e) {
1169 }
1170 }
1171
Fred Quintanaac9385e2009-06-22 18:00:59 -07001172 /**
1173 * Get information about the SyncAdapters that are known to the system.
1174 * @return an array of SyncAdapters that have registered with the system
1175 */
1176 public static SyncAdapterType[] getSyncAdapterTypes() {
1177 try {
1178 return getContentService().getSyncAdapterTypes();
1179 } catch (RemoteException e) {
1180 throw new RuntimeException("the ContentService should always be reachable", e);
1181 }
1182 }
1183
1184 /**
1185 * Check if the provider should be synced when a network tickle is received
1186 *
1187 * @param account the account whose setting we are querying
1188 * @param authority the provider whose setting we are querying
1189 * @return true if the provider should be synced when a network tickle is received
1190 */
1191 public static boolean getSyncAutomatically(Account account, String authority) {
1192 try {
1193 return getContentService().getSyncAutomatically(account, authority);
1194 } catch (RemoteException e) {
1195 throw new RuntimeException("the ContentService should always be reachable", e);
1196 }
1197 }
1198
1199 /**
1200 * Set whether or not the provider is synced when it receives a network tickle.
1201 *
1202 * @param account the account whose setting we are querying
1203 * @param authority the provider whose behavior is being controlled
1204 * @param sync true if the provider should be synced when tickles are received for it
1205 */
1206 public static void setSyncAutomatically(Account account, String authority, boolean sync) {
1207 try {
1208 getContentService().setSyncAutomatically(account, authority, sync);
1209 } catch (RemoteException e) {
1210 // exception ignored; if this is thrown then it means the runtime is in the midst of
1211 // being restarted
1212 }
1213 }
1214
1215 /**
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001216 * Specifies that a sync should be requested with the specified the account, authority,
1217 * and extras at the given frequency. If there is already another periodic sync scheduled
1218 * with the account, authority and extras then a new periodic sync won't be added, instead
1219 * the frequency of the previous one will be updated.
1220 * <p>
1221 * These periodic syncs honor the "syncAutomatically" and "masterSyncAutomatically" settings.
1222 * Although these sync are scheduled at the specified frequency, it may take longer for it to
1223 * actually be started if other syncs are ahead of it in the sync operation queue. This means
1224 * that the actual start time may drift.
Fred Quintana53bd2522010-02-05 15:28:12 -08001225 * <p>
1226 * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY},
1227 * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS},
1228 * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE},
1229 * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true.
1230 * If any are supplied then an {@link IllegalArgumentException} will be thrown.
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001231 *
1232 * @param account the account to specify in the sync
1233 * @param authority the provider to specify in the sync request
1234 * @param extras extra parameters to go along with the sync request
1235 * @param pollFrequency how frequently the sync should be performed, in seconds.
Fred Quintana53bd2522010-02-05 15:28:12 -08001236 * @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
1237 * are null.
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001238 */
1239 public static void addPeriodicSync(Account account, String authority, Bundle extras,
1240 long pollFrequency) {
1241 validateSyncExtrasBundle(extras);
Fred Quintana53bd2522010-02-05 15:28:12 -08001242 if (account == null) {
1243 throw new IllegalArgumentException("account must not be null");
1244 }
1245 if (authority == null) {
1246 throw new IllegalArgumentException("authority must not be null");
1247 }
1248 if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false)
1249 || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false)
1250 || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false)
1251 || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false)
1252 || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false)
1253 || extras.getBoolean(SYNC_EXTRAS_FORCE, false)
1254 || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) {
1255 throw new IllegalArgumentException("illegal extras were set");
1256 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001257 try {
1258 getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
1259 } catch (RemoteException e) {
1260 // exception ignored; if this is thrown then it means the runtime is in the midst of
1261 // being restarted
1262 }
1263 }
1264
1265 /**
1266 * Remove a periodic sync. Has no affect if account, authority and extras don't match
1267 * an existing periodic sync.
1268 *
1269 * @param account the account of the periodic sync to remove
1270 * @param authority the provider of the periodic sync to remove
1271 * @param extras the extras of the periodic sync to remove
1272 */
1273 public static void removePeriodicSync(Account account, String authority, Bundle extras) {
1274 validateSyncExtrasBundle(extras);
Fred Quintana53bd2522010-02-05 15:28:12 -08001275 if (account == null) {
1276 throw new IllegalArgumentException("account must not be null");
1277 }
1278 if (authority == null) {
1279 throw new IllegalArgumentException("authority must not be null");
1280 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001281 try {
1282 getContentService().removePeriodicSync(account, authority, extras);
1283 } catch (RemoteException e) {
1284 throw new RuntimeException("the ContentService should always be reachable", e);
1285 }
1286 }
1287
1288 /**
1289 * Get the list of information about the periodic syncs for the given account and authority.
1290 *
1291 * @param account the account whose periodic syncs we are querying
1292 * @param authority the provider whose periodic syncs we are querying
1293 * @return a list of PeriodicSync objects. This list may be empty but will never be null.
1294 */
1295 public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) {
Fred Quintana53bd2522010-02-05 15:28:12 -08001296 if (account == null) {
1297 throw new IllegalArgumentException("account must not be null");
1298 }
1299 if (authority == null) {
1300 throw new IllegalArgumentException("authority must not be null");
1301 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -08001302 try {
1303 return getContentService().getPeriodicSyncs(account, authority);
1304 } catch (RemoteException e) {
1305 throw new RuntimeException("the ContentService should always be reachable", e);
1306 }
1307 }
1308
1309 /**
Fred Quintana5e787c42009-08-16 23:13:53 -07001310 * Check if this account/provider is syncable.
1311 * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
1312 */
Jim Miller20ea6ce2009-08-17 15:47:14 -07001313 public static int getIsSyncable(Account account, String authority) {
Fred Quintana5e787c42009-08-16 23:13:53 -07001314 try {
1315 return getContentService().getIsSyncable(account, authority);
1316 } catch (RemoteException e) {
1317 throw new RuntimeException("the ContentService should always be reachable", e);
1318 }
1319 }
1320
1321 /**
1322 * Set whether this account/provider is syncable.
Fred Quintana718671b2009-08-17 14:08:37 -07001323 * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown
Fred Quintana5e787c42009-08-16 23:13:53 -07001324 */
Jim Miller20ea6ce2009-08-17 15:47:14 -07001325 public static void setIsSyncable(Account account, String authority, int syncable) {
Fred Quintana5e787c42009-08-16 23:13:53 -07001326 try {
1327 getContentService().setIsSyncable(account, authority, syncable);
1328 } catch (RemoteException e) {
1329 // exception ignored; if this is thrown then it means the runtime is in the midst of
1330 // being restarted
1331 }
1332 }
1333
1334 /**
Fred Quintanaac9385e2009-06-22 18:00:59 -07001335 * Gets the master auto-sync setting that applies to all the providers and accounts.
1336 * If this is false then the per-provider auto-sync setting is ignored.
1337 *
1338 * @return the master auto-sync setting that applies to all the providers and accounts
1339 */
1340 public static boolean getMasterSyncAutomatically() {
1341 try {
1342 return getContentService().getMasterSyncAutomatically();
1343 } catch (RemoteException e) {
1344 throw new RuntimeException("the ContentService should always be reachable", e);
1345 }
1346 }
1347
1348 /**
1349 * Sets the master auto-sync setting that applies to all the providers and accounts.
1350 * If this is false then the per-provider auto-sync setting is ignored.
1351 *
1352 * @param sync the master auto-sync setting that applies to all the providers and accounts
1353 */
1354 public static void setMasterSyncAutomatically(boolean sync) {
1355 try {
1356 getContentService().setMasterSyncAutomatically(sync);
1357 } catch (RemoteException e) {
1358 // exception ignored; if this is thrown then it means the runtime is in the midst of
1359 // being restarted
1360 }
1361 }
1362
1363 /**
1364 * Returns true if there is currently a sync operation for the given
1365 * account or authority in the pending list, or actively being processed.
1366 * @param account the account whose setting we are querying
1367 * @param authority the provider whose behavior is being queried
1368 * @return true if a sync is active for the given account or authority.
1369 */
1370 public static boolean isSyncActive(Account account, String authority) {
1371 try {
1372 return getContentService().isSyncActive(account, authority);
1373 } catch (RemoteException e) {
1374 throw new RuntimeException("the ContentService should always be reachable", e);
1375 }
1376 }
1377
1378 /**
Fred Quintanac6a69552010-09-27 17:05:04 -07001379 * If a sync is active returns the information about it, otherwise returns null.
1380 * <p>
Fred Quintanad5e4fdc2010-03-30 15:16:21 -07001381 * @return the SyncInfo for the currently active sync or null if one is not active.
Fred Quintanac6a69552010-09-27 17:05:04 -07001382 * @deprecated
1383 * Since multiple concurrent syncs are now supported you should use
1384 * {@link #getCurrentSyncs()} to get the accurate list of current syncs.
1385 * This method returns the first item from the list of current syncs
1386 * or null if there are none.
Fred Quintanaac9385e2009-06-22 18:00:59 -07001387 */
Fred Quintanac6a69552010-09-27 17:05:04 -07001388 @Deprecated
Fred Quintanad5e4fdc2010-03-30 15:16:21 -07001389 public static SyncInfo getCurrentSync() {
Fred Quintanaac9385e2009-06-22 18:00:59 -07001390 try {
Fred Quintanac6a69552010-09-27 17:05:04 -07001391 final List<SyncInfo> syncs = getContentService().getCurrentSyncs();
1392 if (syncs.isEmpty()) {
1393 return null;
1394 }
1395 return syncs.get(0);
1396 } catch (RemoteException e) {
1397 throw new RuntimeException("the ContentService should always be reachable", e);
1398 }
1399 }
1400
1401 /**
1402 * Returns a list with information about all the active syncs. This list will be empty
1403 * if there are no active syncs.
1404 * @return a List of SyncInfo objects for the currently active syncs.
1405 */
1406 public static List<SyncInfo> getCurrentSyncs() {
1407 try {
1408 return getContentService().getCurrentSyncs();
Fred Quintanaac9385e2009-06-22 18:00:59 -07001409 } catch (RemoteException e) {
1410 throw new RuntimeException("the ContentService should always be reachable", e);
1411 }
1412 }
1413
1414 /**
Fred Quintana4a6679b2009-08-17 13:05:39 -07001415 * Returns the status that matches the authority.
Fred Quintanaac9385e2009-06-22 18:00:59 -07001416 * @param account the account whose setting we are querying
1417 * @param authority the provider whose behavior is being queried
1418 * @return the SyncStatusInfo for the authority, or null if none exists
1419 * @hide
1420 */
1421 public static SyncStatusInfo getSyncStatus(Account account, String authority) {
1422 try {
1423 return getContentService().getSyncStatus(account, authority);
1424 } catch (RemoteException e) {
1425 throw new RuntimeException("the ContentService should always be reachable", e);
1426 }
1427 }
1428
1429 /**
1430 * Return true if the pending status is true of any matching authorities.
1431 * @param account the account whose setting we are querying
1432 * @param authority the provider whose behavior is being queried
1433 * @return true if there is a pending sync with the matching account and authority
1434 */
1435 public static boolean isSyncPending(Account account, String authority) {
1436 try {
1437 return getContentService().isSyncPending(account, authority);
1438 } catch (RemoteException e) {
1439 throw new RuntimeException("the ContentService should always be reachable", e);
1440 }
1441 }
1442
Fred Quintana1b487ec2010-02-26 10:57:55 -08001443 /**
1444 * Request notifications when the different aspects of the SyncManager change. The
1445 * different items that can be requested are:
1446 * <ul>
1447 * <li> {@link #SYNC_OBSERVER_TYPE_PENDING}
1448 * <li> {@link #SYNC_OBSERVER_TYPE_ACTIVE}
1449 * <li> {@link #SYNC_OBSERVER_TYPE_SETTINGS}
1450 * </ul>
1451 * The caller can set one or more of the status types in the mask for any
1452 * given listener registration.
1453 * @param mask the status change types that will cause the callback to be invoked
1454 * @param callback observer to be invoked when the status changes
1455 * @return a handle that can be used to remove the listener at a later time
1456 */
Fred Quintanaac9385e2009-06-22 18:00:59 -07001457 public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) {
Fred Quintana1b487ec2010-02-26 10:57:55 -08001458 if (callback == null) {
1459 throw new IllegalArgumentException("you passed in a null callback");
1460 }
Fred Quintanaac9385e2009-06-22 18:00:59 -07001461 try {
1462 ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() {
1463 public void onStatusChanged(int which) throws RemoteException {
1464 callback.onStatusChanged(which);
1465 }
1466 };
1467 getContentService().addStatusChangeListener(mask, observer);
1468 return observer;
1469 } catch (RemoteException e) {
1470 throw new RuntimeException("the ContentService should always be reachable", e);
1471 }
1472 }
1473
Fred Quintana1b487ec2010-02-26 10:57:55 -08001474 /**
1475 * Remove a previously registered status change listener.
1476 * @param handle the handle that was returned by {@link #addStatusChangeListener}
1477 */
Fred Quintanaac9385e2009-06-22 18:00:59 -07001478 public static void removeStatusChangeListener(Object handle) {
Fred Quintana1b487ec2010-02-26 10:57:55 -08001479 if (handle == null) {
1480 throw new IllegalArgumentException("you passed in a null handle");
1481 }
Fred Quintanaac9385e2009-06-22 18:00:59 -07001482 try {
1483 getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
1484 } catch (RemoteException e) {
1485 // exception ignored; if this is thrown then it means the runtime is in the midst of
1486 // being restarted
1487 }
1488 }
1489
Brad Fitzpatricka63730d2010-02-07 22:25:34 -08001490 /**
1491 * Returns sampling percentage for a given duration.
1492 *
1493 * Always returns at least 1%.
1494 */
1495 private int samplePercentForDuration(long durationMillis) {
1496 if (durationMillis >= SLOW_THRESHOLD_MILLIS) {
1497 return 100;
1498 }
1499 return (int) (100 * durationMillis / SLOW_THRESHOLD_MILLIS) + 1;
1500 }
1501
1502 private void maybeLogQueryToEventLog(long durationMillis,
1503 Uri uri, String[] projection,
1504 String selection, String sortOrder) {
1505 int samplePercent = samplePercentForDuration(durationMillis);
1506 if (samplePercent < 100) {
1507 synchronized (mRandom) {
1508 if (mRandom.nextInt(100) >= samplePercent) {
1509 return;
1510 }
1511 }
1512 }
1513
1514 StringBuilder projectionBuffer = new StringBuilder(100);
1515 if (projection != null) {
1516 for (int i = 0; i < projection.length; ++i) {
1517 // Note: not using a comma delimiter here, as the
1518 // multiple arguments to EventLog.writeEvent later
1519 // stringify with a comma delimiter, which would make
1520 // parsing uglier later.
1521 if (i != 0) projectionBuffer.append('/');
1522 projectionBuffer.append(projection[i]);
1523 }
1524 }
1525
1526 // ActivityThread.currentPackageName() only returns non-null if the
1527 // current thread is an application main thread. This parameter tells
1528 // us whether an event loop is blocked, and if so, which app it is.
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -07001529 String blockingPackage = AppGlobals.getInitialPackage();
Brad Fitzpatricka63730d2010-02-07 22:25:34 -08001530
1531 EventLog.writeEvent(
Brad Fitzpatricka8fbedb2010-04-08 14:08:54 -07001532 EventLogTags.CONTENT_QUERY_SAMPLE,
Brad Fitzpatricka63730d2010-02-07 22:25:34 -08001533 uri.toString(),
1534 projectionBuffer.toString(),
1535 selection != null ? selection : "",
1536 sortOrder != null ? sortOrder : "",
1537 durationMillis,
1538 blockingPackage != null ? blockingPackage : "",
1539 samplePercent);
1540 }
1541
1542 private void maybeLogUpdateToEventLog(
1543 long durationMillis, Uri uri, String operation, String selection) {
1544 int samplePercent = samplePercentForDuration(durationMillis);
1545 if (samplePercent < 100) {
1546 synchronized (mRandom) {
1547 if (mRandom.nextInt(100) >= samplePercent) {
1548 return;
1549 }
1550 }
1551 }
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -07001552 String blockingPackage = AppGlobals.getInitialPackage();
Brad Fitzpatricka63730d2010-02-07 22:25:34 -08001553 EventLog.writeEvent(
Brad Fitzpatricka8fbedb2010-04-08 14:08:54 -07001554 EventLogTags.CONTENT_UPDATE_SAMPLE,
Brad Fitzpatricka63730d2010-02-07 22:25:34 -08001555 uri.toString(),
1556 operation,
1557 selection != null ? selection : "",
1558 durationMillis,
1559 blockingPackage != null ? blockingPackage : "",
1560 samplePercent);
1561 }
Fred Quintanaac9385e2009-06-22 18:00:59 -07001562
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 private final class CursorWrapperInner extends CursorWrapper {
Gilles Debunne03f02922010-06-09 14:11:45 -07001564 private final IContentProvider mContentProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001565 public static final String TAG="CursorWrapperInner";
1566 private boolean mCloseFlag = false;
1567
1568 CursorWrapperInner(Cursor cursor, IContentProvider icp) {
1569 super(cursor);
1570 mContentProvider = icp;
1571 }
1572
1573 @Override
1574 public void close() {
1575 super.close();
1576 ContentResolver.this.releaseProvider(mContentProvider);
1577 mCloseFlag = true;
1578 }
1579
1580 @Override
1581 protected void finalize() throws Throwable {
Johannes Carlsson872a52c2010-12-20 16:14:52 +01001582 // TODO: integrate CloseGuard support.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001583 try {
1584 if(!mCloseFlag) {
Johannes Carlsson872a52c2010-12-20 16:14:52 +01001585 Log.w(TAG, "Cursor finalized without prior close()");
1586 close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587 }
1588 } finally {
1589 super.finalize();
1590 }
1591 }
1592 }
1593
1594 private final class ParcelFileDescriptorInner extends ParcelFileDescriptor {
Gilles Debunne03f02922010-06-09 14:11:45 -07001595 private final IContentProvider mContentProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 public static final String TAG="ParcelFileDescriptorInner";
1597 private boolean mReleaseProviderFlag = false;
1598
1599 ParcelFileDescriptorInner(ParcelFileDescriptor pfd, IContentProvider icp) {
1600 super(pfd);
1601 mContentProvider = icp;
1602 }
1603
1604 @Override
1605 public void close() throws IOException {
1606 if(!mReleaseProviderFlag) {
1607 super.close();
1608 ContentResolver.this.releaseProvider(mContentProvider);
1609 mReleaseProviderFlag = true;
1610 }
1611 }
1612
1613 @Override
1614 protected void finalize() throws Throwable {
1615 if (!mReleaseProviderFlag) {
1616 close();
1617 }
1618 }
1619 }
1620
Dianne Hackborn231cc602009-04-27 17:10:36 -07001621 /** @hide */
1622 public static final String CONTENT_SERVICE_NAME = "content";
Tom O'Neill0ba1cb02010-01-08 12:53:50 -08001623
Dianne Hackborn231cc602009-04-27 17:10:36 -07001624 /** @hide */
1625 public static IContentService getContentService() {
1626 if (sContentService != null) {
1627 return sContentService;
1628 }
1629 IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
1630 if (Config.LOGV) Log.v("ContentService", "default service binder = " + b);
1631 sContentService = IContentService.Stub.asInterface(b);
1632 if (Config.LOGV) Log.v("ContentService", "default service = " + sContentService);
1633 return sContentService;
1634 }
Tom O'Neill0ba1cb02010-01-08 12:53:50 -08001635
Dianne Hackborn231cc602009-04-27 17:10:36 -07001636 private static IContentService sContentService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637 private final Context mContext;
1638 private static final String TAG = "ContentResolver";
1639}