Loading api/current.xml +113 −0 Original line number Diff line number Diff line Loading @@ -496,6 +496,17 @@ visibility="public" > </field> <field name="GLOBAL_SEARCH" type="java.lang.String" transient="false" volatile="false" value=""android.permission.GLOBAL_SEARCH"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> <field name="HARDWARE_TEST" type="java.lang.String" transient="false" Loading Loading @@ -28253,6 +28264,17 @@ visibility="public" > </method> <method name="getPathPermissions" return="android.content.pm.PathPermission[]" abstract="false" native="false" synchronized="false" static="false" final="true" deprecated="not deprecated" visibility="public" > </method> <method name="getReadPermission" return="java.lang.String" abstract="false" Loading Loading @@ -28455,6 +28477,19 @@ <parameter name="sortOrder" type="java.lang.String"> </parameter> </method> <method name="setPathPermissions" return="void" abstract="false" native="false" synchronized="false" static="false" final="true" deprecated="not deprecated" visibility="protected" > <parameter name="permissions" type="android.content.pm.PathPermission[]"> </parameter> </method> <method name="setReadPermission" return="void" abstract="false" Loading Loading @@ -41340,6 +41375,73 @@ > </field> </class> <class name="PathPermission" extends="android.os.PatternMatcher" abstract="false" static="false" final="false" deprecated="not deprecated" visibility="public" > <constructor name="PathPermission" type="android.content.pm.PathPermission" static="false" final="false" deprecated="not deprecated" visibility="public" > <parameter name="pattern" type="java.lang.String"> </parameter> <parameter name="type" type="int"> </parameter> <parameter name="readPermission" type="java.lang.String"> </parameter> <parameter name="writePermission" type="java.lang.String"> </parameter> </constructor> <constructor name="PathPermission" type="android.content.pm.PathPermission" static="false" final="false" deprecated="not deprecated" visibility="public" > <parameter name="src" type="android.os.Parcel"> </parameter> </constructor> <method name="getReadPermission" return="java.lang.String" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <method name="getWritePermission" return="java.lang.String" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <field name="CREATOR" type="android.os.Parcelable.Creator" transient="false" volatile="false" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> </class> <class name="PermissionGroupInfo" extends="android.content.pm.PackageItemInfo" abstract="false" Loading Loading @@ -41669,6 +41771,17 @@ visibility="public" > </field> <field name="pathPermissions" type="android.content.pm.PathPermission[]" transient="false" volatile="false" value="null" static="false" final="false" deprecated="not deprecated" visibility="public" > </field> <field name="readPermission" type="java.lang.String" transient="false" core/java/android/content/ContentProvider.java +117 −27 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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); } Loading @@ -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); } Loading @@ -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() Loading @@ -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); } } Loading Loading @@ -290,6 +356,28 @@ public abstract class ContentProvider implements ComponentCallbacks { return mWritePermission; } /** * 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. * Loading Loading @@ -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(); } Loading core/java/android/content/pm/PackageParser.java +97 −1 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading core/java/android/content/pm/PathPermission.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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 core/java/android/content/pm/ProviderInfo.java +12 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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}. */ Loading @@ -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; Loading @@ -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); Loading Loading @@ -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; Loading Loading
api/current.xml +113 −0 Original line number Diff line number Diff line Loading @@ -496,6 +496,17 @@ visibility="public" > </field> <field name="GLOBAL_SEARCH" type="java.lang.String" transient="false" volatile="false" value=""android.permission.GLOBAL_SEARCH"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> <field name="HARDWARE_TEST" type="java.lang.String" transient="false" Loading Loading @@ -28253,6 +28264,17 @@ visibility="public" > </method> <method name="getPathPermissions" return="android.content.pm.PathPermission[]" abstract="false" native="false" synchronized="false" static="false" final="true" deprecated="not deprecated" visibility="public" > </method> <method name="getReadPermission" return="java.lang.String" abstract="false" Loading Loading @@ -28455,6 +28477,19 @@ <parameter name="sortOrder" type="java.lang.String"> </parameter> </method> <method name="setPathPermissions" return="void" abstract="false" native="false" synchronized="false" static="false" final="true" deprecated="not deprecated" visibility="protected" > <parameter name="permissions" type="android.content.pm.PathPermission[]"> </parameter> </method> <method name="setReadPermission" return="void" abstract="false" Loading Loading @@ -41340,6 +41375,73 @@ > </field> </class> <class name="PathPermission" extends="android.os.PatternMatcher" abstract="false" static="false" final="false" deprecated="not deprecated" visibility="public" > <constructor name="PathPermission" type="android.content.pm.PathPermission" static="false" final="false" deprecated="not deprecated" visibility="public" > <parameter name="pattern" type="java.lang.String"> </parameter> <parameter name="type" type="int"> </parameter> <parameter name="readPermission" type="java.lang.String"> </parameter> <parameter name="writePermission" type="java.lang.String"> </parameter> </constructor> <constructor name="PathPermission" type="android.content.pm.PathPermission" static="false" final="false" deprecated="not deprecated" visibility="public" > <parameter name="src" type="android.os.Parcel"> </parameter> </constructor> <method name="getReadPermission" return="java.lang.String" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <method name="getWritePermission" return="java.lang.String" abstract="false" native="false" synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > </method> <field name="CREATOR" type="android.os.Parcelable.Creator" transient="false" volatile="false" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> </class> <class name="PermissionGroupInfo" extends="android.content.pm.PackageItemInfo" abstract="false" Loading Loading @@ -41669,6 +41771,17 @@ visibility="public" > </field> <field name="pathPermissions" type="android.content.pm.PathPermission[]" transient="false" volatile="false" value="null" static="false" final="false" deprecated="not deprecated" visibility="public" > </field> <field name="readPermission" type="java.lang.String" transient="false"
core/java/android/content/ContentProvider.java +117 −27 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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); } Loading @@ -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); } Loading @@ -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() Loading @@ -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); } } Loading Loading @@ -290,6 +356,28 @@ public abstract class ContentProvider implements ComponentCallbacks { return mWritePermission; } /** * 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. * Loading Loading @@ -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(); } Loading
core/java/android/content/pm/PackageParser.java +97 −1 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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); Loading
core/java/android/content/pm/PathPermission.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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
core/java/android/content/pm/ProviderInfo.java +12 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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}. */ Loading @@ -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; Loading @@ -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); Loading Loading @@ -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; Loading