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/AbstractCursorEntityIterator.java112
-rw-r--r--core/java/android/content/ComponentName.java12
-rw-r--r--core/java/android/content/ContentProvider.java45
-rw-r--r--core/java/android/content/ContentProviderClient.java58
-rw-r--r--core/java/android/content/ContentProviderNative.java165
-rw-r--r--core/java/android/content/ContentResolver.java97
-rw-r--r--core/java/android/content/Entity.aidl20
-rw-r--r--core/java/android/content/Entity.java26
-rw-r--r--core/java/android/content/EntityIterator.java49
-rw-r--r--core/java/android/content/IContentProvider.java8
-rw-r--r--core/java/android/content/IEntityIterator.java187
-rw-r--r--core/java/android/content/Intent.java63
-rw-r--r--core/java/android/content/SyncManager.java12
-rw-r--r--core/java/android/content/SyncStorageEngine.java22
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java20
-rw-r--r--core/java/android/content/pm/IPackageInstallObserver.aidl2
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl7
-rw-r--r--core/java/android/content/pm/PackageManager.java110
-rw-r--r--core/java/android/content/pm/PackageParser.java88
19 files changed, 1042 insertions, 61 deletions
diff --git a/core/java/android/content/AbstractCursorEntityIterator.java b/core/java/android/content/AbstractCursorEntityIterator.java
new file mode 100644
index 0000000..bf3c4de
--- /dev/null
+++ b/core/java/android/content/AbstractCursorEntityIterator.java
@@ -0,0 +1,112 @@
+package android.content;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.RemoteException;
+
+/**
+ * An abstract class that makes it easy to implement an EntityIterator over a cursor.
+ * The user must implement {@link #newEntityFromCursorLocked}, which runs inside of a
+ * database transaction.
+ */
+public abstract class AbstractCursorEntityIterator implements EntityIterator {
+ private final Cursor mEntityCursor;
+ private final SQLiteDatabase mDb;
+ private volatile Entity mNextEntity;
+ private volatile boolean mIsClosed;
+
+ public AbstractCursorEntityIterator(SQLiteDatabase db, Cursor entityCursor) {
+ mEntityCursor = entityCursor;
+ mDb = db;
+ mNextEntity = null;
+ mIsClosed = false;
+ }
+
+ /**
+ * If there are entries left in the cursor then advance the cursor and use the new row to
+ * populate mNextEntity. If the cursor is at the end or if advancing it causes the cursor
+ * to become at the end then set mEntityCursor to null. If newEntityFromCursor returns null
+ * then continue advancing until it either returns a non-null Entity or the cursor reaches
+ * the end.
+ */
+ private void fillEntityIfAvailable() {
+ while (mNextEntity == null) {
+ if (!mEntityCursor.moveToNext()) {
+ // the cursor is at then end, bail out
+ return;
+ }
+ // This may return null if newEntityFromCursor is not able to create an entity
+ // from the current cursor position. In that case this method will loop and try
+ // the next cursor position
+ mNextEntity = newEntityFromCursorLocked(mEntityCursor);
+ }
+ mDb.beginTransaction();
+ try {
+ int position = mEntityCursor.getPosition();
+ mNextEntity = newEntityFromCursorLocked(mEntityCursor);
+ int newPosition = mEntityCursor.getPosition();
+ if (newPosition != position) {
+ throw new IllegalStateException("the cursor position changed during the call to"
+ + "newEntityFromCursorLocked, from " + position + " to " + newPosition);
+ }
+ } finally {
+ mDb.endTransaction();
+ }
+ }
+
+ /**
+ * Checks if there are more Entities accessible via this iterator. This may not be called
+ * if the iterator is already closed.
+ * @return true if the call to next() will return an Entity.
+ */
+ public boolean hasNext() {
+ if (mIsClosed) {
+ throw new IllegalStateException("calling hasNext() when the iterator is closed");
+ }
+ fillEntityIfAvailable();
+ return mNextEntity != null;
+ }
+
+ /**
+ * Returns the next Entity that is accessible via this iterator. This may not be called
+ * if the iterator is already closed.
+ * @return the next Entity that is accessible via this iterator
+ */
+ public Entity next() {
+ if (mIsClosed) {
+ throw new IllegalStateException("calling next() when the iterator is closed");
+ }
+ if (!hasNext()) {
+ throw new IllegalStateException("you may only call next() if hasNext() is true");
+ }
+
+ try {
+ return mNextEntity;
+ } finally {
+ mNextEntity = null;
+ }
+ }
+
+ /**
+ * Closes this iterator making it invalid. If is invalid for the user to call any public
+ * method on the iterator once it has been closed.
+ */
+ public void close() {
+ if (mIsClosed) {
+ throw new IllegalStateException("closing when already closed");
+ }
+ mIsClosed = true;
+ mEntityCursor.close();
+ }
+
+ /**
+ * Returns a new Entity from the current cursor position. This is called from within a
+ * database transaction. If a new entity cannot be created from this cursor position (e.g.
+ * if the row that is referred to no longer exists) then this may return null. The cursor
+ * is guaranteed to be pointing to a valid row when this call is made. The implementation
+ * of newEntityFromCursorLocked is not allowed to change the position of the cursor.
+ * @param cursor from where to read the data for the Entity
+ * @return an Entity that corresponds to the current cursor position or null
+ */
+ public abstract Entity newEntityFromCursorLocked(Cursor cursor);
+}
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 32c6864..0455202 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -18,6 +18,7 @@ package android.content;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.Comparable;
/**
* Identifier for a specific application component
@@ -29,7 +30,7 @@ import android.os.Parcelable;
* name inside of that package.
*
*/
-public final class ComponentName implements Parcelable {
+public final class ComponentName implements Parcelable, Comparable<ComponentName> {
private final String mPackage;
private final String mClass;
@@ -196,6 +197,15 @@ public final class ComponentName implements Parcelable {
public int hashCode() {
return mPackage.hashCode() + mClass.hashCode();
}
+
+ public int compareTo(ComponentName that) {
+ int v;
+ v = this.mPackage.compareTo(that.mPackage);
+ if (v != 0) {
+ return v;
+ }
+ return this.mClass.compareTo(that.mClass);
+ }
public int describeContents() {
return 0;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 3a080a0..3a8de6e 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -130,6 +130,12 @@ public abstract class ContentProvider implements ComponentCallbacks {
selectionArgs, sortOrder);
}
+ public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+ String sortOrder) {
+ checkReadPermission(uri);
+ return ContentProvider.this.queryEntities(uri, selection, selectionArgs, sortOrder);
+ }
+
public String getType(Uri uri) {
return ContentProvider.this.getType(uri);
}
@@ -145,6 +151,11 @@ public abstract class ContentProvider implements ComponentCallbacks {
return ContentProvider.this.bulkInsert(uri, initialValues);
}
+ public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) {
+ checkWritePermission(uri);
+ return ContentProvider.this.bulkInsertEntities(uri, entities);
+ }
+
public int delete(Uri uri, String selection, String[] selectionArgs) {
checkWritePermission(uri);
return ContentProvider.this.delete(uri, selection, selectionArgs);
@@ -156,6 +167,11 @@ public abstract class ContentProvider implements ComponentCallbacks {
return ContentProvider.this.update(uri, values, selection, selectionArgs);
}
+ public int[] bulkUpdateEntities(Uri uri, Entity[] entities) {
+ checkWritePermission(uri);
+ return ContentProvider.this.bulkUpdateEntities(uri, entities);
+ }
+
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
if (mode != null && mode.startsWith("rw")) checkWritePermission(uri);
@@ -328,6 +344,11 @@ public abstract class ContentProvider implements ComponentCallbacks {
public abstract Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder);
+ public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Return the MIME type of the data at the given URI. This should start with
* <code>vnd.android.cursor.item</code> for a single record,
@@ -378,6 +399,18 @@ public abstract class ContentProvider implements ComponentCallbacks {
return numValues;
}
+ public Uri insertEntity(Uri uri, Entity entity) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) {
+ Uri[] result = new Uri[entities.length];
+ for (int i = 0; i < entities.length; i++) {
+ result[i] = insertEntity(uri, entities[i]);
+ }
+ return result;
+ }
+
/**
* A request to delete one or more rows. The selection clause is applied when performing
* the deletion, allowing the operation to affect multiple rows in a
@@ -422,6 +455,18 @@ public abstract class ContentProvider implements ComponentCallbacks {
public abstract int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs);
+ public int updateEntity(Uri uri, Entity entity) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int[] bulkUpdateEntities(Uri uri, Entity[] entities) {
+ int[] result = new int[entities.length];
+ for (int i = 0; i < entities.length; i++) {
+ result[i] = updateEntity(uri, entities[i]);
+ }
+ return result;
+ }
+
/**
* Open a file blob associated with a content URI.
* This method can be called from multiple
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 9902807..ed3ed42 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package android.content;
import android.database.Cursor;
@@ -26,52 +42,78 @@ public class ContentProviderClient {
mContentResolver = contentResolver;
}
- /** {@see ContentProvider#query} */
+ /** see {@link ContentProvider#query} */
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder);
}
- /** {@see ContentProvider#getType} */
+ /** see {@link ContentProvider#getType} */
public String getType(Uri url) throws RemoteException {
return mContentProvider.getType(url);
}
- /** {@see ContentProvider#insert} */
+ /** see {@link ContentProvider#insert} */
public Uri insert(Uri url, ContentValues initialValues)
throws RemoteException {
return mContentProvider.insert(url, initialValues);
}
- /** {@see ContentProvider#bulkInsert} */
+ /** see {@link ContentProvider#bulkInsert} */
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
return mContentProvider.bulkInsert(url, initialValues);
}
- /** {@see ContentProvider#delete} */
+ /** see {@link ContentProvider#delete} */
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
return mContentProvider.delete(url, selection, selectionArgs);
}
- /** {@see ContentProvider#update} */
+ /** see {@link ContentProvider#update} */
public int update(Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
return mContentProvider.update(url, values, selection, selectionArgs);
}
- /** {@see ContentProvider#openFile} */
+ /** see {@link ContentProvider#openFile} */
public ParcelFileDescriptor openFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
return mContentProvider.openFile(url, mode);
}
- /** {@see ContentProvider#openAssetFile} */
+ /** see {@link ContentProvider#openAssetFile} */
public AssetFileDescriptor openAssetFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
return mContentProvider.openAssetFile(url, mode);
}
+ /** see {@link ContentProvider#queryEntities} */
+ public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+ String sortOrder) throws RemoteException {
+ return mContentProvider.queryEntities(uri, selection, selectionArgs, sortOrder);
+ }
+
+ /** see {@link ContentProvider#insertEntity} */
+ public Uri insertEntity(Uri uri, Entity entity) throws RemoteException {
+ return mContentProvider.bulkInsertEntities(uri, new Entity[]{entity})[0];
+ }
+
+ /** see {@link ContentProvider#bulkInsertEntities} */
+ public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) throws RemoteException {
+ return mContentProvider.bulkInsertEntities(uri, entities);
+ }
+
+ /** see {@link ContentProvider#updateEntity} */
+ public int updateEntity(Uri uri, Entity entity) throws RemoteException {
+ return mContentProvider.bulkUpdateEntities(uri, new Entity[]{entity})[0];
+ }
+
+ /** see {@link ContentProvider#bulkUpdateEntities} */
+ public int[] bulkUpdateEntities(Uri uri, Entity[] entities) throws RemoteException {
+ return mContentProvider.bulkUpdateEntities(uri, entities);
+ }
+
/**
* Call this to indicate to the system that the associated {@link ContentProvider} is no
* longer needed by this {@link ContentProviderClient}.
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index f9282e2..ac67076 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -105,6 +105,20 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
return true;
}
+ case QUERY_ENTITIES_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri url = Uri.CREATOR.createFromParcel(data);
+ String selection = data.readString();
+ String[] selectionArgs = data.readStringArray();
+ String sortOrder = data.readString();
+ EntityIterator entityIterator = queryEntities(url, selection, selectionArgs,
+ sortOrder);
+ reply.writeNoException();
+ reply.writeStrongBinder(new IEntityIteratorImpl(entityIterator).asBinder());
+ return true;
+ }
+
case GET_TYPE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
@@ -140,6 +154,40 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
return true;
}
+ case BULK_INSERT_ENTITIES_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ String className = data.readString();
+ Class entityClass = Class.forName(className);
+ int numEntities = data.readInt();
+ Entity[] entities = new Entity[numEntities];
+ for (int i = 0; i < numEntities; i++) {
+ entities[i] = (Entity) data.readParcelable(entityClass.getClassLoader());
+ }
+ Uri[] uris = bulkInsertEntities(uri, entities);
+ reply.writeNoException();
+ reply.writeTypedArray(uris, 0);
+ return true;
+ }
+
+ case BULK_UPDATE_ENTITIES_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ String className = data.readString();
+ Class entityClass = Class.forName(className);
+ int numEntities = data.readInt();
+ Entity[] entities = new Entity[numEntities];
+ for (int i = 0; i < numEntities; i++) {
+ entities[i] = (Entity) data.readParcelable(entityClass.getClassLoader());
+ }
+ int[] counts = bulkUpdateEntities(uri, entities);
+ reply.writeNoException();
+ reply.writeIntArray(counts);
+ return true;
+ }
+
case DELETE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
@@ -215,6 +263,25 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
return super.onTransact(code, data, reply, flags);
}
+ private class IEntityIteratorImpl extends IEntityIterator.Stub {
+ private final EntityIterator mEntityIterator;
+
+ IEntityIteratorImpl(EntityIterator iterator) {
+ mEntityIterator = iterator;
+ }
+ public boolean hasNext() throws RemoteException {
+ return mEntityIterator.hasNext();
+ }
+
+ public Entity next() throws RemoteException {
+ return mEntityIterator.next();
+ }
+
+ public void close() throws RemoteException {
+ mEntityIterator.close();
+ }
+ }
+
public IBinder asBinder()
{
return this;
@@ -288,7 +355,7 @@ final class ContentProviderProxy implements IContentProvider
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
IBulkCursor bulkCursor = bulkQuery(url, projection, selection, selectionArgs, sortOrder,
adaptor.getObserver(), window);
-
+
if (bulkCursor == null) {
return null;
}
@@ -296,6 +363,54 @@ final class ContentProviderProxy implements IContentProvider
return adaptor;
}
+ public EntityIterator queryEntities(Uri url, String selection, String[] selectionArgs,
+ String sortOrder)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ url.writeToParcel(data, 0);
+ data.writeString(selection);
+ data.writeStringArray(selectionArgs);
+ data.writeString(sortOrder);
+
+ mRemote.transact(IContentProvider.QUERY_ENTITIES_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+
+ IBinder entityIteratorBinder = reply.readStrongBinder();
+
+ data.recycle();
+ reply.recycle();
+
+ return new RemoteEntityIterator(IEntityIterator.Stub.asInterface(entityIteratorBinder));
+ }
+
+ static class RemoteEntityIterator implements EntityIterator {
+ private final IEntityIterator mEntityIterator;
+ RemoteEntityIterator(IEntityIterator entityIterator) {
+ mEntityIterator = entityIterator;
+ }
+
+ public boolean hasNext() throws RemoteException {
+ return mEntityIterator.hasNext();
+ }
+
+ public Entity next() throws RemoteException {
+ return mEntityIterator.next();
+ }
+
+ public void close() {
+ try {
+ mEntityIterator.close();
+ } catch (RemoteException e) {
+ // doesn't matter
+ }
+ }
+ }
+
public String getType(Uri url) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -357,6 +472,54 @@ final class ContentProviderProxy implements IContentProvider
return count;
}
+ public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+ uri.writeToParcel(data, 0);
+ data.writeString(entities[0].getClass().getName());
+ data.writeInt(entities.length);
+ for (Entity entity : entities) {
+ data.writeParcelable(entity, 0);
+ }
+
+ mRemote.transact(IContentProvider.BULK_INSERT_ENTITIES_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+ Uri[] results = new Uri[entities.length];
+ reply.readTypedArray(results, Uri.CREATOR);
+
+ data.recycle();
+ reply.recycle();
+
+ return results;
+ }
+
+ public int[] bulkUpdateEntities(Uri uri, Entity[] entities) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+ uri.writeToParcel(data, 0);
+ data.writeString(entities[0].getClass().getName());
+ data.writeInt(entities.length);
+ for (Entity entity : entities) {
+ data.writeParcelable(entity, 0);
+ }
+
+ mRemote.transact(IContentProvider.BULK_UPDATE_ENTITIES_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+ int[] results = new int[entities.length];
+ reply.readIntArray(results);
+
+ data.recycle();
+ reply.recycle();
+
+ return results;
+ }
+
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 6f3b1b6..f9bed59 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -166,6 +166,53 @@ public abstract class ContentResolver {
}
}
+ class EntityIteratorWrapper implements EntityIterator {
+ private final EntityIterator mInner;
+ private final ContentProviderClient mClient;
+
+ EntityIteratorWrapper(EntityIterator inner, ContentProviderClient client) {
+ mInner = inner;
+ mClient = client;
+ }
+
+ public boolean hasNext() throws RemoteException {
+ return mInner.hasNext();
+ }
+
+ public Entity next() throws RemoteException {
+ return mInner.next();
+ }
+
+ public void close() {
+ mClient.release();
+ mInner.close();
+ }
+
+ protected void finalize() throws Throwable {
+ close();
+ super.finalize();
+ }
+ }
+
+ public final EntityIterator queryEntity(Uri uri,
+ String selection, String[] selectionArgs, String sortOrder) throws RemoteException {
+ ContentProviderClient provider = acquireContentProviderClient(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URL " + uri);
+ }
+ try {
+ EntityIterator entityIterator =
+ provider.queryEntities(uri, selection, selectionArgs, sortOrder);
+ return new EntityIteratorWrapper(entityIterator, provider);
+ } catch(RuntimeException e) {
+ provider.release();
+ throw e;
+ } catch(RemoteException e) {
+ provider.release();
+ throw e;
+ }
+ }
+
/**
* Open a stream on to the content associated with a content URI. If there
* is no data associated with the URI, FileNotFoundException is thrown.
@@ -485,6 +532,56 @@ public abstract class ContentResolver {
}
}
+ public final Uri insertEntity(Uri uri, Entity entity) throws RemoteException {
+ ContentProviderClient provider = acquireContentProviderClient(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URL " + uri);
+ }
+ try {
+ return provider.insertEntity(uri, entity);
+ } finally {
+ provider.release();
+ }
+ }
+
+ public final int updateEntity(Uri uri, Entity entity) throws RemoteException {
+ ContentProviderClient provider = acquireContentProviderClient(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URL " + uri);
+ }
+ try {
+ return provider.updateEntity(uri, entity);
+ } finally {
+ provider.release();
+ }
+ }
+
+ public final Uri[] bulkInsertEntities(Uri uri, Entity[] entities)
+ throws RemoteException {
+ ContentProviderClient provider = acquireContentProviderClient(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URL " + uri);
+ }
+ try {
+ return provider.bulkInsertEntities(uri, entities);
+ } finally {
+ provider.release();
+ }
+ }
+
+ public final int[] bulkUpdateEntities(Uri uri, Entity[] entities)
+ throws RemoteException {
+ ContentProviderClient provider = acquireContentProviderClient(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URL " + uri);
+ }
+ try {
+ return provider.bulkUpdateEntities(uri, entities);
+ } finally {
+ provider.release();
+ }
+ }
+
/**
* Inserts multiple rows into a table at the given URL.
*
diff --git a/core/java/android/content/Entity.aidl b/core/java/android/content/Entity.aidl
new file mode 100644
index 0000000..fb201f3
--- /dev/null
+++ b/core/java/android/content/Entity.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/content/Entity.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content;
+
+parcelable Entity;
diff --git a/core/java/android/content/Entity.java b/core/java/android/content/Entity.java
new file mode 100644
index 0000000..31ea2cd
--- /dev/null
+++ b/core/java/android/content/Entity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcelable;
+
+/**
+ * Objects that pass through the ContentProvider and ContentResolver's methods that deal with
+ * Entities must implement this abstract base class and thus themselves be Parcelable.
+ */
+public abstract class Entity implements Parcelable {
+}
diff --git a/core/java/android/content/EntityIterator.java b/core/java/android/content/EntityIterator.java
new file mode 100644
index 0000000..61914cf
--- /dev/null
+++ b/core/java/android/content/EntityIterator.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.RemoteException;
+
+public interface EntityIterator {
+ /**
+ * Returns whether there are more elements to iterate, i.e. whether the
+ * iterator is positioned in front of an element.
+ *
+ * @return {@code true} if there are more elements, {@code false} otherwise.
+ * @see #next
+ * @since Android 1.0
+ */
+ public boolean hasNext() throws RemoteException;
+
+ /**
+ * Returns the next object in the iteration, i.e. returns the element in
+ * front of the iterator and advances the iterator by one position.
+ *
+ * @return the next object.
+ * @throws java.util.NoSuchElementException
+ * if there are no more elements.
+ * @see #hasNext
+ * @since Android 1.0
+ */
+ public Entity next() throws RemoteException;
+
+ /**
+ * Indicates that this iterator is no longer needed and that any associated resources
+ * may be released (such as a SQLite cursor).
+ */
+ public void close();
+}
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 0b81245..b4d1865 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -43,14 +43,19 @@ public interface IContentProvider extends IInterface {
CursorWindow window) throws RemoteException;
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException;
+ public EntityIterator queryEntities(Uri url, String selection,
+ String[] selectionArgs, String sortOrder)
+ throws RemoteException;
public String getType(Uri url) throws RemoteException;
public Uri insert(Uri url, ContentValues initialValues)
throws RemoteException;
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException;
+ public Uri[] bulkInsertEntities(Uri uri, Entity[] entities) throws RemoteException;
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException;
public int update(Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException;
+ public int[] bulkUpdateEntities(Uri uri, Entity[] entities) throws RemoteException;
public ParcelFileDescriptor openFile(Uri url, String mode)
throws RemoteException, FileNotFoundException;
public AssetFileDescriptor openAssetFile(Uri url, String mode)
@@ -67,4 +72,7 @@ public interface IContentProvider extends IInterface {
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;
+ static final int BULK_INSERT_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 16;
+ static final int BULK_UPDATE_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 17;
+ static final int QUERY_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 18;
}
diff --git a/core/java/android/content/IEntityIterator.java b/core/java/android/content/IEntityIterator.java
new file mode 100644
index 0000000..eb800f7
--- /dev/null
+++ b/core/java/android/content/IEntityIterator.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * ICPC interface methods for an iterator over Entity objects.
+ */
+public interface IEntityIterator extends IInterface {
+ /** Local-side IPC implementation stub class. */
+ public static abstract class Stub extends Binder implements IEntityIterator {
+ private static final String TAG = "IEntityIterator";
+ private static final java.lang.String DESCRIPTOR = "android.content.IEntityIterator";
+
+ /** Construct the stub at attach it to the interface. */
+ public Stub() {
+ this.attachInterface(this, DESCRIPTOR);
+ }
+ /**
+ * Cast an IBinder object into an IEntityIterator interface,
+ * generating a proxy if needed.
+ */
+ public static IEntityIterator asInterface(IBinder obj) {
+ if ((obj==null)) {
+ return null;
+ }
+ IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+ if (((iin!=null)&&(iin instanceof IEntityIterator))) {
+ return ((IEntityIterator)iin);
+ }
+ return new IEntityIterator.Stub.Proxy(obj);
+ }
+
+ public IBinder asBinder() {
+ return this;
+ }
+
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ switch (code) {
+ case INTERFACE_TRANSACTION:
+ {
+ reply.writeString(DESCRIPTOR);
+ return true;
+ }
+
+ case TRANSACTION_hasNext:
+ {
+ data.enforceInterface(DESCRIPTOR);
+ boolean _result;
+ try {
+ _result = this.hasNext();
+ } catch (Exception e) {
+ Log.e(TAG, "caught exception in hasNext()", e);
+ reply.writeException(e);
+ return true;
+ }
+ reply.writeNoException();
+ reply.writeInt(((_result)?(1):(0)));
+ return true;
+ }
+
+ case TRANSACTION_next:
+ {
+ data.enforceInterface(DESCRIPTOR);
+ Entity entity;
+ try {
+ entity = this.next();
+ } catch (RemoteException e) {
+ Log.e(TAG, "caught exception in next()", e);
+ reply.writeException(e);
+ return true;
+ }
+ reply.writeNoException();
+ reply.writeString(entity.getClass().getName());
+ reply.writeParcelable(entity, 0);
+ return true;
+ }
+
+ case TRANSACTION_close:
+ {
+ data.enforceInterface(DESCRIPTOR);
+ try {
+ this.close();
+ } catch (RemoteException e) {
+ Log.e(TAG, "caught exception in close()", e);
+ reply.writeException(e);
+ return true;
+ }
+ reply.writeNoException();
+ return true;
+ }
+ }
+ return super.onTransact(code, data, reply, flags);
+ }
+
+ private static class Proxy implements IEntityIterator {
+ private IBinder mRemote;
+ Proxy(IBinder remote) {
+ mRemote = remote;
+ }
+ public IBinder asBinder() {
+ return mRemote;
+ }
+ public java.lang.String getInterfaceDescriptor() {
+ return DESCRIPTOR;
+ }
+ public boolean hasNext() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ boolean _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_hasNext, _data, _reply, 0);
+ _reply.readException();
+ _result = (0!=_reply.readInt());
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ public Entity next() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ Entity _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_next, _data, _reply, 0);
+ _reply.readException();
+ final String className = _reply.readString();
+ final Class entityClass = Class.forName(className);
+ ClassLoader classLoader = entityClass.getClassLoader();
+ _result = (Entity) _reply.readParcelable(classLoader);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ public void close() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_close, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ }
+ static final int TRANSACTION_hasNext = (IBinder.FIRST_CALL_TRANSACTION + 0);
+ static final int TRANSACTION_next = (IBinder.FIRST_CALL_TRANSACTION + 1);
+ static final int TRANSACTION_close = (IBinder.FIRST_CALL_TRANSACTION + 2);
+ }
+ public boolean hasNext() throws RemoteException;
+ public Entity next() throws RemoteException;
+ public void close() throws RemoteException;
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ebdd588..0e6be0a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -510,6 +510,7 @@ import java.util.Set;
* <li> {@link #ACTION_BATTERY_CHANGED}
* <li> {@link #ACTION_POWER_CONNECTED}
* <li> {@link #ACTION_POWER_DISCONNECTED}
+ * <li> {@link #ACTION_SHUTDOWN}
* </ul>
*
* <h3>Standard Categories</h3>
@@ -1047,6 +1048,17 @@ public class Intent implements Parcelable {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SEARCH_LONG_PRESS = "android.intent.action.SEARCH_LONG_PRESS";
+ /**
+ * Activity Action: The user pressed the "Report" button in the crash/ANR dialog.
+ * This intent is delivered to the package which installed the application, usually
+ * the Market.
+ * <p>Input: No data is specified. The bug report is passed in using
+ * an {@link #EXTRA_BUG_REPORT} field.
+ * <p>Output: Nothing.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_APP_ERROR = "android.intent.action.APP_ERROR";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent broadcast actions (see action variable).
@@ -1271,6 +1283,15 @@ public class Intent implements Parcelable {
public static final String ACTION_POWER_DISCONNECTED =
"android.intent.action.POWER_DISCONNECTED";
/**
+ * Broadcast Action: Device is shutting down.
+ * This is broadcast when the device is being shut down (completely turned
+ * off, not sleeping). Once the broadcast is complete, the final shutdown
+ * will proceed and all unsaved data lost. Apps will not normally need
+ * to handle this, since the forground activity will be paused as well.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
+ /**
* Broadcast Action: Indicates low memory condition on the device
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -1532,6 +1553,21 @@ public class Intent implements Parcelable {
public static final String ACTION_REBOOT =
"android.intent.action.REBOOT";
+ /**
+ * Broadcast Action: a remote intent is to be broadcasted.
+ *
+ * A remote intent is used for remote RPC between devices. The remote intent
+ * is serialized and sent from one device to another device. The receiving
+ * device parses the remote intent and broadcasts it. Note that anyone can
+ * broadcast a remote intent. However, if the intent receiver of the remote intent
+ * does not trust intent broadcasts from arbitrary intent senders, it should require
+ * the sender to hold certain permissions so only trusted sender's broadcast will be
+ * let through.
+ */
+ public static final String ACTION_REMOTE_INTENT =
+ "android.intent.action.REMOTE_INTENT";
+
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
@@ -1771,6 +1807,31 @@ public class Intent implements Parcelable {
* delivered.
*/
public static final String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
+
+ /**
+ * Used as a parcelable extra field in {@link #ACTION_APP_ERROR}, containing
+ * the bug report.
+ *
+ * @hide
+ */
+ public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
+
+ /**
+ * Used as a string extra field when sending an intent to PackageInstaller to install a
+ * package. Specifies the installer package name; this package will receive the
+ * {@link #ACTION_APP_ERROR} intent.
+ *
+ * @hide
+ */
+ public static final String EXTRA_INSTALLER_PACKAGE_NAME
+ = "android.intent.extra.INSTALLER_PACKAGE_NAME";
+
+ /**
+ * Used in the extra field in the remote intent. It's astring token passed with the
+ * remote intent.
+ */
+ public static final String EXTRA_REMOTE_INTENT_TOKEN =
+ "android.intent.extra.remote_intent_token";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
@@ -1931,7 +1992,7 @@ public class Intent implements Parcelable {
/**
* If set, this marks a point in the task's activity stack that should
* be cleared when the task is reset. That is, the next time the task
- * is broad to the foreground with
+ * is brought to the foreground with
* {@link #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED} (typically as a result of
* the user re-launching it from home), this activity and all on top of
* it will be finished so that the user does not return to them, but
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 019abb8..03cfbea 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -255,6 +255,14 @@ class SyncManager implements OnAccountsUpdatedListener {
}
};
+ private BroadcastReceiver mShutdownIntentReceiver =
+ new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ Log.w(TAG, "Writing sync state before shutdown...");
+ getSyncStorageEngine().writeAllState();
+ }
+ };
+
private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
private static final String SYNC_POLL_ALARM = "android.content.syncmanager.SYNC_POLL_ALARM";
private final SyncHandler mSyncHandler;
@@ -301,6 +309,10 @@ class SyncManager implements OnAccountsUpdatedListener {
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
context.registerReceiver(mStorageIntentReceiver, intentFilter);
+ intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
+ intentFilter.setPriority(100);
+ context.registerReceiver(mShutdownIntentReceiver, intentFilter);
+
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index e20e70f..aaa763d 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -391,8 +391,7 @@ public class SyncStorageEngine extends Handler {
while (i > 0) {
i--;
AuthorityInfo authority = mAuthorities.get(i);
- if (authority.account.equals(account)
- && authority.authority.equals(providerName)) {
+ if (authority.authority.equals(providerName)) {
authority.enabled = sync;
}
}
@@ -740,7 +739,7 @@ public class SyncStorageEngine extends Handler {
}
boolean writeStatisticsNow = false;
- int day = getCurrentDay();
+ int day = getCurrentDayLocked();
if (mDayStats[0] == null) {
mDayStats[0] = new DayStats(day);
} else if (day != mDayStats[0].day) {
@@ -929,7 +928,7 @@ public class SyncStorageEngine extends Handler {
}
}
- private int getCurrentDay() {
+ private int getCurrentDayLocked() {
mCal.setTimeInMillis(System.currentTimeMillis());
final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
if (mYear != mCal.get(Calendar.YEAR)) {
@@ -1009,6 +1008,21 @@ public class SyncStorageEngine extends Handler {
return status;
}
+ public void writeAllState() {
+ synchronized (mAuthorities) {
+ // Account info is always written so no need to do it here.
+
+ if (mNumPendingFinished > 0) {
+ // Only write these if they are out of date.
+ writePendingOperationsLocked();
+ }
+
+ // Just always write these... they are likely out of date.
+ writeStatusLocked();
+ writeStatisticsLocked();
+ }
+ }
+
/**
* Read all account information back in to the initial engine state.
*/
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 173057c..88ac04c 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -113,19 +113,31 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/
public static final int FLAG_ALLOW_CLEAR_USER_DATA = 1<<6;
-
/**
- * Value for {@link #flags}: default value for the corresponding ActivityInfo flag.
- * {@hide}
+ * Value for {@link #flags}: this is set if this application has been
+ * install as an update to a built-in system application.
*/
public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7;
+
+ /**
+ * Value for {@link #flags}: this is set of the application has set
+ * its android:targetSdkVersion to something >= the current SDK version.
+ */
+ public static final int FLAG_TARGETS_SDK = 1<<8;
+
+ /**
+ * Value for {@link #flags}: this is set of the application has set
+ * its android:targetSdkVersion to something >= the current SDK version.
+ */
+ public static final int FLAG_TEST_ONLY = 1<<9;
/**
* Flags associated with the application. Any combination of
* {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
* {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
* {@link #FLAG_ALLOW_TASK_REPARENTING}
- * {@link #FLAG_ALLOW_CLEAR_USER_DATA}.
+ * {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP},
+ * {@link #FLAG_TARGETS_SDK}.
*/
public int flags = 0;
diff --git a/core/java/android/content/pm/IPackageInstallObserver.aidl b/core/java/android/content/pm/IPackageInstallObserver.aidl
index e83bbc6..6133365 100644
--- a/core/java/android/content/pm/IPackageInstallObserver.aidl
+++ b/core/java/android/content/pm/IPackageInstallObserver.aidl
@@ -19,7 +19,7 @@ package android.content.pm;
/**
* API for installation callbacks from the Package Manager.
- *
+ * @hide
*/
oneway interface IPackageInstallObserver {
void packageInstalled(in String packageName, int returnCode);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index bf963ff..57b84b2 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -140,8 +140,11 @@ interface IPackageManager {
* @param observer a callback to use to notify when the package installation in finished.
* @param flags - possible values: {@link #FORWARD_LOCK_PACKAGE},
* {@link #REPLACE_EXISITING_PACKAGE}
+ * @param installerPackageName Optional package name of the application that is performing the
+ * installation. This identifies which market the package came from.
*/
- void installPackage(in Uri packageURI, IPackageInstallObserver observer, int flags);
+ void installPackage(in Uri packageURI, IPackageInstallObserver observer, int flags,
+ in String installerPackageName);
/**
* Delete a package.
@@ -152,6 +155,8 @@ interface IPackageManager {
*/
void deletePackage(in String packageName, IPackageDeleteObserver observer, int flags);
+ String getInstallerPackageName(in String packageName);
+
void addPackageToPreferred(String packageName);
void removePackageFromPreferred(String packageName);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9e06666..8095990 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -172,6 +172,14 @@ public abstract class PackageManager {
public static final int GET_SUPPORTS_DENSITIES = 0x00008000;
/**
+ * Resolution and querying flag: if set, only filters that support the
+ * {@link android.content.Intent#CATEGORY_DEFAULT} will be considered for
+ * matching. This is a synonym for including the CATEGORY_DEFAULT in your
+ * supplied Intent.
+ */
+ public static final int MATCH_DEFAULT_ONLY = 0x00010000;
+
+ /**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has been granted to the given package.
*/
@@ -219,14 +227,6 @@ public abstract class PackageManager {
*/
public static final int SIGNATURE_UNKNOWN_PACKAGE = -4;
- /**
- * Resolution and querying flag: if set, only filters that support the
- * {@link android.content.Intent#CATEGORY_DEFAULT} will be considered for
- * matching. This is a synonym for including the CATEGORY_DEFAULT in your
- * supplied Intent.
- */
- public static final int MATCH_DEFAULT_ONLY = 0x00010000;
-
public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0;
public static final int COMPONENT_ENABLED_STATE_ENABLED = 1;
public static final int COMPONENT_ENABLED_STATE_DISABLED = 2;
@@ -235,14 +235,24 @@ public abstract class PackageManager {
* Flag parameter for {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} to
* indicate that this package should be installed as forward locked, i.e. only the app itself
* should have access to it's code and non-resource assets.
+ * @hide
*/
- public static final int FORWARD_LOCK_PACKAGE = 0x00000001;
+ public static final int INSTALL_FORWARD_LOCK = 0x00000001;
/**
* Flag parameter for {@link #installPackage} to indicate that you want to replace an already
- * installed package, if one exists
+ * installed package, if one exists.
+ * @hide
*/
- public static final int REPLACE_EXISTING_PACKAGE = 0x00000002;
+ public static final int INSTALL_REPLACE_EXISTING = 0x00000002;
+
+ /**
+ * Flag parameter for {@link #installPackage} to indicate that you want to
+ * allow test packages (those that have set android:testOnly in their
+ * manifest) to be installed.
+ * @hide
+ */
+ public static final int INSTALL_ALLOW_TEST = 0x00000004;
/**
* Flag parameter for
@@ -255,6 +265,7 @@ public abstract class PackageManager {
/**
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} on success.
+ * @hide
*/
public static final int INSTALL_SUCCEEDED = 1;
@@ -262,6 +273,7 @@ public abstract class PackageManager {
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package is
* already installed.
+ * @hide
*/
public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
@@ -269,6 +281,7 @@ public abstract class PackageManager {
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package archive
* file is invalid.
+ * @hide
*/
public static final int INSTALL_FAILED_INVALID_APK = -2;
@@ -276,13 +289,15 @@ public abstract class PackageManager {
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the URI passed in
* is invalid.
+ * @hide
*/
public static final int INSTALL_FAILED_INVALID_URI = -3;
/**
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package manager
- * service found that the device didn't have enough storage space to install the app
+ * service found that the device didn't have enough storage space to install the app.
+ * @hide
*/
public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
@@ -290,6 +305,7 @@ public abstract class PackageManager {
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if a
* package is already installed with the same name.
+ * @hide
*/
public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
@@ -297,6 +313,7 @@ public abstract class PackageManager {
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
* the requested shared user does not exist.
+ * @hide
*/
public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
@@ -305,6 +322,7 @@ public abstract class PackageManager {
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
* a previously installed package of the same name has a different signature
* than the new package (and the old package's data was not removed).
+ * @hide
*/
public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
@@ -313,6 +331,7 @@ public abstract class PackageManager {
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
* the new package is requested a shared user which is already installed on the
* device and does not have matching signature.
+ * @hide
*/
public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
@@ -320,6 +339,7 @@ public abstract class PackageManager {
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
* the new package uses a shared library that is not available.
+ * @hide
*/
public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
@@ -327,6 +347,7 @@ public abstract class PackageManager {
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
* the new package uses a shared library that is not available.
+ * @hide
*/
public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
@@ -335,6 +356,7 @@ public abstract class PackageManager {
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
* the new package failed while optimizing and validating its dex files,
* either because there was not enough storage or the validation failed.
+ * @hide
*/
public static final int INSTALL_FAILED_DEXOPT = -11;
@@ -343,6 +365,7 @@ public abstract class PackageManager {
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
* the new package failed because the current SDK version is older than
* that required by the package.
+ * @hide
*/
public static final int INSTALL_FAILED_OLDER_SDK = -12;
@@ -351,14 +374,35 @@ public abstract class PackageManager {
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
* the new package failed because it contains a content provider with the
* same authority as a provider already installed in the system.
+ * @hide
*/
public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
/**
+ * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package failed because the current SDK version is newer than
+ * that required by the package.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_NEWER_SDK = -14;
+
+ /**
+ * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package failed because it has specified that it is a test-only
+ * package and the caller has not supplied the {@link #INSTALL_ALLOW_TEST}
+ * flag.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_TEST_ONLY = -15;
+
+ /**
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser was given a path that is not a file, or does not end with the expected
* '.apk' extension.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
@@ -366,6 +410,7 @@ public abstract class PackageManager {
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser was unable to retrieve the AndroidManifest.xml file.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
@@ -373,6 +418,7 @@ public abstract class PackageManager {
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser encountered an unexpected exception.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
@@ -380,6 +426,7 @@ public abstract class PackageManager {
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser did not find any certificates in the .apk.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
@@ -387,6 +434,7 @@ public abstract class PackageManager {
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser found inconsistent certificates on the files in the .apk.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
@@ -395,6 +443,7 @@ public abstract class PackageManager {
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser encountered a CertificateEncodingException in one of the
* files in the .apk.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
@@ -402,6 +451,7 @@ public abstract class PackageManager {
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser encountered a bad or missing package name in the manifest.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
@@ -409,6 +459,7 @@ public abstract class PackageManager {
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser encountered a bad shared user id name in the manifest.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
@@ -416,6 +467,7 @@ public abstract class PackageManager {
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser encountered some structural problem in the manifest.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
@@ -424,6 +476,7 @@ public abstract class PackageManager {
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser did not find any actionable tags (instrumentation or application)
* in the manifest.
+ * @hide
*/
public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
@@ -1330,6 +1383,8 @@ public abstract class PackageManager {
}
/**
+ * @hide
+ *
* Install a package. Since this may take a little while, the result will
* be posted back to the given observer. An installation will fail if the calling context
* lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
@@ -1341,13 +1396,14 @@ public abstract class PackageManager {
* @param observer An observer callback to get notified when the package installation is
* complete. {@link IPackageInstallObserver#packageInstalled(String, int)} will be
* called when that happens. observer may be null to indicate that no callback is desired.
- * @param flags - possible values: {@link #FORWARD_LOCK_PACKAGE},
- * {@link #REPLACE_EXISTING_PACKAGE}
- *
- * @see #installPackage(android.net.Uri)
+ * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
+ * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
+ * @param installerPackageName Optional package name of the application that is performing the
+ * installation. This identifies which market the package came from.
*/
public abstract void installPackage(
- Uri packageURI, IPackageInstallObserver observer, int flags);
+ Uri packageURI, IPackageInstallObserver observer, int flags,
+ String installerPackageName);
/**
* Attempts to delete a package. Since this may take a little while, the result will
@@ -1366,6 +1422,15 @@ public abstract class PackageManager {
*/
public abstract void deletePackage(
String packageName, IPackageDeleteObserver observer, int flags);
+
+ /**
+ * Retrieve the package name of the application that installed a package. This identifies
+ * which market the package came from.
+ *
+ * @param packageName The name of the package to query
+ */
+ public abstract String getInstallerPackageName(String packageName);
+
/**
* Attempts to clear the user data directory of an application.
* Since this may take a little while, the result will
@@ -1471,17 +1536,6 @@ public abstract class PackageManager {
IPackageStatsObserver observer);
/**
- * Install a package.
- *
- * @param packageURI The location of the package file to install
- *
- * @see #installPackage(android.net.Uri, IPackageInstallObserver, int)
- */
- public void installPackage(Uri packageURI) {
- installPackage(packageURI, null, 0);
- }
-
- /**
* Add a new package to the list of preferred packages. This new package
* will be added to the front of the list (removed from its current location
* if already listed), meaning it will now be preferred over all other
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f9c4984..88907c1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -59,6 +59,7 @@ public class PackageParser {
private String mArchiveSourcePath;
private String[] mSeparateProcesses;
private int mSdkVersion;
+ private String mSdkCodename;
private int mParseError = PackageManager.INSTALL_SUCCEEDED;
@@ -123,8 +124,9 @@ public class PackageParser {
mSeparateProcesses = procs;
}
- public void setSdkVersion(int sdkVersion) {
+ public void setSdkVersion(int sdkVersion, String codename) {
mSdkVersion = sdkVersion;
+ mSdkCodename = codename;
}
private static final boolean isPackageFilename(String name) {
@@ -613,9 +615,9 @@ public class PackageParser {
int type;
final Package pkg = new Package(pkgName);
- pkg.mSystem = (flags&PARSE_IS_SYSTEM) != 0;
boolean foundApp = false;
-
+ boolean targetsSdk = false;
+
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = sa.getInteger(
@@ -721,19 +723,74 @@ public class PackageParser {
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("uses-sdk")) {
+ } else if (tagName.equals("uses-sdk")) {
if (mSdkVersion > 0) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesSdk);
- int vers = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion, 0);
+ int minVers = 0;
+ String minCode = null;
+ int targetVers = 0;
+ String targetCode = null;
+
+ TypedValue val = sa.peekValue(
+ com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+ if (val != null) {
+ if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+ targetCode = minCode = val.string.toString();
+ } else {
+ // If it's not a string, it's an integer.
+ minVers = val.data;
+ }
+ }
+
+ val = sa.peekValue(
+ com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+ if (val != null) {
+ if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+ targetCode = minCode = val.string.toString();
+ } else {
+ // If it's not a string, it's an integer.
+ targetVers = val.data;
+ }
+ }
+
+ int maxVers = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesSdk_maxSdkVersion,
+ mSdkVersion);
sa.recycle();
- if (vers > mSdkVersion) {
- outError[0] = "Requires newer sdk version #" + vers
- + " (current version is #" + mSdkVersion + ")";
+ if (targetCode != null) {
+ if (!targetCode.equals(mSdkCodename)) {
+ if (mSdkCodename != null) {
+ outError[0] = "Requires development platform " + targetCode
+ + " (current platform is " + mSdkCodename + ")";
+ } else {
+ outError[0] = "Requires development platform " + targetCode
+ + " but this is a release platform.";
+ }
+ mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
+ return null;
+ }
+ // If the code matches, it definitely targets this SDK.
+ targetsSdk = true;
+ } else if (targetVers >= mSdkVersion) {
+ // If they have explicitly targeted our current version
+ // or something after it, then note this.
+ targetsSdk = true;
+ }
+
+ if (minVers > mSdkVersion) {
+ outError[0] = "Requires newer sdk version #" + minVers
+ + " (current version is #" + mSdkVersion + ")";
+ mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
+ return null;
+ }
+
+ if (maxVers < mSdkVersion) {
+ outError[0] = "Requires older sdk version #" + maxVers
+ + " (current version is #" + mSdkVersion + ")";
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
@@ -767,6 +824,10 @@ public class PackageParser {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
}
+ if (targetsSdk) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_TARGETS_SDK;
+ }
+
if (pkg.usesLibraries.size() > 0) {
pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()];
pkg.usesLibraries.toArray(pkg.usesLibraryFiles);
@@ -1126,6 +1187,12 @@ public class PackageParser {
ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
+ false)) {
+ ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
+ }
+
String str;
str = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestApplication_permission);
@@ -2133,9 +2200,6 @@ public class PackageParser {
// If this is a 3rd party app, this is the path of the zip file.
public String mPath;
- // True if this package is part of the system image.
- public boolean mSystem;
-
// The version code declared for this package.
public int mVersionCode;