diff options
Diffstat (limited to 'core/java/android/content')
23 files changed, 1197 insertions, 334 deletions
diff --git a/core/java/android/content/AsyncQueryHandler.java b/core/java/android/content/AsyncQueryHandler.java index 2d651a7..ac851cc 100644 --- a/core/java/android/content/AsyncQueryHandler.java +++ b/core/java/android/content/AsyncQueryHandler.java @@ -146,6 +146,20 @@ public abstract class AsyncQueryHandler extends Handler { * @param token A token passed into {@link #onQueryComplete} to identify * the query. * @param cookie An object that gets passed into {@link #onQueryComplete} + * @param uri The URI, using the content:// scheme, for the content to + * retrieve. + * @param projection A list of which columns to return. Passing null will + * return all columns, which is discouraged to prevent reading data + * from storage that isn't going to be used. + * @param selection A filter declaring which rows to return, formatted as an + * SQL WHERE clause (excluding the WHERE itself). Passing null will + * return all rows for the given URI. + * @param selectionArgs You may include ?s in selection, which will be + * replaced by the values from selectionArgs, in the order that they + * appear in the selection. The values will be bound as Strings. + * @param orderBy How to order the rows, formatted as an SQL ORDER BY + * clause (excluding the ORDER BY itself). Passing null will use the + * default sort order, which may be unordered. */ public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index cd92002..08f6191 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -16,7 +16,11 @@ package android.content; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; import android.util.Log; /** @@ -75,7 +79,7 @@ import android.util.Log; * <p>The BroadcastReceiver class (when launched as a component through * a manifest's {@link android.R.styleable#AndroidManifestReceiver <receiver>} * tag) is an important part of an - * <a href="{@docRoot}intro/lifecycle.html">application's overall lifecycle</a>.</p> + * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">application's overall lifecycle</a>.</p> * * <p>Topics covered here: * <ol> @@ -131,7 +135,7 @@ import android.util.Log; * tag in their <code>AndroidManifest.xml</code>) will be able to send an * Intent to the receiver. * - * <p>See the <a href="{@docRoot}devel/security.html">Security Model</a> + * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> * document for more information on permissions and security in general. * * <a name="ProcessLifecycle"></a> @@ -175,7 +179,9 @@ public abstract class BroadcastReceiver { * return a result to you asynchronously -- in particular, for interacting * with services, you should use * {@link Context#startService(Intent)} instead of - * {@link Context#bindService(Intent, ServiceConnection, int)}. + * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish + * to interact with a service that is already running, you can use + * {@link #peekService}. * * @param context The Context in which the receiver is running. * @param intent The Intent being received. @@ -183,6 +189,26 @@ public abstract class BroadcastReceiver { public abstract void onReceive(Context context, Intent intent); /** + * Provide a binder to an already-running service. This method is synchronous + * and will not start the target service if it is not present, so it is safe + * to call from {@link #onReceive}. + * + * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)} + * @param service The Intent indicating the service you wish to use. See {@link + * Context#startService(Intent)} for more information. + */ + public IBinder peekService(Context myContext, Intent service) { + IActivityManager am = ActivityManagerNative.getDefault(); + IBinder binder = null; + try { + binder = am.peekService(service, service.resolveTypeIfNeeded( + myContext.getContentResolver())); + } catch (RemoteException e) { + } + return binder; + } + + /** * Change the current result code of this broadcast; only works with * broadcasts sent through * {@link Context#sendOrderedBroadcast(Intent, String) diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 226c5ab..25544de 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -18,6 +18,7 @@ package android.content; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; +import android.content.res.AssetFileDescriptor; import android.content.res.Configuration; import android.database.Cursor; import android.database.CursorToBulkCursorAdaptor; @@ -41,8 +42,8 @@ import java.io.FileNotFoundException; * multiple applications you can use a database directly via * {@link android.database.sqlite.SQLiteDatabase}. * - * <p>See <a href="{@docRoot}devel/data/contentproviders.html">this page</a> for more information on - * content providers.</p> + * <p>For more information, read <a href="{@docRoot}guide/topics/providers/content-providers.html">Content + * Providers</a>.</p> * * <p>When a request is made via * a {@link ContentResolver} the system inspects the authority of the given URI and passes the @@ -162,6 +163,13 @@ public abstract class ContentProvider implements ComponentCallbacks { return ContentProvider.this.openFile(uri, mode); } + public AssetFileDescriptor openAssetFile(Uri uri, String mode) + throws FileNotFoundException { + if (mode != null && mode.startsWith("rw")) checkWritePermission(uri); + else checkReadPermission(uri); + return ContentProvider.this.openAssetFile(uri, mode); + } + public ISyncAdapter getSyncAdapter() { checkWritePermission(null); return ContentProvider.this.getSyncAdapter().getISyncAdapter(); @@ -226,9 +234,9 @@ public abstract class ContentProvider implements ComponentCallbacks { /** * Return the name of the permission required for read-only access to * this content provider. This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of - * the Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. */ public final String getReadPermission() { return mReadPermission; @@ -248,9 +256,9 @@ public abstract class ContentProvider implements ComponentCallbacks { /** * Return the name of the permission required for read/write access to * this content provider. This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of - * the Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. */ public final String getWritePermission() { return mWritePermission; @@ -273,9 +281,9 @@ public abstract class ContentProvider implements ComponentCallbacks { * Receives a query request from a client in a local process, and * returns a Cursor. This is called internally by the {@link ContentResolver}. * This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of - * the Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. * <p> * Example client call:<p> * <pre>// Request a specific record. @@ -330,9 +338,9 @@ public abstract class ContentProvider implements ComponentCallbacks { * <code>vnd.android.cursor.item</code> for a single record, * or <code>vnd.android.cursor.dir/</code> for multiple items. * This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of - * the Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. * * @param uri the URI to query. * @return a MIME type string, or null if there is no type. @@ -344,9 +352,9 @@ public abstract class ContentProvider implements ComponentCallbacks { * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after inserting. * This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of the - * Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. * @param uri The content:// URI of the insertion request. * @param values A set of column_name/value pairs to add to the database. * @return The URI for the newly inserted item. @@ -359,9 +367,9 @@ public abstract class ContentProvider implements ComponentCallbacks { * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after inserting. * This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of - * the Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. * * @param uri The content:// URI of the insertion request. * @param values An array of sets of column_name/value pairs to add to the database. @@ -382,9 +390,9 @@ public abstract class ContentProvider implements ComponentCallbacks { * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()} * after deleting. * This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of the - * Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. * * <p>The implementation is responsible for parsing out a row ID at the end * of the URI, if a specific row is being deleted. That is, the client would @@ -405,9 +413,9 @@ public abstract class ContentProvider implements ComponentCallbacks { * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after updating. * This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of the - * Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. * * @param uri The URI to query. This can potentially have a record ID if this * is an update request for a specific record. @@ -422,9 +430,9 @@ public abstract class ContentProvider implements ComponentCallbacks { /** * Open a file blob associated with a content URI. * This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of the - * Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. * * <p>Returns a * ParcelFileDescriptor, from which you can obtain a @@ -438,8 +446,9 @@ public abstract class ContentProvider implements ComponentCallbacks { * of this method should create a new ParcelFileDescriptor for each call. * * @param uri The URI whose file is to be opened. - * @param mode Access mode for the file. May be "r" for read-only access - * or "rw" for read and write access. + * @param mode Access mode for the file. May be "r" for read-only access, + * "rw" for read and write access, or "rwt" for read and write access + * that truncates any existing file. * * @return Returns a new ParcelFileDescriptor which you can use to access * the file. @@ -448,19 +457,66 @@ public abstract class ContentProvider implements ComponentCallbacks { * no file associated with the given URI or the mode is invalid. * @throws SecurityException Throws SecurityException if the caller does * not have permission to access the file. - */ + * + * @see #openAssetFile(Uri, String) + * @see #openFileHelper(Uri, String) + */ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { throw new FileNotFoundException("No files supported by provider at " + uri); } + + /** + * This is like {@link #openFile}, but can be implemented by providers + * that need to be able to return sub-sections of files, often assets + * inside of their .apk. Note that when implementing this your clients + * must be able to deal with such files, either directly with + * {@link ContentResolver#openAssetFileDescriptor + * ContentResolver.openAssetFileDescriptor}, or by using the higher-level + * {@link ContentResolver#openInputStream ContentResolver.openInputStream} + * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream} + * methods. + * + * <p><em>Note: if you are implementing this to return a full file, you + * should create the AssetFileDescriptor with + * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with + * applications that can not handle sub-sections of files.</em></p> + * + * @param uri The URI whose file is to be opened. + * @param mode Access mode for the file. May be "r" for read-only access, + * "w" for write-only access (erasing whatever data is currently in + * the file), "wa" for write-only access to append to any existing data, + * "rw" for read and write access on any existing data, and "rwt" for read + * and write access that truncates any existing file. + * + * @return Returns a new AssetFileDescriptor which you can use to access + * the file. + * + * @throws FileNotFoundException Throws FileNotFoundException if there is + * no file associated with the given URI or the mode is invalid. + * @throws SecurityException Throws SecurityException if the caller does + * not have permission to access the file. + * + * @see #openFile(Uri, String) + * @see #openFileHelper(Uri, String) + */ + public AssetFileDescriptor openAssetFile(Uri uri, String mode) + throws FileNotFoundException { + ParcelFileDescriptor fd = openFile(uri, mode); + return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null; + } /** * Convenience for subclasses that wish to implement {@link #openFile} * by looking up a column named "_data" at the given URI. * * @param uri The URI to be opened. - * @param mode The file mode. + * @param mode The file mode. May be "r" for read-only access, + * "w" for write-only access (erasing whatever data is currently in + * the file), "wa" for write-only access to append to any existing data, + * "rw" for read and write access on any existing data, and "rwt" for read + * and write access that truncates any existing file. * * @return Returns a new ParcelFileDescriptor that can be used by the * client to access the file. @@ -489,16 +545,7 @@ public abstract class ContentProvider implements ComponentCallbacks { throw new FileNotFoundException("Column _data not found."); } - int modeBits; - if ("r".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_ONLY; - } else if ("rw".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_WRITE - | ParcelFileDescriptor.MODE_CREATE; - } else { - throw new FileNotFoundException("Bad mode for " + uri + ": " - + mode); - } + int modeBits = ContentResolver.modeToMode(uri, mode); return ParcelFileDescriptor.open(new File(path), modeBits); } @@ -507,9 +554,9 @@ public abstract class ContentProvider implements ComponentCallbacks { * This is intended for use by the sync system. If null then this * content provider is considered not syncable. * This method can be called from multiple - * threads, as described in the - * <a href="{@docRoot}intro/appmodel.html#Threads">Threading section of - * the Application Model overview</a>. + * threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. * * @return the SyncAdapter that is to be used by this ContentProvider, or null * if this ContentProvider is not syncable diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index ede2c9b..e5e3f74 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -16,6 +16,7 @@ package android.content; +import android.content.res.AssetFileDescriptor; import android.database.BulkCursorNative; import android.database.BulkCursorToCursorAdaptor; import android.database.Cursor; @@ -187,6 +188,25 @@ abstract public class ContentProviderNative extends Binder implements IContentPr return true; } + case OPEN_ASSET_FILE_TRANSACTION: + { + data.enforceInterface(IContentProvider.descriptor); + Uri url = Uri.CREATOR.createFromParcel(data); + String mode = data.readString(); + + AssetFileDescriptor fd; + fd = openAssetFile(url, mode); + reply.writeNoException(); + if (fd != null) { + reply.writeInt(1); + fd.writeToParcel(reply, + Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } else { + reply.writeInt(0); + } + return true; + } + case GET_SYNC_ADAPTER_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); @@ -413,6 +433,29 @@ final class ContentProviderProxy implements IContentProvider return fd; } + public AssetFileDescriptor openAssetFile(Uri url, String mode) + throws RemoteException, FileNotFoundException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + + data.writeInterfaceToken(IContentProvider.descriptor); + + url.writeToParcel(data, 0); + data.writeString(mode); + + mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply); + int has = reply.readInt(); + AssetFileDescriptor fd = has != 0 + ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null; + + data.recycle(); + reply.recycle(); + + return fd; + } + public ISyncAdapter getSyncAdapter() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 52f55b6..0d886ee 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -17,6 +17,7 @@ package android.content; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.database.ContentObserver; import android.database.Cursor; @@ -28,6 +29,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.text.TextUtils; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -170,119 +172,100 @@ public abstract class ContentResolver { * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li> * <li>file ({@link #SCHEME_FILE})</li> * </ul> - * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5> - * <p> - * A Uri object can be used to reference a resource in an APK file. The - * Uri should be one of the following formats: - * <ul> - * <li><code>android.resource://package_name/id_number</code><br/> - * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. - * For example <code>com.example.myapp</code><br/> - * <code>id_number</code> is the int form of the ID.<br/> - * The easiest way to construct this form is - * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre> - * </li> - * <li><code>android.resource://package_name/type/name</code><br/> - * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. - * For example <code>com.example.myapp</code><br/> - * <code>type</code> is the string form of the resource type. For example, <code>raw</code> - * or <code>drawable</code>. - * <code>name</code> is the string form of the resource name. That is, whatever the file - * name was in your res directory, without the type extension. - * The easiest way to construct this form is - * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre> - * </li> - * </ul> - * @param uri The desired "content:" URI. + * + * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information + * on these schemes. + * + * @param uri The desired URI. * @return InputStream * @throws FileNotFoundException if the provided URI could not be opened. + * @see #openAssetFileDescriptor(Uri, String) */ public final InputStream openInputStream(Uri uri) throws FileNotFoundException { String scheme = uri.getScheme(); - if (SCHEME_CONTENT.equals(scheme)) { - ParcelFileDescriptor fd = openFileDescriptor(uri, "r"); - return fd != null ? new ParcelFileDescriptor.AutoCloseInputStream(fd) : null; - } else if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { - String authority = uri.getAuthority(); - Resources r; - if (TextUtils.isEmpty(authority)) { - throw new FileNotFoundException("No authority: " + uri); - } else { - try { - r = mContext.getPackageManager().getResourcesForApplication(authority); - } catch (NameNotFoundException ex) { - throw new FileNotFoundException("No package found for authority: " + uri); - } - } - List<String> path = uri.getPathSegments(); - if (path == null) { - throw new FileNotFoundException("No path: " + uri); - } - int len = path.size(); - int id; - if (len == 1) { - try { - id = Integer.parseInt(path.get(0)); - } catch (NumberFormatException e) { - throw new FileNotFoundException("Single path segment is not a resource ID: " + uri); - } - } else if (len == 2) { - id = r.getIdentifier(path.get(1), path.get(0), authority); - } else { - throw new FileNotFoundException("More than two path segments: " + uri); - } - if (id == 0) { - throw new FileNotFoundException("No resource found for: " + uri); - } + if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { + // Note: left here to avoid breaking compatibility. May be removed + // with sufficient testing. + OpenResourceIdResult r = getResourceId(uri); try { - InputStream stream = r.openRawResource(id); + InputStream stream = r.r.openRawResource(r.id); return stream; } catch (Resources.NotFoundException ex) { - throw new FileNotFoundException("Resource ID does not exist: " + uri); + throw new FileNotFoundException("Resource does not exist: " + uri); } } else if (SCHEME_FILE.equals(scheme)) { + // Note: left here to avoid breaking compatibility. May be removed + // with sufficient testing. return new FileInputStream(uri.getPath()); } else { - throw new FileNotFoundException("Unknown scheme: " + uri); + AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r"); + try { + return fd != null ? fd.createInputStream() : null; + } catch (IOException e) { + throw new FileNotFoundException("Unable to create stream"); + } } } /** + * Synonym for {@link #openOutputStream(Uri, String) + * openOutputStream(uri, "w")}. + * @throws FileNotFoundException if the provided URI could not be opened. + */ + public final OutputStream openOutputStream(Uri uri) + throws FileNotFoundException { + return openOutputStream(uri, "w"); + } + + /** * Open a stream on to the content associated with a content URI. If there * is no data associated with the URI, FileNotFoundException is thrown. * * <h5>Accepts the following URI schemes:</h5> * <ul> * <li>content ({@link #SCHEME_CONTENT})</li> + * <li>file ({@link #SCHEME_FILE})</li> * </ul> * - * @param uri The desired "content:" URI. + * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information + * on these schemes. + * + * @param uri The desired URI. + * @param mode May be "w", "wa", "rw", or "rwt". * @return OutputStream + * @throws FileNotFoundException if the provided URI could not be opened. + * @see #openAssetFileDescriptor(Uri, String) */ - public final OutputStream openOutputStream(Uri uri) + public final OutputStream openOutputStream(Uri uri, String mode) throws FileNotFoundException { - String scheme = uri.getScheme(); - if (SCHEME_CONTENT.equals(scheme)) { - ParcelFileDescriptor fd = openFileDescriptor(uri, "rw"); - return fd != null - ? new ParcelFileDescriptor.AutoCloseOutputStream(fd) : null; - } else { - throw new FileNotFoundException("Unknown scheme: " + uri); + AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode); + try { + return fd != null ? fd.createOutputStream() : null; + } catch (IOException e) { + throw new FileNotFoundException("Unable to create stream"); } } /** * Open a raw file descriptor to access data under a "content:" URI. This - * interacts with the underlying {@link ContentProvider#openFile} - * ContentProvider.openFile()} method of the provider associated with the - * given URI, to retrieve any file stored there. + * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the + * underlying {@link ContentProvider#openFile} + * ContentProvider.openFile()} method, so will <em>not</em> work with + * providers that return sub-sections of files. If at all possible, + * you should use {@link #openAssetFileDescriptor(Uri, String)}. You + * will receive a FileNotFoundException exception if the provider returns a + * sub-section of a file. * * <h5>Accepts the following URI schemes:</h5> * <ul> * <li>content ({@link #SCHEME_CONTENT})</li> + * <li>file ({@link #SCHEME_FILE})</li> * </ul> * + * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information + * on these schemes. + * * @param uri The desired URI to open. * @param mode The file mode to use, as per {@link ContentProvider#openFile * ContentProvider.openFile}. @@ -290,32 +273,189 @@ public abstract class ContentResolver { * own this descriptor and are responsible for closing it when done. * @throws FileNotFoundException Throws FileNotFoundException of no * file exists under the URI or the mode is invalid. + * @see #openAssetFileDescriptor(Uri, String) */ public final ParcelFileDescriptor openFileDescriptor(Uri uri, String mode) throws FileNotFoundException { - IContentProvider provider = acquireProvider(uri); - if (provider == null) { - throw new FileNotFoundException("No content provider: " + uri); + AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode); + if (afd == null) { + return null; + } + + if (afd.getDeclaredLength() < 0) { + // This is a full file! + return afd.getParcelFileDescriptor(); } + + // Client can't handle a sub-section of a file, so close what + // we got and bail with an exception. try { - ParcelFileDescriptor fd = provider.openFile(uri, mode); - if(fd == null) { + afd.close(); + } catch (IOException e) { + } + + throw new FileNotFoundException("Not a whole file"); + } + + /** + * Open a raw file descriptor to access data under a "content:" URI. This + * interacts with the underlying {@link ContentProvider#openAssetFile} + * ContentProvider.openAssetFile()} method of the provider associated with the + * given URI, to retrieve any file stored there. + * + * <h5>Accepts the following URI schemes:</h5> + * <ul> + * <li>content ({@link #SCHEME_CONTENT})</li> + * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li> + * <li>file ({@link #SCHEME_FILE})</li> + * </ul> + * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5> + * <p> + * A Uri object can be used to reference a resource in an APK file. The + * Uri should be one of the following formats: + * <ul> + * <li><code>android.resource://package_name/id_number</code><br/> + * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. + * For example <code>com.example.myapp</code><br/> + * <code>id_number</code> is the int form of the ID.<br/> + * The easiest way to construct this form is + * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre> + * </li> + * <li><code>android.resource://package_name/type/name</code><br/> + * <code>package_name</code> is your package name as listed in your AndroidManifest.xml. + * For example <code>com.example.myapp</code><br/> + * <code>type</code> is the string form of the resource type. For example, <code>raw</code> + * or <code>drawable</code>. + * <code>name</code> is the string form of the resource name. That is, whatever the file + * name was in your res directory, without the type extension. + * The easiest way to construct this form is + * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre> + * </li> + * </ul> + * + * @param uri The desired URI to open. + * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile + * ContentProvider.openAssetFile}. + * @return Returns a new ParcelFileDescriptor pointing to the file. You + * own this descriptor and are responsible for closing it when done. + * @throws FileNotFoundException Throws FileNotFoundException of no + * file exists under the URI or the mode is invalid. + */ + public final AssetFileDescriptor openAssetFileDescriptor(Uri uri, + String mode) throws FileNotFoundException { + String scheme = uri.getScheme(); + if (SCHEME_ANDROID_RESOURCE.equals(scheme)) { + if (!"r".equals(mode)) { + throw new FileNotFoundException("Can't write resources: " + uri); + } + OpenResourceIdResult r = getResourceId(uri); + try { + return r.r.openRawResourceFd(r.id); + } catch (Resources.NotFoundException ex) { + throw new FileNotFoundException("Resource does not exist: " + uri); + } + } else if (SCHEME_FILE.equals(scheme)) { + ParcelFileDescriptor pfd = ParcelFileDescriptor.open( + new File(uri.getPath()), modeToMode(uri, mode)); + return new AssetFileDescriptor(pfd, 0, -1); + } else { + IContentProvider provider = acquireProvider(uri); + if (provider == null) { + throw new FileNotFoundException("No content provider: " + uri); + } + try { + AssetFileDescriptor fd = provider.openAssetFile(uri, mode); + if(fd == null) { + releaseProvider(provider); + return null; + } + ParcelFileDescriptor pfd = new ParcelFileDescriptorInner( + fd.getParcelFileDescriptor(), provider); + return new AssetFileDescriptor(pfd, fd.getStartOffset(), + fd.getDeclaredLength()); + } catch (RemoteException e) { releaseProvider(provider); - return null; + throw new FileNotFoundException("Dead content provider: " + uri); + } catch (FileNotFoundException e) { + releaseProvider(provider); + throw e; + } catch (RuntimeException e) { + releaseProvider(provider); + throw e; } - return new ParcelFileDescriptorInner(fd, provider); - } catch (RemoteException e) { - releaseProvider(provider); - throw new FileNotFoundException("Dead content provider: " + uri); - } catch (FileNotFoundException e) { - releaseProvider(provider); - throw e; - } catch (RuntimeException e) { - releaseProvider(provider); - throw e; } } + class OpenResourceIdResult { + Resources r; + int id; + } + + OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException { + String authority = uri.getAuthority(); + Resources r; + if (TextUtils.isEmpty(authority)) { + throw new FileNotFoundException("No authority: " + uri); + } else { + try { + r = mContext.getPackageManager().getResourcesForApplication(authority); + } catch (NameNotFoundException ex) { + throw new FileNotFoundException("No package found for authority: " + uri); + } + } + List<String> path = uri.getPathSegments(); + if (path == null) { + throw new FileNotFoundException("No path: " + uri); + } + int len = path.size(); + int id; + if (len == 1) { + try { + id = Integer.parseInt(path.get(0)); + } catch (NumberFormatException e) { + throw new FileNotFoundException("Single path segment is not a resource ID: " + uri); + } + } else if (len == 2) { + id = r.getIdentifier(path.get(1), path.get(0), authority); + } else { + throw new FileNotFoundException("More than two path segments: " + uri); + } + if (id == 0) { + throw new FileNotFoundException("No resource found for: " + uri); + } + OpenResourceIdResult res = new OpenResourceIdResult(); + res.r = r; + res.id = id; + return res; + } + + /** @hide */ + static public int modeToMode(Uri uri, String mode) throws FileNotFoundException { + int modeBits; + if ("r".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_READ_ONLY; + } else if ("w".equals(mode) || "wt".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY + | ParcelFileDescriptor.MODE_CREATE + | ParcelFileDescriptor.MODE_TRUNCATE; + } else if ("wa".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY + | ParcelFileDescriptor.MODE_CREATE + | ParcelFileDescriptor.MODE_APPEND; + } else if ("rw".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_READ_WRITE + | ParcelFileDescriptor.MODE_CREATE; + } else if ("rwt".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_READ_WRITE + | ParcelFileDescriptor.MODE_CREATE + | ParcelFileDescriptor.MODE_TRUNCATE; + } else { + throw new FileNotFoundException("Bad mode for " + uri + ": " + + mode); + } + return modeBits; + } + /** * Inserts a row into a table at the given URL. * diff --git a/core/java/android/content/ContentServiceNative.java b/core/java/android/content/ContentServiceNative.java index f050501..364f9ee 100644 --- a/core/java/android/content/ContentServiceNative.java +++ b/core/java/android/content/ContentServiceNative.java @@ -75,6 +75,13 @@ abstract class ContentServiceNative extends Binder implements IContentService { try { switch (code) { + case 5038: { + data.readString(); // ignore the interface token that service generated + Uri uri = Uri.parse(data.readString()); + notifyChange(uri, null, false, false); + return true; + } + case REGISTER_CONTENT_OBSERVER_TRANSACTION: { Uri uri = Uri.CREATOR.createFromParcel(data); boolean notifyForDescendents = data.readInt() != 0; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3908aa1..9a0dc9f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -127,7 +127,7 @@ public abstract class Context { * current process. */ public abstract Context getApplicationContext(); - + /** * Return a localized, styled CharSequence from the application's package's * default string table. @@ -428,7 +428,7 @@ public abstract class Context { * cursor when query is called. * * @return The contents of a newly created database with the given name. - * @throws SQLiteException if the database file could not be opened. + * @throws android.database.sqlite.SQLiteException if the database file could not be opened. * * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE @@ -1064,7 +1064,7 @@ public abstract class Context { * @see #AUDIO_SERVICE * @see android.media.AudioManager * @see #TELEPHONY_SERVICE - * @see android.internal.TelephonyManager + * @see android.telephony.TelephonyManager * @see #INPUT_METHOD_SERVICE * @see android.view.inputmethod.InputMethodManager */ @@ -1250,12 +1250,12 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a - * {@blink android.gadget.GadgetManager} for accessing wallpapers. + * {@blink android.appwidget.AppWidgetManager} for accessing AppWidgets. * * @hide * @see #getSystemService */ - public static final String GADGET_SERVICE = "gadget"; + public static final String APPWIDGET_SERVICE = "appwidget"; /** * Determine whether the given permission is allowed for a particular diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index a6ef46f..0606956 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -16,6 +16,7 @@ package android.content; +import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.CursorWindow; import android.database.IBulkCursor; @@ -52,6 +53,8 @@ public interface IContentProvider extends IInterface { String[] selectionArgs) throws RemoteException; public ParcelFileDescriptor openFile(Uri url, String mode) throws RemoteException, FileNotFoundException; + public AssetFileDescriptor openAssetFile(Uri url, String mode) + throws RemoteException, FileNotFoundException; public ISyncAdapter getSyncAdapter() throws RemoteException; /* IPC constants */ @@ -65,4 +68,5 @@ public interface IContentProvider extends IInterface { static final int GET_SYNC_ADAPTER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10; static final int BULK_INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 12; static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13; + static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 23fd171..c47b72f 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -504,6 +504,8 @@ import java.util.Set; * <li> {@link #ACTION_PACKAGE_ADDED} * <li> {@link #ACTION_PACKAGE_CHANGED} * <li> {@link #ACTION_PACKAGE_REMOVED} + * <li> {@link #ACTION_PACKAGE_RESTARTED} + * <li> {@link #ACTION_PACKAGE_DATA_CLEARED} * <li> {@link #ACTION_UID_REMOVED} * <li> {@link #ACTION_BATTERY_CHANGED} * <li> {@link #ACTION_POWER_CONNECTED} @@ -522,9 +524,9 @@ import java.util.Set; * <li> {@link #CATEGORY_ALTERNATIVE} * <li> {@link #CATEGORY_SELECTED_ALTERNATIVE} * <li> {@link #CATEGORY_LAUNCHER} + * <li> {@link #CATEGORY_INFO} * <li> {@link #CATEGORY_HOME} * <li> {@link #CATEGORY_PREFERENCE} - * <li> {@link #CATEGORY_GADGET} * <li> {@link #CATEGORY_TEST} * </ul> * @@ -1026,6 +1028,15 @@ public class Intent implements Parcelable { */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND"; + + /** + * Activity Action: Start action associated with long pressing on the + * search key. + * <p>Input: Nothing. + * <p>Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SEARCH_LONG_PRESS = "android.intent.action.SEARCH_LONG_PRESS"; // --------------------------------------------------------------------- // --------------------------------------------------------------------- @@ -1041,6 +1052,14 @@ public class Intent implements Parcelable { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON"; + + /** + * Broadcast Action: Sent when the user is present after device wakes up (e.g when the + * keyguard is gone). + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_USER_PRESENT= "android.intent.action.USER_PRESENT"; + /** * Broadcast Action: The current time has changed. Sent every * minute. You can <em>not</em> receive this through components declared @@ -1109,6 +1128,12 @@ public class Intent implements Parcelable { /** * Broadcast Action: A new application package has been installed on the * device. The data contains the name of the package. + * <p>My include the following extras: + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package. + * <li> {@link #EXTRA_REPLACING} is set to true if this is following + * an {@link #ACTION_PACKAGE_REMOVED} broadcast for the same package. + * </ul> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED"; @@ -1116,23 +1141,49 @@ public class Intent implements Parcelable { * Broadcast Action: An existing application package has been removed from * the device. The data contains the name of the package. The package * that is being installed does <em>not</em> receive this Intent. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid previously assigned + * to the package. + * <li> {@link #EXTRA_DATA_REMOVED} is set to true if the entire + * application -- data and code -- is being removed. + * <li> {@link #EXTRA_REPLACING} is set to true if this will be followed + * by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package. + * </ul> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED"; /** * Broadcast Action: An existing application package has been changed (e.g. a component has been * enabled or disabled. The data contains the name of the package. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. + * </ul> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; /** - * Broadcast Action: The user has restarted a package, all runtime state + * Broadcast Action: The user has restarted a package, and all of its + * processes have been killed. All runtime state * associated with it (processes, alarms, notifications, etc) should - * be remove. The data contains the name of the package. + * be removed. The data contains the name of the package. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. + * </ul> */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED"; /** + * Broadcast Action: The user has cleared the data of a package. This should + * be preceded by {@link #ACTION_PACKAGE_RESTARTED}, after which all of + * its persistent data is erased and this broadcast sent. The data contains + * the name of the package. + * <ul> + * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. + * </ul> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; + /** * Broadcast Action: A user ID has been removed from the system. The user * ID number is stored in the extra data under {@link #EXTRA_UID}. */ @@ -1248,7 +1299,6 @@ public class Intent implements Parcelable { /** * Broadcast Action: External media is present, and being disk-checked * The path to the mount point for the checking media is contained in the Intent.mData field. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING"; @@ -1256,7 +1306,6 @@ public class Intent implements Parcelable { /** * Broadcast Action: External media is present, but is using an incompatible fs (or is blank) * The path to the mount point for the checking media is contained in the Intent.mData field. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MEDIA_NOFS = "android.intent.action.MEDIA_NOFS"; @@ -1524,16 +1573,17 @@ public class Intent implements Parcelable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_TAB = "android.intent.category.TAB"; /** - * This activity can be embedded inside of another activity that is hosting - * gadgets. + * Should be displayed in the top-level launcher. */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) - public static final String CATEGORY_GADGET = "android.intent.category.GADGET"; + public static final String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; /** - * Should be displayed in the top-level launcher. + * Provides information about the package it is in; typically used if + * a package does not contain a {@link #CATEGORY_LAUNCHER} to provide + * a front-door to the user without having to be shown in the all apps list. */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) - public static final String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; + public static final String CATEGORY_INFO = "android.intent.category.INFO"; /** * This is the home activity, that is the first activity that is displayed * when the device boots. @@ -1552,9 +1602,6 @@ public class Intent implements Parcelable { public static final String CATEGORY_DEVELOPMENT_PREFERENCE = "android.intent.category.DEVELOPMENT_PREFERENCE"; /** * Capable of running inside a parent activity container. - * - * <p>Note: being removed in favor of more explicit categories such as - * CATEGORY_GADGET */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_EMBED = "android.intent.category.EMBED"; @@ -1677,6 +1724,22 @@ public class Intent implements Parcelable { public static final String EXTRA_UID = "android.intent.extra.UID"; /** + * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} + * intents to indicate whether this represents a full uninstall (removing + * both the code and its data) or a partial uninstall (leaving its data, + * implying that this is an update). + */ + public static final String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED"; + + /** + * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} + * intents to indicate that this is a replacement of the package, so this + * broadcast will immediately be followed by an add broadcast for a + * different version of the same package. + */ + public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING"; + + /** * Used as an int extra field in {@link android.app.AlarmManager} intents * to tell the application being invoked how many pending alarms are being * delievered with the intent. For one-shot alarms this will always be 1. @@ -1739,9 +1802,9 @@ public class Intent implements Parcelable { * next task activity) defines an atomic group of activities that the * user can move to. Tasks can be moved to the foreground and background; * all of the activities inside of a particular task always remain in - * the same order. See the - * <a href="{@docRoot}intro/appmodel.html">Application Model</a> - * documentation for more details on tasks. + * the same order. See + * <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application Fundamentals: + * Activities and Tasks</a> for more details on tasks. * * <p>This flag is generally used by activities that want * to present a "launcher" style behavior: they give the user a list of @@ -1774,9 +1837,8 @@ public class Intent implements Parcelable { * <p>This flag is ignored if * {@link #FLAG_ACTIVITY_NEW_TASK} is not set. * - * <p>See the - * <a href="{@docRoot}intro/appmodel.html">Application Model</a> - * documentation for more details on tasks. + * <p>See <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application Fundamentals: + * Activities and Tasks</a> for more details on tasks. */ public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000; /** @@ -1791,8 +1853,8 @@ public class Intent implements Parcelable { * Intent, resulting in the stack now being: A, B. * * <p>The currently running instance of task B in the above example will - * either receiving the new intent you are starting here in its - * onNewIntent() method, or be itself finished and restarting with the + * either receive the new intent you are starting here in its + * onNewIntent() method, or be itself finished and restarted with the * new intent. If it has declared its launch mode to be "multiple" (the * default) it will be finished and re-created; for all other launch modes * it will receive the Intent in the current instance. @@ -1804,9 +1866,8 @@ public class Intent implements Parcelable { * especially useful, for example, when launching an activity from the * notification manager. * - * <p>See the - * <a href="{@docRoot}intro/appmodel.html">Application Model</a> - * documentation for more details on tasks. + * <p>See <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application Fundamentals: + * Activities and Tasks</a> for more details on tasks. */ public static final int FLAG_ACTIVITY_CLEAR_TOP = 0x04000000; /** @@ -1876,7 +1937,7 @@ public class Intent implements Parcelable { */ public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000; /** - * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaving} + * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaveHint} * callback from occurring on the current frontmost activity before it is * paused as the newly-started activity is brought to the front. * @@ -1892,12 +1953,39 @@ public class Intent implements Parcelable { * activity does not think the user has acknowledged its notification. */ public static final int FLAG_ACTIVITY_NO_USER_ACTION = 0x00040000; - + /** + * If set in an Intent passed to {@link Context#startActivity Context.startActivity()}, + * this flag will cause the launched activity to be brought to the front of its + * task's history stack if it is already running. + * + * <p>For example, consider a task consisting of four activities: A, B, C, D. + * If D calls startActivity() with an Intent that resolves to the component + * of activity B, then B will be brought to the front of the history stack, + * with this resulting order: A, C, D, B. + * + * This flag will be ignored if {@link #FLAG_ACTIVITY_CLEAR_TOP} is also + * specified. + */ + public static final int FLAG_ACTIVITY_REORDER_TO_FRONT = 0X00020000; /** * If set, when sending a broadcast only registered receivers will be * called -- no BroadcastReceiver components will be launched. */ public static final int FLAG_RECEIVER_REGISTERED_ONLY = 0x40000000; + /** + * If set, when sending a broadcast <i>before boot has completed</i> only + * registered receivers will be called -- no BroadcastReceiver components + * will be launched. Sticky intent state will be recorded properly even + * if no receivers wind up being called. If {@link #FLAG_RECEIVER_REGISTERED_ONLY} + * is specified in the broadcast intent, this flag is unnecessary. + * + * <p>This flag is only for use by system sevices as a convenience to + * avoid having to implement a more complex mechanism around detection + * of boot completion. + * + * @hide + */ + public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000; // --------------------------------------------------------------------- @@ -3865,8 +3953,8 @@ public class Intent implements Parcelable { * FLAG_RECEIVER_* flags are all for use with * {@link Context#sendBroadcast(Intent) Context.sendBroadcast()}. * - * <p>See the <a href="{@docRoot}intro/appmodel.html">Application Model</a> - * documentation for important information on how some of these options impact + * <p>See the <a href="{@docRoot}guide/topics/fundamentals.html#acttask">Application Fundamentals: + * Activities and Tasks</a> documentation for important information on how some of these options impact * the behavior of your application. * * @param flags The desired flags. @@ -4141,14 +4229,11 @@ public class Intent implements Parcelable { @Override public boolean equals(Object obj) { - Intent other; - try { - other = ((FilterComparison)obj).mIntent; - } catch (ClassCastException e) { - return false; + if (obj instanceof FilterComparison) { + Intent other = ((FilterComparison)obj).mIntent; + return mIntent.filterEquals(other); } - - return mIntent.filterEquals(other); + return false; } @Override diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 6bc3774..96470c3 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -123,7 +123,7 @@ class SyncManager { private static final String SYNC_WAKE_LOCK = "SyncManagerSyncWakeLock"; private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock"; - + private Context mContext; private ContentResolver mContentResolver; @@ -249,7 +249,7 @@ class SyncManager { mSyncQueue = new SyncQueue(mSyncStorageEngine); mContext = context; - + mSyncThread = new HandlerThread("SyncHandlerThread", Process.THREAD_PRIORITY_BACKGROUND); mSyncThread.start(); mSyncHandler = new SyncHandler(mSyncThread.getLooper()); @@ -489,7 +489,7 @@ class SyncManager { // Require the precise value "yes" to discourage accidental activation. return "yes".equals(SystemProperties.get("ro.config.sync")); } - + /** * Initiate a sync. This can start a sync for all providers * (pass null to url, set onlyTicklable to false), only those @@ -515,7 +515,7 @@ class SyncManager { * syncs of a specific provider. Can be null. Is ignored * if the url is null. * @param delay how many milliseconds in the future to wait before performing this - * sync. -1 means to make this the next sync to perform. + * sync. -1 means to make this the next sync to perform. */ public void scheduleSync(Uri url, Bundle extras, long delay) { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); @@ -694,7 +694,7 @@ class SyncManager { class SyncHandlerMessagePayload { public final ActiveSyncContext activeSyncContext; public final SyncResult syncResult; - + SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) { this.activeSyncContext = syncContext; this.syncResult = syncResult; @@ -740,7 +740,7 @@ class SyncManager { if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) { newDelayInMs = maxSyncRetryTimeInSeconds * 1000; } - + SyncOperation rescheduledSyncOperation = new SyncOperation(syncOperation); rescheduledSyncOperation.setDelay(newDelayInMs); scheduleSyncOperation(rescheduledSyncOperation); @@ -786,7 +786,7 @@ class SyncManager { // key than the one we are scheduling. if (!activeIsExpedited && !hasSameKey) { rescheduleImmediately(activeSyncContext.mSyncOperation); - sendSyncFinishedOrCanceledMessage(activeSyncContext, + sendSyncFinishedOrCanceledMessage(activeSyncContext, null /* no result since this is a cancel */); } } @@ -1323,7 +1323,7 @@ class SyncManager { public SyncHandler(Looper looper) { super(looper); } - + public void handleMessage(Message msg) { handleSyncHandlerMessage(msg); } @@ -1462,6 +1462,9 @@ class SyncManager { // start it, otherwise just get out. SyncOperation syncOperation; final Sync.Settings.QueryMap syncSettings = getSyncSettings(); + final ConnectivityManager connManager = (ConnectivityManager) + mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + final boolean backgroundDataSetting = connManager.getBackgroundDataSetting(); synchronized (mSyncQueue) { while (true) { syncOperation = mSyncQueue.head(); @@ -1484,10 +1487,10 @@ class SyncManager { // skip the sync if it isn't a force and the settings are off for this provider final boolean force = syncOperation.extras.getBoolean( ContentResolver.SYNC_EXTRAS_FORCE, false); - if (!force && (!syncSettings.getBackgroundData() + if (!force && (!backgroundDataSetting || !syncSettings.getListenForNetworkTickles() || !syncSettings.getSyncProviderAutomatically( - syncOperation.authority))) { + syncOperation.authority))) { if (isLoggable) { Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation); } @@ -1669,7 +1672,7 @@ class SyncManager { * @param syncResult the SyncResult from which to read * @return the most "serious" error set in the SyncResult * @throws IllegalStateException if the SyncResult does not indicate any errors. - * If SyncResult.error() is true then it is safe to call this. + * If SyncResult.error() is true then it is safe to call this. */ private int syncResultToErrorNumber(SyncResult syncResult) { if (syncResult.syncAlreadyInProgress) return History.ERROR_SYNC_ALREADY_IN_PROGRESS; @@ -1679,7 +1682,8 @@ class SyncManager { if (syncResult.stats.numConflictDetectedExceptions > 0) return History.ERROR_CONFLICT; if (syncResult.tooManyDeletions) return History.ERROR_TOO_MANY_DELETIONS; if (syncResult.tooManyRetries) return History.ERROR_TOO_MANY_RETRIES; - throw new IllegalStateException("we are not in an error state, " + toString()); + if (syncResult.databaseError) return History.ERROR_INTERNAL; + throw new IllegalStateException("we are not in an error state, " + syncResult); } private void manageSyncNotification() { @@ -1717,7 +1721,7 @@ class SyncManager { if (mSyncNotificationInfo.isActive) { shouldInstall = shouldCancel; } else { - final boolean timeToShowNotification = + final boolean timeToShowNotification = now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY; final boolean syncIsForced = syncOperation.extras .getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false); @@ -1769,7 +1773,7 @@ class SyncManager { if (!mDataConnectionIsConnected) return; if (mAccounts == null) return; if (mStorageIsLow) return; - + // Compute the alarm fire time: // - not syncing: time of the next sync operation // - syncing, no notification: time from sync start to notification create time @@ -1850,12 +1854,12 @@ class SyncManager { clickIntent.putExtra("account", account); clickIntent.putExtra("provider", authority); clickIntent.putExtra("numDeletes", numDeletes); - + if (!isActivityAvailable(clickIntent)) { Log.w(TAG, "No activity found to handle too many deletes."); return; } - + final PendingIntent pendingIntent = PendingIntent .getActivity(mContext, 0, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT); @@ -1877,7 +1881,7 @@ class SyncManager { /** * Checks whether an activity exists on the system image for the given intent. - * + * * @param intent The intent for an activity. * @return Whether or not an activity exists. */ @@ -1892,10 +1896,10 @@ class SyncManager { return true; } } - + return false; } - + public long insertStartSyncEvent(SyncOperation syncOperation) { final int source = syncOperation.syncSource; final long now = System.currentTimeMillis(); diff --git a/core/java/android/content/TempProviderSyncAdapter.java b/core/java/android/content/TempProviderSyncAdapter.java index 78510aa..eb3a5da 100644 --- a/core/java/android/content/TempProviderSyncAdapter.java +++ b/core/java/android/content/TempProviderSyncAdapter.java @@ -1,11 +1,11 @@ package android.content; -import com.google.android.net.NetStats; - import android.database.SQLException; import android.os.Bundle; import android.os.Debug; +import android.os.NetStat; import android.os.Parcelable; +import android.os.Process; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Config; @@ -177,7 +177,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter { private final Bundle mExtras; private final SyncContext mSyncContext; private volatile boolean mIsCanceled = false; - private long[] mNetStats; + private long mInitialTxBytes; + private long mInitialRxBytes; private final SyncResult mResult; SyncThread(SyncContext syncContext, String account, Bundle extras) { @@ -193,15 +194,18 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter { if (mAdapterSyncStarted) onSyncCanceled(); if (mProviderSyncStarted) mProvider.onSyncCanceled(); // We may lose the last few sync events when canceling. Oh well. - long[] newNetStats = NetStats.getStats(); - logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult); + int uid = Process.myUid(); + logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes, + NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult); } @Override public void run() { - android.os.Process.setThreadPriority(android.os.Process.myTid(), - android.os.Process.THREAD_PRIORITY_BACKGROUND); - mNetStats = NetStats.getStats(); + Process.setThreadPriority(Process.myTid(), + Process.THREAD_PRIORITY_BACKGROUND); + int uid = Process.myUid(); + mInitialTxBytes = NetStat.getUidTxBytes(uid); + mInitialRxBytes = NetStat.getUidRxBytes(uid); try { sync(mSyncContext, mAccount, mExtras); } catch (SQLException e) { @@ -210,8 +214,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter { } finally { mSyncThread = null; if (!mIsCanceled) { - long[] newNetStats = NetStats.getStats(); - logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult); + logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes, + NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult); mSyncContext.onFinished(mResult); } } diff --git a/core/java/android/content/package.html b/core/java/android/content/package.html index 7b3e8cf..dd5360f 100644 --- a/core/java/android/content/package.html +++ b/core/java/android/content/package.html @@ -50,9 +50,9 @@ an application's resources and transfer data between applications.</p> <p>This topic includes a terminology list associated with resources, and a series of examples of using resources in code. For a complete guide on creating and - using resources, see the document on <a href="{@docRoot}devel/resources-i18n.html">Resources + using resources, see the document on <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources and Internationalization</a>. For a reference on the supported Android resource types, - see <a href="{@docRoot}reference/available-resources.html">Available Resource Types</a>.</p> + see <a href="{@docRoot}guide/topics/resources/available-resources.html">Available Resource Types</a>.</p> <p>The Android resource system keeps track of all non-code assets associated with an application. You use the {@link android.content.res.Resources Resources} class to access your @@ -175,7 +175,8 @@ download files with new appearances.</p> <p>This section gives a few quick examples you can use to make your own resources. For more details on how to define and use resources, see <a - href="{@docRoot}devel/resources-i18n.html">Resources</a>. </p> + href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources and + Internationalization</a>. </p> <a name="UsingSystemResources"></a> <h4>Using System Resources</h4> diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java index 9115225..dcc7463 100755 --- a/core/java/android/content/pm/ConfigurationInfo.java +++ b/core/java/android/content/pm/ConfigurationInfo.java @@ -16,7 +16,6 @@ package android.content.pm; -import android.content.res.Configuration; import android.os.Parcel; import android.os.Parcelable; @@ -60,12 +59,12 @@ public class ConfigurationInfo implements Parcelable { /** * Value for {@link #reqInputFeatures}: if set, indicates that the application - * requires a hard keyboard + * requires a five way navigation device */ public static final int INPUT_FEATURE_FIVE_WAY_NAV = 0x00000002; /** - * Flags associated with the application. Any combination of + * Flags associated with the input features. Any combination of * {@link #INPUT_FEATURE_HARD_KEYBOARD}, * {@link #INPUT_FEATURE_FIVE_WAY_NAV} */ diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index ea86188..d3f6f3c 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -255,8 +255,15 @@ interface IPackageManager { * retrieval of information is complete. */ void getPackageSizeInfo(in String packageName, IPackageStatsObserver observer); + + /** + * Get a list of shared libraries that are available on the + * system. + */ + String[] getSystemSharedLibraryNames(); void enterSafeMode(); + boolean isSafeMode(); void systemReady(); boolean hasSystemUidErrors(); } diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 994afc8..d9326f2 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -29,6 +29,20 @@ public class PackageInfo implements Parcelable { public String versionName; /** + * The shared user ID name of this package, as specified by the <manifest> + * tag's {@link android.R.styleable#AndroidManifest_sharedUserId sharedUserId} + * attribute. + */ + public String sharedUserId; + + /** + * The shared user ID label of this package, as specified by the <manifest> + * tag's {@link android.R.styleable#AndroidManifest_sharedUserLabel sharedUserLabel} + * attribute. + */ + public int sharedUserLabel; + + /** * Information collected from the <application> tag, or null if * there was none. */ @@ -130,6 +144,8 @@ public class PackageInfo implements Parcelable { dest.writeString(packageName); dest.writeInt(versionCode); dest.writeString(versionName); + dest.writeString(sharedUserId); + dest.writeInt(sharedUserLabel); if (applicationInfo != null) { dest.writeInt(1); applicationInfo.writeToParcel(dest, parcelableFlags); @@ -163,6 +179,8 @@ public class PackageInfo implements Parcelable { packageName = source.readString(); versionCode = source.readInt(); versionName = source.readString(); + sharedUserId = source.readString(); + sharedUserLabel = source.readInt(); int hasApp = source.readInt(); if (hasApp != 0) { applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 4b902e9..7287d9c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -480,6 +480,26 @@ public abstract class PackageManager { throws NameNotFoundException; /** + * Return a "good" intent to launch a front-door activity in a package, + * for use for example to implement an "open" button when browsing through + * packages. The current implementation will look first for a main + * activity in the category {@link Intent#CATEGORY_INFO}, next for a + * main activity in the category {@link Intent#CATEGORY_LAUNCHER}, or return + * null if neither are found. + * + * <p>Throws {@link NameNotFoundException} if a package with the given + * name can not be found on the system. + * + * @param packageName The name of the package to inspect. + * + * @return Returns either a fully-qualified Intent that can be used to + * launch the main activity in the package, or null if the package does + * not contain such an activity. + */ + public abstract Intent getLaunchIntentForPackage(String packageName) + throws NameNotFoundException; + + /** * Return an array of all of the secondary group-ids that have been * assigned to a package. * @@ -851,6 +871,16 @@ public abstract class PackageManager { * @see #GET_UNINSTALLED_PACKAGES */ public abstract List<ApplicationInfo> getInstalledApplications(int flags); + + /** + * Get a list of shared libraries that are available on the + * system. + * + * @return An array of shared library names that are + * available on the system, or null if none are installed. + * + */ + public abstract String[] getSystemSharedLibraryNames(); /** * Determine the best action to perform for a given Intent. This is how @@ -1608,4 +1638,9 @@ public abstract class PackageManager { * the manifest as found in {@link ComponentInfo}. */ public abstract int getApplicationEnabledSetting(String packageName); + + /** + * Return whether the device has been booted into safe mode. + */ + public abstract boolean isSafeMode(); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index e08f1d1..2dcb483 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -101,6 +101,8 @@ public class PackageParser { pi.packageName = p.packageName; pi.versionCode = p.mVersionCode; pi.versionName = p.mVersionName; + pi.sharedUserId = p.mSharedUserId; + pi.sharedUserLabel = p.mSharedUserLabel; pi.applicationInfo = p.applicationInfo; if ((flags&PackageManager.GET_GIDS) != 0) { pi.gids = gids; @@ -258,8 +260,9 @@ public class PackageParser { boolean assetError = true; try { assmgr = new AssetManager(); - if(assmgr.addAssetPath(mArchiveSourcePath) != 0) { - parser = assmgr.openXmlResourceParser("AndroidManifest.xml"); + int cookie = assmgr.addAssetPath(mArchiveSourcePath); + if(cookie != 0) { + parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml"); assetError = false; } else { Log.w(TAG, "Failed adding asset path:"+mArchiveSourcePath); @@ -585,6 +588,8 @@ public class PackageParser { return null; } pkg.mSharedUserId = str.intern(); + pkg.mSharedUserLabel = sa.getResourceId( + com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0); } sa.recycle(); @@ -2045,6 +2050,9 @@ public class PackageParser { // The shared user id that this package wants to use. public String mSharedUserId; + // The shared user label that this package wants to use. + public int mSharedUserLabel; + // Signatures that were read from the package. public Signature mSignatures[]; diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java index 4a073f7..231e3e2 100644 --- a/core/java/android/content/res/AssetFileDescriptor.java +++ b/core/java/android/content/res/AssetFileDescriptor.java @@ -16,9 +16,13 @@ package android.content.res; +import android.os.Parcel; import android.os.ParcelFileDescriptor; +import android.os.Parcelable; import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; /** @@ -26,16 +30,32 @@ import java.io.IOException; * opened FileDescriptor that can be used to read the data, as well as the * offset and length of that entry's data in the file. */ -public class AssetFileDescriptor { +public class AssetFileDescriptor implements Parcelable { + /** + * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)} + * and {@link #getDeclaredLength} when a length has not been declared. This means + * the data extends to the end of the file. + */ + public static final long UNKNOWN_LENGTH = -1; + private final ParcelFileDescriptor mFd; private final long mStartOffset; private final long mLength; /** * Create a new AssetFileDescriptor from the given values. + * @param fd The underlying file descriptor. + * @param startOffset The location within the file that the asset starts. + * This must be 0 if length is UNKNOWN_LENGTH. + * @param length The number of bytes of the asset, or + * {@link #UNKNOWN_LENGTH if it extends to the end of the file. */ public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length) { + if (length < 0 && startOffset != 0) { + throw new IllegalArgumentException( + "startOffset must be 0 when using UNKNOWN_LENGTH"); + } mFd = fd; mStartOffset = startOffset; mLength = length; @@ -66,9 +86,33 @@ public class AssetFileDescriptor { } /** - * Returns the total number of bytes of this asset entry's data. + * Returns the total number of bytes of this asset entry's data. May be + * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file. + * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH}, + * this will use {@link ParcelFileDescriptor#getStatSize() + * ParcelFileDescriptor.getStatSize()} to find the total size of the file, + * returning that number if found or {@link #UNKNOWN_LENGTH} if it could + * not be determined. + * + * @see #getDeclaredLength() */ public long getLength() { + if (mLength >= 0) { + return mLength; + } + long len = mFd.getStatSize(); + return len >= 0 ? len : UNKNOWN_LENGTH; + } + + /** + * Return the actual number of bytes that were declared when the + * AssetFileDescriptor was constructed. Will be + * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data + * should be read to the end of the file. + * + * @see #getDeclaredLength() + */ + public long getDeclaredLength() { return mLength; } @@ -78,4 +122,227 @@ public class AssetFileDescriptor { public void close() throws IOException { mFd.close(); } + + /** + * Create and return a new auto-close input stream for this asset. This + * will either return a full asset {@link AutoCloseInputStream}, or + * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream + * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the + * the object represents a complete file or sub-section of a file. You + * should only call this once for a particular asset. + */ + public FileInputStream createInputStream() throws IOException { + if (mLength < 0) { + return new ParcelFileDescriptor.AutoCloseInputStream(mFd); + } + return new AutoCloseInputStream(this); + } + + /** + * Create and return a new auto-close output stream for this asset. This + * will either return a full asset {@link AutoCloseOutputStream}, or + * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream + * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the + * the object represents a complete file or sub-section of a file. You + * should only call this once for a particular asset. + */ + public FileOutputStream createOutputStream() throws IOException { + if (mLength < 0) { + return new ParcelFileDescriptor.AutoCloseOutputStream(mFd); + } + return new AutoCloseOutputStream(this); + } + + @Override + public String toString() { + return "{AssetFileDescriptor: " + mFd + + " start=" + mStartOffset + " len=" + mLength + "}"; + } + + /** + * An InputStream you can create on a ParcelFileDescriptor, which will + * take care of calling {@link ParcelFileDescriptor#close + * ParcelFileDescritor.close()} for you when the stream is closed. + */ + public static class AutoCloseInputStream + extends ParcelFileDescriptor.AutoCloseInputStream { + private long mRemaining; + + public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException { + super(fd.getParcelFileDescriptor()); + super.skip(fd.getStartOffset()); + mRemaining = (int)fd.getLength(); + } + + @Override + public int available() throws IOException { + return mRemaining >= 0 + ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff) + : super.available(); + } + + @Override + public int read() throws IOException { + if (mRemaining >= 0) { + if (mRemaining == 0) return -1; + int res = super.read(); + if (res >= 0) mRemaining--; + return res; + } + + return super.read(); + } + + @Override + public int read(byte[] buffer, int offset, int count) throws IOException { + if (mRemaining >= 0) { + if (mRemaining == 0) return -1; + if (count > mRemaining) count = (int)mRemaining; + int res = super.read(buffer, offset, count); + if (res >= 0) mRemaining -= res; + return res; + } + + return super.read(buffer, offset, count); + } + + @Override + public int read(byte[] buffer) throws IOException { + if (mRemaining >= 0) { + if (mRemaining == 0) return -1; + int count = buffer.length; + if (count > mRemaining) count = (int)mRemaining; + int res = super.read(buffer, 0, count); + if (res >= 0) mRemaining -= res; + return res; + } + + return super.read(buffer); + } + + @Override + public long skip(long count) throws IOException { + if (mRemaining >= 0) { + if (mRemaining == 0) return -1; + if (count > mRemaining) count = mRemaining; + long res = super.skip(count); + if (res >= 0) mRemaining -= res; + return res; + } + + // TODO Auto-generated method stub + return super.skip(count); + } + + @Override + public void mark(int readlimit) { + if (mRemaining >= 0) { + // Not supported. + return; + } + super.mark(readlimit); + } + + @Override + public boolean markSupported() { + if (mRemaining >= 0) { + return false; + } + return super.markSupported(); + } + + @Override + public synchronized void reset() throws IOException { + if (mRemaining >= 0) { + // Not supported. + return; + } + super.reset(); + } + } + + /** + * An OutputStream you can create on a ParcelFileDescriptor, which will + * take care of calling {@link ParcelFileDescriptor#close + * ParcelFileDescritor.close()} for you when the stream is closed. + */ + public static class AutoCloseOutputStream + extends ParcelFileDescriptor.AutoCloseOutputStream { + private long mRemaining; + + public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException { + super(fd.getParcelFileDescriptor()); + if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) { + throw new IOException("Unable to seek"); + } + mRemaining = (int)fd.getLength(); + } + + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + if (mRemaining >= 0) { + if (mRemaining == 0) return; + if (count > mRemaining) count = (int)mRemaining; + super.write(buffer, offset, count); + mRemaining -= count; + return; + } + + super.write(buffer, offset, count); + } + + @Override + public void write(byte[] buffer) throws IOException { + if (mRemaining >= 0) { + if (mRemaining == 0) return; + int count = buffer.length; + if (count > mRemaining) count = (int)mRemaining; + super.write(buffer); + mRemaining -= count; + return; + } + + super.write(buffer); + } + + @Override + public void write(int oneByte) throws IOException { + if (mRemaining >= 0) { + if (mRemaining == 0) return; + super.write(oneByte); + mRemaining--; + return; + } + + super.write(oneByte); + } + } + + + /* Parcelable interface */ + public int describeContents() { + return mFd.describeContents(); + } + + public void writeToParcel(Parcel out, int flags) { + mFd.writeToParcel(out, flags); + out.writeLong(mStartOffset); + out.writeLong(mLength); + } + + AssetFileDescriptor(Parcel src) { + mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src); + mStartOffset = src.readLong(); + mLength = src.readLong(); + } + + public static final Parcelable.Creator<AssetFileDescriptor> CREATOR + = new Parcelable.Creator<AssetFileDescriptor>() { + public AssetFileDescriptor createFromParcel(Parcel in) { + return new AssetFileDescriptor(in); + } + public AssetFileDescriptor[] newArray(int size) { + return new AssetFileDescriptor[size]; + } + }; } diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index fadcb35..1c91736 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -567,8 +567,8 @@ public final class AssetManager { /** * Add an additional set of assets to the asset manager. This can be - * either a directory or ZIP file. Not for use by applications. A - * zero return value indicates failure. + * either a directory or ZIP file. Not for use by applications. Returns + * the cookie of the added asset, or 0 on failure. * {@hide} */ public native final int addAssetPath(String path); diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 4b1e678..453a83d 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -16,8 +16,6 @@ package android.content.res; -import com.google.android.collect.Lists; - import com.android.internal.util.ArrayUtils; import org.xmlpull.v1.XmlPullParser; @@ -113,7 +111,8 @@ public class ColorStateList implements Parcelable { * Create a ColorStateList from an XML document, given a set of {@link Resources}. */ public static ColorStateList createFromXml(Resources r, XmlPullParser parser) - throws XmlPullParserException, IOException { + throws XmlPullParserException, IOException { + AttributeSet attrs = Xml.asAttributeSet(parser); int type; @@ -125,19 +124,16 @@ public class ColorStateList implements Parcelable { throw new XmlPullParserException("No start tag found"); } - final ColorStateList colorStateList = createFromXmlInner(r, parser, attrs); - - return colorStateList; + return createFromXmlInner(r, parser, attrs); } /* Create from inside an XML document. Called on a parser positioned at * a tag in an XML document, tries to create a ColorStateList from that tag. * Returns null if the tag is not a valid ColorStateList. */ - private static ColorStateList createFromXmlInner(Resources r, - XmlPullParser parser, - AttributeSet attrs) - throws XmlPullParserException, IOException { + private static ColorStateList createFromXmlInner(Resources r, XmlPullParser parser, + AttributeSet attrs) throws XmlPullParserException, IOException { + ColorStateList colorStateList; final String name = parser.getName(); @@ -146,8 +142,7 @@ public class ColorStateList implements Parcelable { colorStateList = new ColorStateList(); } else { throw new XmlPullParserException( - parser.getPositionDescription() + ": invalid drawable tag " - + name); + parser.getPositionDescription() + ": invalid drawable tag " + name); } colorStateList.inflate(r, parser, attrs); @@ -304,7 +299,11 @@ public class ColorStateList implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { - dest.writeArray(mStateSpecs); + final int N = mStateSpecs.length; + dest.writeInt(N); + for (int i=0; i<N; i++) { + dest.writeIntArray(mStateSpecs[i]); + } dest.writeIntArray(mColors); } @@ -315,14 +314,11 @@ public class ColorStateList implements Parcelable { } public ColorStateList createFromParcel(Parcel source) { - Object[] o = source.readArray( - ColorStateList.class.getClassLoader()); - int[][] stateSpecs = new int[o.length][]; - - for (int i = 0; i < o.length; i++) { - stateSpecs[i] = (int[]) o[i]; + final int N = source.readInt(); + int[][] stateSpecs = new int[N][]; + for (int i=0; i<N; i++) { + stateSpecs[i] = source.createIntArray(); } - int[] colors = source.createIntArray(); return new ColorStateList(stateSpecs, colors); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 10eced6..1a963f6 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -22,7 +22,6 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import android.content.Intent; import android.graphics.Movie; import android.graphics.drawable.Drawable; import android.graphics.drawable.ColorDrawable; @@ -47,6 +46,7 @@ public class Resources { static final String TAG = "Resources"; private static final boolean DEBUG_LOAD = false; private static final boolean DEBUG_CONFIG = false; + private static final boolean TRACE_FOR_PRELOAD = false; private static final int sSdkVersion = SystemProperties.getInt( "ro.build.version.sdk", 0); @@ -58,6 +58,8 @@ public class Resources { // single-threaded, and after that these are immutable. private static final SparseArray<Drawable.ConstantState> mPreloadedDrawables = new SparseArray<Drawable.ConstantState>(); + private static final SparseArray<ColorStateList> mPreloadedColorStateLists + = new SparseArray<ColorStateList>(); private static boolean mPreloaded; /*package*/ final TypedValue mTmpValue = new TypedValue(); @@ -79,7 +81,7 @@ public class Resources { private final Configuration mConfiguration = new Configuration(); /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics(); PluralRules mPluralRule; - + /** * This exception is thrown by the resource APIs when a requested resource * can not be found. @@ -91,7 +93,7 @@ public class Resources { public NotFoundException(String name) { super(name); } - }; + } /** * Create a new Resources object on top of an existing set of assets in an @@ -399,7 +401,6 @@ public class Resources { * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. * - * @return CharSequence The string data associated with the resource, plus * @see #getDimensionPixelOffset * @see #getDimensionPixelSize */ @@ -432,7 +433,6 @@ public class Resources { * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. * - * @return CharSequence The string data associated with the resource, plus * @see #getDimension * @see #getDimensionPixelSize */ @@ -467,7 +467,6 @@ public class Resources { * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. * - * @return CharSequence The string data associated with the resource, plus * @see #getDimension * @see #getDimensionPixelOffset */ @@ -486,6 +485,36 @@ public class Resources { } /** + * Retrieve a fractional unit for a particular resource ID. + * + * @param id The desired resource identifier, as generated by the aapt + * tool. This integer encodes the package, type, and resource + * entry. The value 0 is an invalid identifier. + * @param base The base value of this fraction. In other words, a + * standard fraction is multiplied by this value. + * @param pbase The parent base value of this fraction. In other + * words, a parent fraction (nn%p) is multiplied by this + * value. + * + * @return Attribute fractional value multiplied by the appropriate + * base value. + * + * @throws NotFoundException Throws NotFoundException if the given ID does not exist. + */ + public float getFraction(int id, int base, int pbase) { + synchronized (mTmpValue) { + TypedValue value = mTmpValue; + getValue(id, value, true); + if (value.type == TypedValue.TYPE_FRACTION) { + return TypedValue.complexToFraction(value.data, base, pbase); + } + throw new NotFoundException( + "Resource ID #0x" + Integer.toHexString(id) + " type #0x" + + Integer.toHexString(value.type) + " is not valid"); + } + } + + /** * Return a drawable object associated with a particular resource ID. * Various types of objects will be returned depending on the underlying * resource -- for example, a solid color, PNG image, scalable image, etc. @@ -721,22 +750,36 @@ public class Resources { */ public InputStream openRawResource(int id) throws NotFoundException { synchronized (mTmpValue) { - TypedValue value = mTmpValue; - getValue(id, value, true); + return openRawResource(id, mTmpValue); + } + } - try { - return mAssets.openNonAsset( - value.assetCookie, value.string.toString(), - AssetManager.ACCESS_STREAMING); - } catch (Exception e) { - NotFoundException rnf = new NotFoundException( - "File " + value.string.toString() - + " from drawable resource ID #0x" - + Integer.toHexString(id)); - rnf.initCause(e); - throw rnf; - } + /** + * Open a data stream for reading a raw resource. This can only be used + * with resources whose value is the name of an asset files -- that is, it can be + * used to open drawable, sound, and raw resources; it will fail on string + * and color resources. + * + * @param id The resource identifier to open, as generated by the appt tool. + * @param value The TypedValue object to hold the resource information. + * + * @return InputStream Access to the resource data. + * + * @throws NotFoundException Throws NotFoundException if the given ID does not exist. + * + * @hide Pending API council approval + */ + public InputStream openRawResource(int id, TypedValue value) throws NotFoundException { + getValue(id, value, true); + try { + return mAssets.openNonAsset(value.assetCookie, value.string.toString(), + AssetManager.ACCESS_STREAMING); + } catch (Exception e) { + NotFoundException rnf = new NotFoundException("File " + value.string.toString() + + " from drawable resource ID #0x" + Integer.toHexString(id)); + rnf.initCause(e); + throw rnf; } } @@ -1189,7 +1232,9 @@ public class Resources { width = mMetrics.widthPixels; height = mMetrics.heightPixels; } else { + //noinspection SuspiciousNameCombination width = mMetrics.heightPixels; + //noinspection SuspiciousNameCombination height = mMetrics.widthPixels; } int keyboardHidden = mConfiguration.keyboardHidden; @@ -1302,6 +1347,7 @@ public class Resources { try { return Integer.parseInt(name); } catch (Exception e) { + // Ignore } return mAssets.getResourceIdentifier(name, defType, defPackage); } @@ -1535,21 +1581,18 @@ public class Resources { /*package*/ Drawable loadDrawable(TypedValue value, int id) throws NotFoundException { - if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT - && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { - // Should we be caching these? If we use constant colors much - // at all, most likely... - //System.out.println("Creating drawable for color: #" + - // Integer.toHexString(value.data)); - Drawable dr = new ColorDrawable(value.data); - dr.setChangingConfigurations(value.changingConfigurations); - return dr; + + if (TRACE_FOR_PRELOAD) { + // Log only framework resources + if ((id >>> 24) == 0x1) { + final String name = getResourceName(id); + if (name != null) android.util.Log.d("PreloadDrawable", name); + } } - final int key = (value.assetCookie<<24)|value.data; + final int key = (value.assetCookie << 24) | value.data; Drawable dr = getCachedDrawable(key); - //System.out.println("Cached drawable @ #" + - // Integer.toHexString(key.intValue()) + ": " + dr); + if (dr != null) { return dr; } @@ -1557,46 +1600,52 @@ public class Resources { Drawable.ConstantState cs = mPreloadedDrawables.get(key); if (cs != null) { dr = cs.newDrawable(); - } else { - if (value.string == null) { - throw new NotFoundException( - "Resource is not a Drawable (color or path): " + value); + if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && + value.type <= TypedValue.TYPE_LAST_COLOR_INT) { + dr = new ColorDrawable(value.data); } - - String file = value.string.toString(); - - if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " - + value.assetCookie + ": " + file); - - if (file.endsWith(".xml")) { - try { - XmlResourceParser rp = loadXmlResourceParser( - file, id, value.assetCookie, "drawable"); - dr = Drawable.createFromXml(this, rp); - rp.close(); - } catch (Exception e) { - NotFoundException rnf = new NotFoundException( - "File " + file + " from drawable resource ID #0x" - + Integer.toHexString(id)); - rnf.initCause(e); - throw rnf; + + if (dr == null) { + if (value.string == null) { + throw new NotFoundException( + "Resource is not a Drawable (color or path): " + value); } - - } else { - try { - InputStream is = mAssets.openNonAsset( - value.assetCookie, file, AssetManager.ACCESS_BUFFER); - // System.out.println("Opened file " + file + ": " + is); - dr = Drawable.createFromStream(is, file); - is.close(); - // System.out.println("Created stream: " + dr); - } catch (Exception e) { - NotFoundException rnf = new NotFoundException( - "File " + file + " from drawable resource ID #0x" - + Integer.toHexString(id)); - rnf.initCause(e); - throw rnf; + + String file = value.string.toString(); + + if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " + + value.assetCookie + ": " + file); + + if (file.endsWith(".xml")) { + try { + XmlResourceParser rp = loadXmlResourceParser( + file, id, value.assetCookie, "drawable"); + dr = Drawable.createFromXml(this, rp); + rp.close(); + } catch (Exception e) { + NotFoundException rnf = new NotFoundException( + "File " + file + " from drawable resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(e); + throw rnf; + } + + } else { + try { + InputStream is = mAssets.openNonAsset( + value.assetCookie, file, AssetManager.ACCESS_BUFFER); + // System.out.println("Opened file " + file + ": " + is); + dr = Drawable.createFromResourceStream(this, value, is, file); + is.close(); + // System.out.println("Created stream: " + dr); + } catch (Exception e) { + NotFoundException rnf = new NotFoundException( + "File " + file + " from drawable resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(e); + throw rnf; + } } } } @@ -1607,13 +1656,13 @@ public class Resources { if (cs != null) { if (mPreloading) { mPreloadedDrawables.put(key, cs); - } - synchronized (mTmpValue) { - //Log.i(TAG, "Saving cached drawable @ #" + - // Integer.toHexString(key.intValue()) - // + " in " + this + ": " + cs); - mDrawableCache.put( - key, new WeakReference<Drawable.ConstantState>(cs)); + } else { + synchronized (mTmpValue) { + //Log.i(TAG, "Saving cached drawable @ #" + + // Integer.toHexString(key.intValue()) + // + " in " + this + ": " + cs); + mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); + } } } } @@ -1621,7 +1670,7 @@ public class Resources { return dr; } - private final Drawable getCachedDrawable(int key) { + private Drawable getCachedDrawable(int key) { synchronized (mTmpValue) { WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key); if (wr != null) { // we have the key @@ -1642,13 +1691,40 @@ public class Resources { /*package*/ ColorStateList loadColorStateList(TypedValue value, int id) throws NotFoundException { - if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT - && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { - return ColorStateList.valueOf(value.data); + if (TRACE_FOR_PRELOAD) { + // Log only framework resources + if ((id >>> 24) == 0x1) { + final String name = getResourceName(id); + if (name != null) android.util.Log.d("PreloadColorStateList", name); + } } - final int key = (value.assetCookie<<24)|value.data; - ColorStateList csl = getCachedColorStateList(key); + final int key = (value.assetCookie << 24) | value.data; + + ColorStateList csl; + + if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && + value.type <= TypedValue.TYPE_LAST_COLOR_INT) { + + csl = mPreloadedColorStateLists.get(key); + if (csl != null) { + return csl; + } + + csl = ColorStateList.valueOf(value.data); + if (mPreloading) { + mPreloadedColorStateLists.put(key, csl); + } + + return csl; + } + + csl = getCachedColorStateList(key); + if (csl != null) { + return csl; + } + + csl = mPreloadedColorStateLists.get(key); if (csl != null) { return csl; } @@ -1680,12 +1756,16 @@ public class Resources { } if (csl != null) { - synchronized (mTmpValue) { - //Log.i(TAG, "Saving cached color state list @ #" + - // Integer.toHexString(key.intValue()) - // + " in " + this + ": " + csl); - mColorStateListCache.put( - key, new WeakReference<ColorStateList>(csl)); + if (mPreloading) { + mPreloadedColorStateLists.put(key, csl); + } else { + synchronized (mTmpValue) { + //Log.i(TAG, "Saving cached color state list @ #" + + // Integer.toHexString(key.intValue()) + // + " in " + this + ": " + csl); + mColorStateListCache.put( + key, new WeakReference<ColorStateList>(csl)); + } } } diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java index 3df7708..e684cb8 100644 --- a/core/java/android/content/res/StringBlock.java +++ b/core/java/android/content/res/StringBlock.java @@ -141,6 +141,8 @@ final class StringBlock { int type = style[i]; if (localLOGV) Log.v(TAG, "Applying style span id=" + type + ", start=" + style[i+1] + ", end=" + style[i+2]); + + if (type == ids.boldId) { buffer.setSpan(new StyleSpan(Typeface.BOLD), style[i+1], style[i+2]+1, @@ -178,9 +180,8 @@ final class StringBlock { style[i+1], style[i+2]+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } else if (type == ids.listItemId) { - buffer.setSpan(new BulletSpan(10), - style[i+1], style[i+2]+1, - Spannable.SPAN_PARAGRAPH); + addParagraphSpan(buffer, new BulletSpan(10), + style[i+1], style[i+2]+1); } else if (type == ids.marqueeId) { buffer.setSpan(TextUtils.TruncateAt.MARQUEE, style[i+1], style[i+2]+1, @@ -194,9 +195,8 @@ final class StringBlock { sub = subtag(tag, ";height="); if (sub != null) { int size = Integer.parseInt(sub); - buffer.setSpan(new Height(size), - style[i+1], style[i+2]+1, - Spannable.SPAN_PARAGRAPH); + addParagraphSpan(buffer, new Height(size), + style[i+1], style[i+2]+1); } sub = subtag(tag, ";size="); @@ -231,6 +231,28 @@ final class StringBlock { style[i+1], style[i+2]+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } + } else if (tag.startsWith("annotation;")) { + int len = tag.length(); + int next; + + for (int t = tag.indexOf(';'); t < len; t = next) { + int eq = tag.indexOf('=', t); + if (eq < 0) { + break; + } + + next = tag.indexOf(';', eq); + if (next < 0) { + next = len; + } + + String key = tag.substring(t + 1, eq); + String value = tag.substring(eq + 1, next); + + buffer.setSpan(new Annotation(key, value), + style[i+1], style[i+2]+1, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } } } @@ -239,6 +261,34 @@ final class StringBlock { return new SpannedString(buffer); } + /** + * If a translator has messed up the edges of paragraph-level markup, + * fix it to actually cover the entire paragraph that it is attached to + * instead of just whatever range they put it on. + */ + private static void addParagraphSpan(Spannable buffer, Object what, + int start, int end) { + int len = buffer.length(); + + if (start != 0 && start != len && buffer.charAt(start - 1) != '\n') { + for (start--; start > 0; start--) { + if (buffer.charAt(start - 1) == '\n') { + break; + } + } + } + + if (end != 0 && end != len && buffer.charAt(end - 1) != '\n') { + for (end++; end < len; end++) { + if (buffer.charAt(end - 1) == '\n') { + break; + } + } + } + + buffer.setSpan(what, start, end, Spannable.SPAN_PARAGRAPH); + } + private static String subtag(String full, String attribute) { int start = full.indexOf(attribute); if (start < 0) { diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 82a57dd..3a32c03 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -438,6 +438,34 @@ public class TypedArray { throw new RuntimeException(getPositionDescription() + ": You must supply a " + name + " attribute."); } + + /** + * Special version of {@link #getDimensionPixelSize} for retrieving + * {@link android.view.ViewGroup}'s layout_width and layout_height + * attributes. This is only here for performance reasons; applications + * should use {@link #getDimensionPixelSize}. + * + * @param index Index of the attribute to retrieve. + * @param defValue The default value to return if this attribute is not + * default or contains the wrong type of data. + * + * @return Attribute dimension value multiplied by the appropriate + * metric and truncated to integer pixels. + */ + public int getLayoutDimension(int index, int defValue) { + index *= AssetManager.STYLE_NUM_ENTRIES; + final int[] data = mData; + final int type = data[index+AssetManager.STYLE_TYPE]; + if (type >= TypedValue.TYPE_FIRST_INT + && type <= TypedValue.TYPE_LAST_INT) { + return data[index+AssetManager.STYLE_DATA]; + } else if (type == TypedValue.TYPE_DIMENSION) { + return TypedValue.complexToDimensionPixelSize( + data[index+AssetManager.STYLE_DATA], mResources.mMetrics); + } + + return defValue; + } /** * Retrieve a fractional unit attribute at <var>index</var>. |