From cd154e95353e7af11314d2339500d6c7f85e181a Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 28 Feb 2017 17:37:35 -0800 Subject: [PATCH] Implement issue #34842682: Add ability to limit permissions based on features Two new attributes for : 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 --- api/current.txt | 6 +- api/system-current.txt | 6 +- api/test-current.txt | 6 +- .../android/content/pm/PackageManager.java | 1 + .../android/content/pm/PackageParser.java | 116 +++++++++++++----- core/res/res/values/attrs_manifest.xml | 8 ++ core/res/res/values/public.xml | 7 +- .../server/pm/PackageManagerService.java | 14 ++- .../server/pm/ParallelPackageParser.java | 5 +- .../server/pm/ParallelPackageParserTest.java | 2 +- tools/aapt/Command.cpp | 18 ++- 11 files changed, 142 insertions(+), 47 deletions(-) diff --git a/api/current.txt b/api/current.txt index 22dbe7b514624..8b112e1617031 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1004,7 +1004,7 @@ package android { field public static final int preferenceStyle = 16842894; // 0x101008e field public static final int presentationTheme = 16843712; // 0x10103c0 field public static final int previewImage = 16843482; // 0x10102da - field public static final int primaryContentAlpha = 16843367; // 0x1010267 + field public static final int primaryContentAlpha = 16844117; // 0x1010555 field public static final int priority = 16842780; // 0x101001c field public static final int privateImeOptions = 16843299; // 0x1010223 field public static final int process = 16842769; // 0x1010011 @@ -1060,7 +1060,9 @@ package android { field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 + field public static final int requiredFeature = 16844119; // 0x1010557 field public static final int requiredForAllUsers = 16843728; // 0x10103d0 + field public static final int requiredNotFeature = 16844120; // 0x1010558 field public static final int requiresFadingEdge = 16843685; // 0x10103a5 field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364 field public static final int resizeClip = 16843983; // 0x10104cf @@ -1129,7 +1131,7 @@ package android { field public static final int searchSuggestSelection = 16843224; // 0x10101d8 field public static final int searchSuggestThreshold = 16843373; // 0x101026d field public static final int searchViewStyle = 16843904; // 0x1010480 - field public static final int secondaryContentAlpha = 16843688; // 0x10103a8 + field public static final int secondaryContentAlpha = 16844118; // 0x1010556 field public static final int secondaryProgress = 16843064; // 0x1010138 field public static final int secondaryProgressTint = 16843879; // 0x1010467 field public static final int secondaryProgressTintMode = 16843880; // 0x1010468 diff --git a/api/system-current.txt b/api/system-current.txt index daf8cefeb4435..93346a396c8a2 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1117,7 +1117,7 @@ package android { field public static final int preferenceStyle = 16842894; // 0x101008e field public static final int presentationTheme = 16843712; // 0x10103c0 field public static final int previewImage = 16843482; // 0x10102da - field public static final int primaryContentAlpha = 16843367; // 0x1010267 + field public static final int primaryContentAlpha = 16844117; // 0x1010555 field public static final int priority = 16842780; // 0x101001c field public static final int privateImeOptions = 16843299; // 0x1010223 field public static final int process = 16842769; // 0x1010011 @@ -1173,7 +1173,9 @@ package android { field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 + field public static final int requiredFeature = 16844119; // 0x1010557 field public static final int requiredForAllUsers = 16843728; // 0x10103d0 + field public static final int requiredNotFeature = 16844120; // 0x1010558 field public static final int requiresFadingEdge = 16843685; // 0x10103a5 field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364 field public static final int resizeClip = 16843983; // 0x10104cf @@ -1246,7 +1248,7 @@ package android { field public static final int searchSuggestSelection = 16843224; // 0x10101d8 field public static final int searchSuggestThreshold = 16843373; // 0x101026d field public static final int searchViewStyle = 16843904; // 0x1010480 - field public static final int secondaryContentAlpha = 16843688; // 0x10103a8 + field public static final int secondaryContentAlpha = 16844118; // 0x1010556 field public static final int secondaryProgress = 16843064; // 0x1010138 field public static final int secondaryProgressTint = 16843879; // 0x1010467 field public static final int secondaryProgressTintMode = 16843880; // 0x1010468 diff --git a/api/test-current.txt b/api/test-current.txt index e9dddc313fbec..47a0c464c0bfa 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1004,7 +1004,7 @@ package android { field public static final int preferenceStyle = 16842894; // 0x101008e field public static final int presentationTheme = 16843712; // 0x10103c0 field public static final int previewImage = 16843482; // 0x10102da - field public static final int primaryContentAlpha = 16843367; // 0x1010267 + field public static final int primaryContentAlpha = 16844117; // 0x1010555 field public static final int priority = 16842780; // 0x101001c field public static final int privateImeOptions = 16843299; // 0x1010223 field public static final int process = 16842769; // 0x1010011 @@ -1060,7 +1060,9 @@ package android { field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 + field public static final int requiredFeature = 16844119; // 0x1010557 field public static final int requiredForAllUsers = 16843728; // 0x10103d0 + field public static final int requiredNotFeature = 16844120; // 0x1010558 field public static final int requiresFadingEdge = 16843685; // 0x10103a5 field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364 field public static final int resizeClip = 16843983; // 0x10104cf @@ -1129,7 +1131,7 @@ package android { field public static final int searchSuggestSelection = 16843224; // 0x10101d8 field public static final int searchSuggestThreshold = 16843373; // 0x101026d field public static final int searchViewStyle = 16843904; // 0x1010480 - field public static final int secondaryContentAlpha = 16843688; // 0x10103a8 + field public static final int secondaryContentAlpha = 16844118; // 0x1010556 field public static final int secondaryProgress = 16843064; // 0x1010138 field public static final int secondaryProgressTint = 16843879; // 0x1010467 field public static final int secondaryProgressTintMode = 16843880; // 0x1010468 diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 0482f5136b0cb..85f6169412b0b 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 f801e4547658d..60cc6b06bf11a 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, "", 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, "", 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] = " 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] = " 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, "", 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] = " 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, "", 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, diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 76f4a7654af73..deacc24be158f 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1534,6 +1534,14 @@ of Android higher than the number given here, the permission will not be requested. --> + + + +