diff options
10 files changed, 197 insertions, 19 deletions
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 29edd405be6b..ba6416d0a396 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -34,6 +34,7 @@ import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.parsing.component.ParsedProcess; import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedService; +import android.content.pm.parsing.component.ParsedUsesPermission; import android.os.Bundle; import android.util.SparseArray; import android.util.SparseIntArray; @@ -89,7 +90,7 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage addReqFeature(FeatureInfo reqFeature); - ParsingPackage addRequestedPermission(String permission); + ParsingPackage addUsesPermission(ParsedUsesPermission parsedUsesPermission); ParsingPackage addService(ParsedService parsedService); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 067787d725d9..b3c26abc57dc 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -44,6 +44,7 @@ import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.parsing.component.ParsedProcess; import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedService; +import android.content.pm.parsing.component.ParsedUsesPermission; import android.content.res.TypedArray; import android.os.Build; import android.os.Bundle; @@ -71,6 +72,7 @@ import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringValueMap; import com.android.internal.util.Parcelling.BuiltIn.ForStringSet; import java.security.PublicKey; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -227,8 +229,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { protected List<String> adoptPermissions = emptyList(); @NonNull - @DataClass.ParcelWith(ForInternedStringList.class) - private List<String> requestedPermissions = emptyList(); + private List<ParsedUsesPermission> usesPermissions = emptyList(); + @NonNull @DataClass.ParcelWith(ForInternedStringList.class) private List<String> implicitPermissions = emptyList(); @@ -691,9 +693,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override - public ParsingPackageImpl addRequestedPermission(String permission) { - this.requestedPermissions = CollectionUtils.add(this.requestedPermissions, - TextUtils.safeIntern(permission)); + public ParsingPackageImpl addUsesPermission(ParsedUsesPermission permission) { + this.usesPermissions = CollectionUtils.add(this.usesPermissions, permission); return this; } @@ -1134,7 +1135,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeByteArray(this.restrictUpdateHash); dest.writeStringList(this.originalPackages); sForInternedStringList.parcel(this.adoptPermissions, dest, flags); - sForInternedStringList.parcel(this.requestedPermissions, dest, flags); + dest.writeTypedList(this.usesPermissions); sForInternedStringList.parcel(this.implicitPermissions, dest, flags); sForStringSet.parcel(this.upgradeKeySets, dest, flags); dest.writeMap(this.keySetMapping); @@ -1255,7 +1256,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.restrictUpdateHash = in.createByteArray(); this.originalPackages = in.createStringArrayList(); this.adoptPermissions = sForInternedStringList.unparcel(in); - this.requestedPermissions = sForInternedStringList.unparcel(in); + this.usesPermissions = in.createTypedArrayList(ParsedUsesPermission.CREATOR); this.implicitPermissions = sForInternedStringList.unparcel(in); this.upgradeKeySets = sForStringSet.unparcel(in); this.keySetMapping = in.readHashMap(boot); @@ -1551,11 +1552,23 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { @NonNull @Override public List<String> getRequestedPermissions() { + final List<ParsedUsesPermission> usesPermissions = getUsesPermissions(); + final int size = usesPermissions.size(); + final List<String> requestedPermissions = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + requestedPermissions.add(usesPermissions.get(i).name); + } return requestedPermissions; } @NonNull @Override + public List<ParsedUsesPermission> getUsesPermissions() { + return usesPermissions; + } + + @NonNull + @Override public List<String> getImplicitPermissions() { return implicitPermissions; } diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index f7f3e19efdf3..9f5218371393 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -37,6 +37,7 @@ import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.parsing.component.ParsedProcess; import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedService; +import android.content.pm.parsing.component.ParsedUsesPermission; import android.os.Bundle; import android.os.Parcelable; import android.util.ArraySet; @@ -45,6 +46,7 @@ import android.util.SparseArray; import android.util.SparseIntArray; import java.security.PublicKey; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; @@ -193,6 +195,14 @@ public interface ParsingPackageRead extends Parcelable { List<FeatureInfo> getReqFeatures(); /** + * @deprecated consider migrating to {@link #getUsesPermissions} which has + * more parsed details, such as flags + */ + @NonNull + @Deprecated + List<String> getRequestedPermissions(); + + /** * All the permissions declared. This is an effective set, and may include permissions * transformed from split/migrated permissions from previous versions, so may not be exactly * what the package declares in its manifest. @@ -200,7 +210,7 @@ public interface ParsingPackageRead extends Parcelable { * @see R.styleable#AndroidManifestUsesPermission */ @NonNull - List<String> getRequestedPermissions(); + List<ParsedUsesPermission> getUsesPermissions(); /** * Returns the properties set on the application diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 0c033fddf069..2be0157836ae 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -67,6 +67,7 @@ import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedProviderUtils; import android.content.pm.parsing.component.ParsedService; import android.content.pm.parsing.component.ParsedServiceUtils; +import android.content.pm.parsing.component.ParsedUsesPermission; import android.content.pm.parsing.result.ParseInput; import android.content.pm.parsing.result.ParseInput.DeferredError; import android.content.pm.parsing.result.ParseResult; @@ -119,6 +120,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; @@ -1206,6 +1208,10 @@ public class ParsingPackageUtils { requiredNotFeatures.add(feature); } + final int usesPermissionFlags = sa.getInt( + com.android.internal.R.styleable.AndroidManifestUsesPermission_usesPermissionFlags, + 0); + final int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -1270,14 +1276,31 @@ public class ParsingPackageUtils { } } - if (!pkg.getRequestedPermissions().contains(name)) { - pkg.addRequestedPermission(name.intern()); - } else { - Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: " - + name + " in package: " + pkg.getPackageName() + " at: " - + parser.getPositionDescription()); + // Quietly ignore duplicate permission requests, but fail loudly if + // the two requests have conflicting flags + boolean found = false; + final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions(); + final int size = usesPermissions.size(); + for (int i = 0; i < size; i++) { + final ParsedUsesPermission usesPermission = usesPermissions.get(i); + if (Objects.equals(usesPermission.name, name)) { + if (usesPermission.usesPermissionFlags != usesPermissionFlags) { + return input.error("Conflicting uses-permissions flags: " + + name + " in package: " + pkg.getPackageName() + " at: " + + parser.getPositionDescription()); + } else { + Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: " + + name + " in package: " + pkg.getPackageName() + " at: " + + parser.getPositionDescription()); + } + found = true; + break; + } } + if (!found) { + pkg.addUsesPermission(new ParsedUsesPermission(name, usesPermissionFlags)); + } return success; } finally { sa.recycle(); @@ -2755,7 +2778,7 @@ public class ParsingPackageUtils { newPermsMsg.append(' '); } newPermsMsg.append(npi.name); - pkg.addRequestedPermission(npi.name) + pkg.addUsesPermission(new ParsedUsesPermission(npi.name, 0)) .addImplicitPermission(npi.name); } } @@ -2777,7 +2800,7 @@ public class ParsingPackageUtils { for (int in = 0; in < newPerms.size(); in++) { final String perm = newPerms.get(in); if (!requestedPermissions.contains(perm)) { - pkg.addRequestedPermission(perm) + pkg.addUsesPermission(new ParsedUsesPermission(perm, 0)) .addImplicitPermission(perm); } } diff --git a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java new file mode 100644 index 000000000000..b9c2e366c2d5 --- /dev/null +++ b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 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.parsing.component; + +import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A {@link android.R.styleable#AndroidManifestUsesPermission + * <uses-permission>} tag parsed from the manifest. + * + * @hide + */ +public class ParsedUsesPermission implements Parcelable { + /** Name of the permission requested */ + public @NonNull String name; + + /** Set of flags that should apply to this permission request. */ + public @UsesPermissionFlags int usesPermissionFlags; + + /** + * Strong assertion by a developer that they will never use this permission + * to derive the physical location of the device, regardless of + * ACCESS_FINE_LOCATION and/or ACCESS_COARSE_LOCATION being granted. + */ + public static final int FLAG_NEVER_FOR_LOCATION = 0x1; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "FLAG_" }, value = { + FLAG_NEVER_FOR_LOCATION + }) + public @interface UsesPermissionFlags {} + + public ParsedUsesPermission(@NonNull String name, + @UsesPermissionFlags int usesPermissionFlags) { + this.name = name.intern(); + this.usesPermissionFlags = usesPermissionFlags; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + sForInternedString.parcel(this.name, dest, flags); + dest.writeInt(usesPermissionFlags); + } + + @Override + public int describeContents() { + return 0; + } + + protected ParsedUsesPermission(@NonNull Parcel in) { + this.name = sForInternedString.unparcel(in); + this.usesPermissionFlags = in.readInt(); + } + + public static final @NonNull Parcelable.Creator<ParsedUsesPermission> CREATOR + = new Parcelable.Creator<ParsedUsesPermission>() { + @Override + public ParsedUsesPermission[] newArray(int size) { + return new ParsedUsesPermission[size]; + } + + @Override + public ParsedUsesPermission createFromParcel(@NonNull Parcel in) { + return new ParsedUsesPermission(in); + } + }; +} diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index cc52655ad7d2..0350609f9a28 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2058,6 +2058,14 @@ requested. If it does support the feature, it will be as if the manifest didn't request it at all. --> <attr name="requiredNotFeature" format="string" /> + <!-- Optional: set of flags that should apply to this permission request. --> + <attr name="usesPermissionFlags"> + <!-- Strong assertion by a developer that they will never use this + permission to derive the physical location of the device, even + when the app has been granted the ACCESS_FINE_LOCATION and/or + ACCESS_COARSE_LOCATION permissions. --> + <flag name="neverForLocation" value="0x1" /> + </attr> </declare-styleable> <!-- <code>required-feature</code> and <code>required-not-feature</code> elements inside diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index c2957780c9d7..b089014b6e1e 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -1141,4 +1141,12 @@ public abstract class PackageManagerInternal { */ public abstract boolean isPackageFrozen( @NonNull String packageName, int callingUid, int userId); + + /** + * Returns true if the given {@code packageName} has declared the + * {@code neverForLocation} flag in the {@code uses-permission} manifest tag + * where they request the given {@code permissionName}. + */ + public abstract boolean isPackageUsesPermissionNeverForLocation(@NonNull String packageName, + @NonNull String permissionName); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a1da241c6642..508491c39a7b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -252,6 +252,7 @@ import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.parsing.component.ParsedProcess; import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedService; +import android.content.pm.parsing.component.ParsedUsesPermission; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.content.res.Resources; @@ -27264,6 +27265,28 @@ public class PackageManagerService extends IPackageManager.Stub return PackageManagerService.this.getPackageStartability( packageName, callingUid, userId) == PACKAGE_STARTABILITY_FROZEN; } + + @Override + public boolean isPackageUsesPermissionNeverForLocation(@NonNull String packageName, + @NonNull String permissionName) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(permissionName); + final AndroidPackage pkg; + synchronized (mLock) { + pkg = mPackages.get(packageName); + } + if (pkg == null) return false; + final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions(); + final int size = usesPermissions.size(); + for (int i = 0; i < size; i++) { + final ParsedUsesPermission usesPermission = usesPermissions.get(i); + if (Objects.equals(usesPermission.name, permissionName)) { + return (usesPermission.usesPermissionFlags + & ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0; + } + } + return false; + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index ba6011140cf1..128cbaaffd74 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -48,6 +48,7 @@ import android.content.pm.parsing.component.ParsedPermission; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedService; +import android.content.pm.parsing.component.ParsedUsesPermission; import android.os.Bundle; import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -854,7 +855,7 @@ public class PackageParserTest { .addProvider(new ParsedProvider()) .addService(new ParsedService()) .addInstrumentation(new ParsedInstrumentation()) - .addRequestedPermission("foo7") + .addUsesPermission(new ParsedUsesPermission("foo7", 0)) .addImplicitPermission("foo25") .addProtectedBroadcast("foo8") .setStaticSharedLibName("foo23") diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index b5add849c2dc..8e1fc165fb25 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -43,6 +43,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.parsing.ParsingPackage; +import android.content.pm.parsing.component.ParsedUsesPermission; import android.content.res.TypedArray; import android.os.Environment; import android.os.UserHandle; @@ -429,7 +430,7 @@ public class ScanTests { @Test public void factoryTestFlagSet() throws Exception { final ParsingPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME) - .addRequestedPermission(Manifest.permission.FACTORY_TEST); + .addUsesPermission(new ParsedUsesPermission(Manifest.permission.FACTORY_TEST, 0)); final PackageManagerService.ScanResult scanResult = PackageManagerService.scanPackageOnlyLI( createBasicScanRequestBuilder(basicPackage).build(), |