summaryrefslogtreecommitdiffstats
path: root/core/java/android/content
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/content')
-rw-r--r--core/java/android/content/AsyncQueryHandler.java14
-rw-r--r--core/java/android/content/BroadcastReceiver.java32
-rw-r--r--core/java/android/content/ContentProvider.java139
-rw-r--r--core/java/android/content/ContentProviderNative.java43
-rw-r--r--core/java/android/content/ContentResolver.java320
-rw-r--r--core/java/android/content/ContentServiceNative.java7
-rw-r--r--core/java/android/content/Context.java10
-rw-r--r--core/java/android/content/IContentProvider.java4
-rw-r--r--core/java/android/content/Intent.java155
-rw-r--r--core/java/android/content/SyncManager.java42
-rw-r--r--core/java/android/content/TempProviderSyncAdapter.java24
-rw-r--r--core/java/android/content/package.html7
-rwxr-xr-xcore/java/android/content/pm/ConfigurationInfo.java5
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl7
-rw-r--r--core/java/android/content/pm/PackageInfo.java18
-rw-r--r--core/java/android/content/pm/PackageManager.java35
-rw-r--r--core/java/android/content/pm/PackageParser.java12
-rw-r--r--core/java/android/content/res/AssetFileDescriptor.java271
-rw-r--r--core/java/android/content/res/AssetManager.java4
-rw-r--r--core/java/android/content/res/ColorStateList.java36
-rw-r--r--core/java/android/content/res/Resources.java256
-rw-r--r--core/java/android/content/res/StringBlock.java62
-rw-r--r--core/java/android/content/res/TypedArray.java28
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 &lt;receiver&gt;}
* 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 &lt;manifest&gt;
+ * 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 &lt;manifest&gt;
+ * tag's {@link android.R.styleable#AndroidManifest_sharedUserLabel sharedUserLabel}
+ * attribute.
+ */
+ public int sharedUserLabel;
+
+ /**
* Information collected from the &lt;application&gt; 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>.