summaryrefslogtreecommitdiffstats
path: root/core/java/android/content
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2009-07-09 12:15:46 -0700
committerDianne Hackborn <hackbod@google.com>2009-07-09 12:15:46 -0700
commite3f054411b9f025848f68389c4e2c325e76b3826 (patch)
treebe0af2b731613faaba3595c306d40027ea49fa3c /core/java/android/content
parent3ff23c46978adfb691507baa3d2bf04b3f5ca001 (diff)
parent2af632f87d487deaa5b2eb71341cfc4f0c0d1173 (diff)
downloadframeworks_base-e3f054411b9f025848f68389c4e2c325e76b3826.zip
frameworks_base-e3f054411b9f025848f68389c4e2c325e76b3826.tar.gz
frameworks_base-e3f054411b9f025848f68389c4e2c325e76b3826.tar.bz2
resolved conflicts for merge of 2af632f8 to master
Diffstat (limited to 'core/java/android/content')
-rw-r--r--core/java/android/content/ContentProvider.java144
-rw-r--r--core/java/android/content/pm/PackageParser.java98
-rw-r--r--core/java/android/content/pm/PathPermission.java68
-rw-r--r--core/java/android/content/pm/ProviderInfo.java12
4 files changed, 294 insertions, 28 deletions
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4c7a9d3..5b29b97 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -17,6 +17,7 @@
package android.content;
import android.content.pm.PackageManager;
+import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
@@ -29,6 +30,7 @@ import android.database.SQLException;
import android.net.Uri;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import java.io.File;
import java.io.FileNotFoundException;
@@ -66,8 +68,10 @@ import java.util.ArrayList;
*/
public abstract class ContentProvider implements ComponentCallbacks {
private Context mContext = null;
+ private int mMyUid;
private String mReadPermission;
private String mWritePermission;
+ private PathPermission[] mPathPermissions;
private Transport mTransport = new Transport();
@@ -109,31 +113,27 @@ public abstract class ContentProvider implements ComponentCallbacks {
public IBulkCursor bulkQuery(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
IContentObserver observer, CursorWindow window) {
- checkReadPermission(uri);
+ enforceReadPermission(uri);
Cursor cursor = ContentProvider.this.query(uri, projection,
selection, selectionArgs, sortOrder);
if (cursor == null) {
return null;
}
- String wperm = getWritePermission();
return new CursorToBulkCursorAdaptor(cursor, observer,
ContentProvider.this.getClass().getName(),
- wperm == null ||
- getContext().checkCallingOrSelfPermission(getWritePermission())
- == PackageManager.PERMISSION_GRANTED,
- window);
+ hasWritePermission(uri), window);
}
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
- checkReadPermission(uri);
+ enforceReadPermission(uri);
return ContentProvider.this.query(uri, projection, selection,
selectionArgs, sortOrder);
}
public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
String sortOrder) {
- checkReadPermission(uri);
+ enforceReadPermission(uri);
return ContentProvider.this.queryEntities(uri, selection, selectionArgs, sortOrder);
}
@@ -143,17 +143,17 @@ public abstract class ContentProvider implements ComponentCallbacks {
public Uri insert(Uri uri, ContentValues initialValues) {
- checkWritePermission(uri);
+ enforceWritePermission(uri);
return ContentProvider.this.insert(uri, initialValues);
}
public int bulkInsert(Uri uri, ContentValues[] initialValues) {
- checkWritePermission(uri);
+ enforceWritePermission(uri);
return ContentProvider.this.bulkInsert(uri, initialValues);
}
public Uri insertEntity(Uri uri, Entity entities) {
- checkWritePermission(uri);
+ enforceWritePermission(uri);
return ContentProvider.this.insertEntity(uri, entities);
}
@@ -161,55 +161,84 @@ public abstract class ContentProvider implements ComponentCallbacks {
throws OperationApplicationException {
for (ContentProviderOperation operation : operations) {
if (operation.isReadOperation()) {
- checkReadPermission(operation.getUri());
+ enforceReadPermission(operation.getUri());
}
if (operation.isWriteOperation()) {
- checkWritePermission(operation.getUri());
+ enforceWritePermission(operation.getUri());
}
}
return ContentProvider.this.applyBatch(operations);
}
public int delete(Uri uri, String selection, String[] selectionArgs) {
- checkWritePermission(uri);
+ enforceWritePermission(uri);
return ContentProvider.this.delete(uri, selection, selectionArgs);
}
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
- checkWritePermission(uri);
+ enforceWritePermission(uri);
return ContentProvider.this.update(uri, values, selection, selectionArgs);
}
public int updateEntity(Uri uri, Entity entity) {
- checkWritePermission(uri);
+ enforceWritePermission(uri);
return ContentProvider.this.updateEntity(uri, entity);
}
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
- if (mode != null && mode.startsWith("rw")) checkWritePermission(uri);
- else checkReadPermission(uri);
+ if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri);
+ else enforceReadPermission(uri);
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);
+ if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri);
+ else enforceReadPermission(uri);
return ContentProvider.this.openAssetFile(uri, mode);
}
- private void checkReadPermission(Uri uri) {
+ private void enforceReadPermission(Uri uri) {
+ final int uid = Binder.getCallingUid();
+ if (uid == mMyUid) {
+ return;
+ }
+
+ final Context context = getContext();
final String rperm = getReadPermission();
final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- if (getContext().checkUriPermission(uri, rperm, null, pid, uid,
+ if (rperm == null
+ || context.checkPermission(rperm, pid, uid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+
+ PathPermission[] pps = getPathPermissions();
+ if (pps != null) {
+ final String path = uri.getPath();
+ int i = pps.length;
+ while (i > 0) {
+ i--;
+ final PathPermission pp = pps[i];
+ final String pprperm = pp.getReadPermission();
+ if (pprperm != null && pp.match(path)) {
+ if (context.checkPermission(pprperm, pid, uid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+ }
+ }
+
+ if (context.checkUriPermission(uri, pid, uid,
Intent.FLAG_GRANT_READ_URI_PERMISSION)
== PackageManager.PERMISSION_GRANTED) {
return;
}
+
String msg = "Permission Denial: reading "
+ ContentProvider.this.getClass().getName()
+ " uri " + uri + " from pid=" + Binder.getCallingPid()
@@ -218,20 +247,57 @@ public abstract class ContentProvider implements ComponentCallbacks {
throw new SecurityException(msg);
}
- private void checkWritePermission(Uri uri) {
+ private boolean hasWritePermission(Uri uri) {
+ final int uid = Binder.getCallingUid();
+ if (uid == mMyUid) {
+ return true;
+ }
+
+ final Context context = getContext();
final String wperm = getWritePermission();
final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- if (getContext().checkUriPermission(uri, null, wperm, pid, uid,
+ if (wperm == null
+ || context.checkPermission(wperm, pid, uid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+
+ PathPermission[] pps = getPathPermissions();
+ if (pps != null) {
+ final String path = uri.getPath();
+ int i = pps.length;
+ while (i > 0) {
+ i--;
+ final PathPermission pp = pps[i];
+ final String ppwperm = pp.getWritePermission();
+ if (ppwperm != null && pp.match(path)) {
+ if (context.checkPermission(ppwperm, pid, uid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ }
+ }
+ }
+
+ if (context.checkUriPermission(uri, pid, uid,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
== PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void enforceWritePermission(Uri uri) {
+ if (hasWritePermission(uri)) {
return;
}
+
String msg = "Permission Denial: writing "
+ ContentProvider.this.getClass().getName()
+ " uri " + uri + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
- + " requires " + wperm;
+ + " requires " + getWritePermission();
throw new SecurityException(msg);
}
}
@@ -291,6 +357,28 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
+ * Change the path-based permission required to read and/or write data in
+ * the content provider. This is normally set for you from its manifest
+ * information when the provider is first created.
+ *
+ * @param permissions Array of path permission descriptions.
+ */
+ protected final void setPathPermissions(PathPermission[] permissions) {
+ mPathPermissions = permissions;
+ }
+
+ /**
+ * Return the path-based permissions required for read and/or write access to
+ * this content provider. This method can be called from multiple
+ * threads, as described in
+ * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
+ * Processes and Threads</a>.
+ */
+ public final PathPermission[] getPathPermissions() {
+ return mPathPermissions;
+ }
+
+ /**
* Called when the provider is being started.
*
* @return true if the provider was successfully loaded, false otherwise
@@ -625,9 +713,11 @@ public abstract class ContentProvider implements ComponentCallbacks {
*/
if (mContext == null) {
mContext = context;
+ mMyUid = Process.myUid();
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
+ setPathPermissions(info.pathPermissions);
}
ContentProvider.this.onCreate();
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b293636..0e2deed 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1918,6 +1918,7 @@ public class PackageParser {
outInfo.metaData, outError)) == null) {
return false;
}
+
} else if (parser.getName().equals("grant-uri-permission")) {
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestGrantUriPermission);
@@ -1941,7 +1942,7 @@ public class PackageParser {
if (str != null) {
pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
}
-
+
sa.recycle();
if (pa != null) {
@@ -1956,6 +1957,101 @@ public class PackageParser {
outInfo.info.uriPermissionPatterns = newp;
}
outInfo.info.grantUriPermissions = true;
+ } else {
+ if (!RIGID_PARSER) {
+ Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
+ Log.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>");
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
+ return false;
+ }
+ XmlUtils.skipCurrentTag(parser);
+
+ } else if (parser.getName().equals("path-permission")) {
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestPathPermission);
+
+ PathPermission pa = null;
+
+ String permission = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestPathPermission_permission);
+ String readPermission = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission);
+ if (readPermission == null) {
+ readPermission = permission;
+ }
+ String writePermission = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission);
+ if (writePermission == null) {
+ writePermission = permission;
+ }
+
+ boolean havePerm = false;
+ if (readPermission != null) {
+ readPermission = readPermission.intern();
+ havePerm = true;
+ }
+ if (writePermission != null) {
+ writePermission = readPermission.intern();
+ havePerm = true;
+ }
+
+ if (!havePerm) {
+ if (!RIGID_PARSER) {
+ Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
+ Log.w(TAG, "No readPermission or writePermssion for <path-permission>");
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ outError[0] = "No readPermission or writePermssion for <path-permission>";
+ return false;
+ }
+
+ String path = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestPathPermission_path);
+ if (path != null) {
+ pa = new PathPermission(path,
+ PatternMatcher.PATTERN_LITERAL, readPermission, writePermission);
+ }
+
+ path = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix);
+ if (path != null) {
+ pa = new PathPermission(path,
+ PatternMatcher.PATTERN_PREFIX, readPermission, writePermission);
+ }
+
+ path = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern);
+ if (path != null) {
+ pa = new PathPermission(path,
+ PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission);
+ }
+
+ sa.recycle();
+
+ if (pa != null) {
+ if (outInfo.info.pathPermissions == null) {
+ outInfo.info.pathPermissions = new PathPermission[1];
+ outInfo.info.pathPermissions[0] = pa;
+ } else {
+ final int N = outInfo.info.pathPermissions.length;
+ PathPermission[] newp = new PathPermission[N+1];
+ System.arraycopy(outInfo.info.pathPermissions, 0, newp, 0, N);
+ newp[N] = pa;
+ outInfo.info.pathPermissions = newp;
+ }
+ } else {
+ if (!RIGID_PARSER) {
+ Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
+ Log.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>");
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
+ return false;
}
XmlUtils.skipCurrentTag(parser);
diff --git a/core/java/android/content/pm/PathPermission.java b/core/java/android/content/pm/PathPermission.java
new file mode 100644
index 0000000..7e49d7d
--- /dev/null
+++ b/core/java/android/content/pm/PathPermission.java
@@ -0,0 +1,68 @@
+/*
+ * 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.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+
+/**
+ * Description of permissions needed to access a particular path
+ * in a {@link ProviderInfo}.
+ */
+public class PathPermission extends PatternMatcher {
+ private final String mReadPermission;
+ private final String mWritePermission;
+
+ public PathPermission(String pattern, int type, String readPermission,
+ String writePermission) {
+ super(pattern, type);
+ mReadPermission = readPermission;
+ mWritePermission = writePermission;
+ }
+
+ public String getReadPermission() {
+ return mReadPermission;
+ }
+
+ public String getWritePermission() {
+ return mWritePermission;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mReadPermission);
+ dest.writeString(mWritePermission);
+ }
+
+ public PathPermission(Parcel src) {
+ super(src);
+ mReadPermission = src.readString();
+ mWritePermission = src.readString();
+ }
+
+ public static final Parcelable.Creator<PathPermission> CREATOR
+ = new Parcelable.Creator<PathPermission>() {
+ public PathPermission createFromParcel(Parcel source) {
+ return new PathPermission(source);
+ }
+
+ public PathPermission[] newArray(int size) {
+ return new PathPermission[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index 1d11b31..d61e95b 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -28,6 +28,7 @@ import android.os.PatternMatcher;
*/
public final class ProviderInfo extends ComponentInfo
implements Parcelable {
+
/** The name provider is published under content:// */
public String authority = null;
@@ -56,6 +57,14 @@ public final class ProviderInfo extends ComponentInfo
*/
public PatternMatcher[] uriPermissionPatterns = null;
+ /**
+ * If non-null, these are path-specific permissions that are allowed for
+ * accessing the provider. Any permissions listed here will allow a
+ * holding client to access the provider, and the provider will check
+ * the URI it provides when making calls against the patterns here.
+ */
+ public PathPermission[] pathPermissions = null;
+
/** If true, this content provider allows multiple instances of itself
* to run in different process. If false, a single instances is always
* run in {@link #processName}. */
@@ -82,6 +91,7 @@ public final class ProviderInfo extends ComponentInfo
writePermission = orig.writePermission;
grantUriPermissions = orig.grantUriPermissions;
uriPermissionPatterns = orig.uriPermissionPatterns;
+ pathPermissions = orig.pathPermissions;
multiprocess = orig.multiprocess;
initOrder = orig.initOrder;
isSyncable = orig.isSyncable;
@@ -98,6 +108,7 @@ public final class ProviderInfo extends ComponentInfo
out.writeString(writePermission);
out.writeInt(grantUriPermissions ? 1 : 0);
out.writeTypedArray(uriPermissionPatterns, parcelableFlags);
+ out.writeTypedArray(pathPermissions, parcelableFlags);
out.writeInt(multiprocess ? 1 : 0);
out.writeInt(initOrder);
out.writeInt(isSyncable ? 1 : 0);
@@ -126,6 +137,7 @@ public final class ProviderInfo extends ComponentInfo
writePermission = in.readString();
grantUriPermissions = in.readInt() != 0;
uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
+ pathPermissions = in.createTypedArray(PathPermission.CREATOR);
multiprocess = in.readInt() != 0;
initOrder = in.readInt();
isSyncable = in.readInt() != 0;