diff options
| author | Dianne Hackborn <hackbod@google.com> | 2017-02-28 17:37:35 -0800 |
|---|---|---|
| committer | Dianne Hackborn <hackbod@google.com> | 2017-03-01 12:40:57 -0800 |
| commit | cd154e95353e7af11314d2339500d6c7f85e181a (patch) | |
| tree | 4e04c20b2ff5fde52da63511ba1a68a96ab6835b /core/java/android | |
| parent | eb422ead0cd7101855db4bff50cb71321cee2377 (diff) | |
Implement issue #34842682: Add ability to limit permissions based on features
Two new attributes for <uses-permission>: android:requiredFeature and
android:requiredNotFeature.
Also update aapt to include this information in badging:
uses-permission: name='android.content.cts.REQUIRED_NOT_FEATURE_UNDEFINED' requiredNotFeature='android.software.cts.undefined'
uses-permission: name='android.content.cts.REQUIRED_MULTI_DENY' requiredFeature='android.software.cts.undefined' requiredNotFeature='android.software.cts'
Test: new PermissionFeatureTest suite.
Change-Id: Icc1f815a4675ae9dd2cb7f61730ab28b5c11228a
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/content/pm/PackageManager.java | 1 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageParser.java | 116 |
2 files changed, 86 insertions, 31 deletions
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 0482f5136b0c..85f6169412b0 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4983,6 +4983,7 @@ public abstract class PackageManager { */ public PackageInfo getPackageArchiveInfo(String archiveFilePath, @PackageInfoFlags int flags) { final PackageParser parser = new PackageParser(); + parser.setCallback(new PackageParser.CallbackImpl(this)); final File apkFile = new File(archiveFilePath); try { if ((flags & (MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE)) != 0) { diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index f801e4547658..60cc6b06bf11 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -285,6 +285,7 @@ public class PackageParser { private String[] mSeparateProcesses; private boolean mOnlyCoreApps; private DisplayMetrics mMetrics; + private Callback mCallback; private File mCacheDir; private static final int SDK_VERSION = Build.VERSION.SDK_INT; @@ -506,6 +507,37 @@ public class PackageParser { mCacheDir = cacheDir; } + /** + * Callback interface for retrieving information that may be needed while parsing + * a package. + */ + public interface Callback { + boolean hasFeature(String feature); + } + + /** + * Standard implementation of {@link Callback} on top of the public {@link PackageManager} + * class. + */ + public static final class CallbackImpl implements Callback { + private final PackageManager mPm; + + public CallbackImpl(PackageManager pm) { + mPm = pm; + } + + @Override public boolean hasFeature(String feature) { + return mPm.hasSystemFeature(feature); + } + } + + /** + * Set the {@link Callback} that can be used while parsing. + */ + public void setCallback(Callback cb) { + mCallback = cb; + } + public static final boolean isApkFile(File file) { return isApkPath(file.getName()); } @@ -2079,15 +2111,15 @@ public class PackageParser { return null; } } else if (tagName.equals(TAG_PERMISSION_GROUP)) { - if (parsePermissionGroup(pkg, flags, res, parser, outError) == null) { + if (!parsePermissionGroup(pkg, flags, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_PERMISSION)) { - if (parsePermission(pkg, res, parser, outError) == null) { + if (!parsePermission(pkg, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_PERMISSION_TREE)) { - if (parsePermissionTree(pkg, res, parser, outError) == null) { + if (!parsePermissionTree(pkg, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_USES_PERMISSION)) { @@ -2708,22 +2740,44 @@ public class PackageParser { } } + final String requiredFeature = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, 0); + + final String requiredNotfeature = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0); + sa.recycle(); - if ((maxSdkVersion == 0) || (maxSdkVersion >= Build.VERSION.RESOURCES_SDK_INT)) { - if (name != null) { - int index = pkg.requestedPermissions.indexOf(name); - if (index == -1) { - pkg.requestedPermissions.add(name.intern()); - } else { - Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: " - + name + " in package: " + pkg.packageName + " at: " - + parser.getPositionDescription()); - } - } + XmlUtils.skipCurrentTag(parser); + + if (name == null) { + return true; + } + + if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) { + return true; + } + + // Only allow requesting this permission if the platform supports the given feature. + if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(requiredFeature)) { + return true; + } + + // Only allow requesting this permission if the platform doesn't support the given feature. + if (requiredNotfeature != null && mCallback != null + && mCallback.hasFeature(requiredNotfeature)) { + return true; + } + + int index = pkg.requestedPermissions.indexOf(name); + if (index == -1) { + pkg.requestedPermissions.add(name.intern()); + } else { + Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: " + + name + " in package: " + pkg.packageName + " at: " + + parser.getPositionDescription()); } - XmlUtils.skipCurrentTag(parser); return true; } @@ -2951,7 +3005,7 @@ public class PackageParser { return true; } - private PermissionGroup parsePermissionGroup(Package owner, int flags, Resources res, + private boolean parsePermissionGroup(Package owner, int flags, Resources res, XmlResourceParser parser, String[] outError) throws XmlPullParserException, IOException { PermissionGroup perm = new PermissionGroup(owner); @@ -2968,7 +3022,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return null; + return false; } perm.info.descriptionRes = sa.getResourceId( @@ -2987,22 +3041,22 @@ public class PackageParser { if (!parseAllMetaData(res, parser, "<permission-group>", perm, outError)) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return null; + return false; } owner.permissionGroups.add(perm); - return perm; + return true; } - private Permission parsePermission(Package owner, Resources res, + private boolean parsePermission(Package owner, Resources res, XmlResourceParser parser, String[] outError) throws XmlPullParserException, IOException { - Permission perm = new Permission(owner); TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestPermission); + Permission perm = new Permission(owner); if (!parsePackageItemInfo(owner, perm.info, outError, "<permission>", sa, true /*nameRequired*/, com.android.internal.R.styleable.AndroidManifestPermission_name, @@ -3013,7 +3067,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestPermission_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return null; + return false; } // Note: don't allow this value to be a reference to a resource @@ -3040,7 +3094,7 @@ public class PackageParser { if (perm.info.protectionLevel == -1) { outError[0] = "<permission> does not specify protectionLevel"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return null; + return false; } perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel); @@ -3052,21 +3106,21 @@ public class PackageParser { outError[0] = "<permission> protectionLevel specifies a non-ephemeral flag but is " + "not based on signature type"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return null; + return false; } } if (!parseAllMetaData(res, parser, "<permission>", perm, outError)) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return null; + return false; } owner.permissions.add(perm); - return perm; + return true; } - private Permission parsePermissionTree(Package owner, Resources res, + private boolean parsePermissionTree(Package owner, Resources res, XmlResourceParser parser, String[] outError) throws XmlPullParserException, IOException { Permission perm = new Permission(owner); @@ -3084,7 +3138,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return null; + return false; } sa.recycle(); @@ -3097,7 +3151,7 @@ public class PackageParser { outError[0] = "<permission-tree> name has less than three segments: " + perm.info.name; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return null; + return false; } perm.info.descriptionRes = 0; @@ -3107,12 +3161,12 @@ public class PackageParser { if (!parseAllMetaData(res, parser, "<permission-tree>", perm, outError)) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return null; + return false; } owner.permissions.add(perm); - return perm; + return true; } private Instrumentation parseInstrumentation(Package owner, Resources res, |
