diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java index 584a058aaedec..1e659b74db77b 100644 --- a/core/java/android/content/pm/SigningDetails.java +++ b/core/java/android/content/pm/SigningDetails.java @@ -112,6 +112,29 @@ public final class SigningDetails implements Parcelable { int AUTH = 16; } + @IntDef(value = {CapabilityMergeRule.MERGE_SELF_CAPABILITY, + CapabilityMergeRule.MERGE_OTHER_CAPABILITY, + CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY}) + public @interface CapabilityMergeRule { + /** + * When capabilities are different for a common signer in the lineage, use the capabilities + * from this instance. + */ + int MERGE_SELF_CAPABILITY = 0; + + /** + * When capabilities are different for a common signer in the lineage, use the capabilites + * from the other instance. + */ + int MERGE_OTHER_CAPABILITY = 1; + + /** + * When capabilities are different for a common signer in the lineage, use the most + * restrictive between the two signers. + */ + int MERGE_RESTRICTED_CAPABILITY = 2; + } + /** A representation of unknown signing details. Use instead of null. */ public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null, SignatureSchemeVersion.UNKNOWN, /* keys */ null, /* pastSigningCertificates */ null); @@ -164,30 +187,60 @@ public final class SigningDetails implements Parcelable { /** * Merges the signing lineage of this instance with the lineage in the provided {@code - * otherSigningDetails} when one has the same or an ancestor signer of the other. + * otherSigningDetails} using {@link CapabilityMergeRule#MERGE_OTHER_CAPABILITY} as the merge + * rule. + * + * @param otherSigningDetails the {@code SigningDetails} with which to merge + * @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer + * of the other. If neither instance has a lineage, or if neither has the same or an + * ancestor signer then this instance is returned. + * @see #mergeLineageWith(SigningDetails, int) + */ + public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) { + return mergeLineageWith(otherSigningDetails, CapabilityMergeRule.MERGE_OTHER_CAPABILITY); + } + + /** + * Merges the signing lineage of this instance with the lineage in the provided {@code + * otherSigningDetails} when one has the same or an ancestor signer of the other using the + * provided {@code mergeRule} to handle differences in capabilities for shared signers. * *

Merging two signing lineages will result in a new {@code SigningDetails} instance - * containing the longest common lineage with the most restrictive capabilities. If the two - * lineages contain the same signers with the same capabilities then the instance on which - * this was invoked is returned without any changes. Similarly if neither instance has a - * lineage, or if neither has the same or an ancestor signer then this instance is returned. + * containing the longest common lineage with differences in capabilities for shared signers + * resolved using the provided {@code mergeRule}. If the two lineages contain the same signers + * with the same capabilities then the instance on which this was invoked is returned without + * any changes. Similarly if neither instance has a lineage, or if neither has the same or an + * ancestor signer then this instance is returned. * * Following are some example results of this method for lineages with signers A, B, C, D: - * - lineage B merged with lineage A -> B returns lineage A -> B. - * - lineage A -> B merged with lineage B -> C returns lineage A -> B -> C - * - lineage A -> B with the {@code PERMISSION} capability revoked for A merged with - * lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns - * lineage A -> B with both capabilities revoked for A. - * - lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage - * A -> B -> C since the current signer of both instances is not the same or in the - * lineage of the other. + *

* - * @param otherSigningDetails The {@code SigningDetails} you would like to merge with. + * @param otherSigningDetails the {@code SigningDetails} with which to merge + * @param mergeRule the {@link CapabilityMergeRule} to use when resolving differences in + * capabilities for shared signers * @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer * of the other. If neither instance has a lineage, or if neither has the same or an * ancestor signer then this instance is returned. */ - public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) { + public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails, + @CapabilityMergeRule int mergeRule) { if (!hasPastSigningCertificates()) { return otherSigningDetails.hasPastSigningCertificates() && otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this; @@ -201,19 +254,43 @@ public final class SigningDetails implements Parcelable { if (descendantSigningDetails == null) { return this; } - return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf( - otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(this); + SigningDetails mergedDetails = this; + if (descendantSigningDetails == this) { + // If this instance is the descendant then the merge will also be invoked against this + // instance and the provided mergeRule can be used as is. + mergedDetails = mergeLineageWithAncestorOrSelf(otherSigningDetails, mergeRule); + } else { + // If the provided instance is the descendant then the merge will be invoked against the + // other instance and a self or other merge rule will need to be flipped. + switch (mergeRule) { + case CapabilityMergeRule.MERGE_SELF_CAPABILITY: + mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this, + CapabilityMergeRule.MERGE_OTHER_CAPABILITY); + break; + case CapabilityMergeRule.MERGE_OTHER_CAPABILITY: + mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this, + CapabilityMergeRule.MERGE_SELF_CAPABILITY); + break; + case CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY: + mergedDetails = otherSigningDetails.mergeLineageWithAncestorOrSelf(this, + mergeRule); + break; + } + } + return mergedDetails; } /** * Merges the signing lineage of this instance with the lineage of the ancestor (or same) * signer in the provided {@code otherSigningDetails}. * - * @param otherSigningDetails The {@code SigningDetails} you would like to merge with. + * @param otherSigningDetails the {@code SigningDetails} with which to merge + * @param mergeRule the {@link CapabilityMergeRule} to use when resolving differences in + * capabilities for shared signers * @return Merged {@code SigningDetails} instance. */ private @NonNull SigningDetails mergeLineageWithAncestorOrSelf( - @NonNull SigningDetails otherSigningDetails) { + @NonNull SigningDetails otherSigningDetails, @CapabilityMergeRule int mergeRule) { // This method should only be called with instances that contain lineages. int index = mPastSigningCertificates.length - 1; int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1; @@ -236,16 +313,26 @@ public final class SigningDetails implements Parcelable { } do { - // Add the common signer to the merged lineage with the most restrictive - // capabilities of the two lineages. + // Add the common signer to the merged lineage and resolve any differences in + // capabilites with the merge rule. Signature signature = mPastSigningCertificates[index--]; Signature ancestorSignature = otherSigningDetails.mPastSigningCertificates[otherIndex--]; Signature mergedSignature = new Signature(signature); - int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags(); - if (signature.getFlags() != mergedCapabilities) { + if (signature.getFlags() != ancestorSignature.getFlags()) { capabilitiesModified = true; - mergedSignature.setFlags(mergedCapabilities); + switch (mergeRule) { + case CapabilityMergeRule.MERGE_SELF_CAPABILITY: + mergedSignature.setFlags(signature.getFlags()); + break; + case CapabilityMergeRule.MERGE_OTHER_CAPABILITY: + mergedSignature.setFlags(ancestorSignature.getFlags()); + break; + case CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY: + mergedSignature.setFlags( + signature.getFlags() & ancestorSignature.getFlags()); + break; + } } mergedSignatures.add(mergedSignature); } while (index >= 0 && otherIndex >= 0 && mPastSigningCertificates[index].equals( @@ -858,7 +945,7 @@ public final class SigningDetails implements Parcelable { - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -914,10 +1001,10 @@ public final class SigningDetails implements Parcelable { } @DataClass.Generated( - time = 1616984092921L, - codegenVersion = "1.0.22", + time = 1650058974710L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/SigningDetails.java", - inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)") + inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails,int)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails,int)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)") @Deprecated private void __metadata() {} diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java index d5223492a83c0..b331a24fedc60 100644 --- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java +++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java @@ -15,6 +15,9 @@ */ package android.content.pm; +import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_OTHER_CAPABILITY; +import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY; +import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_SELF_CAPABILITY; import static android.content.pm.SigningDetails.CertCapabilities.AUTH; import static android.content.pm.SigningDetails.CertCapabilities.INSTALLED_DATA; import static android.content.pm.SigningDetails.CertCapabilities.PERMISSION; @@ -45,6 +48,7 @@ import java.util.Set; public class SigningDetailsTest { private static final int DEFAULT_CAPABILITIES = INSTALLED_DATA | SHARED_USER_ID | PERMISSION | AUTH; + private static final int CURRENT_SIGNER_CAPABILITIES = DEFAULT_CAPABILITIES | ROLLBACK; // Some of the tests in this class require valid certificate encodings from which to pull the // public key for the SigningDetails; the following are all DER encoded EC X.509 certificates. @@ -368,10 +372,10 @@ public class SigningDetailsTest { } @Test - public void mergeLineageWith_sameLineageDifferentCaps_returnsLineageWithModifiedCaps() + public void mergeLineageWith_sameLineageDifferentCaps_returnsLineageWithProvidedCaps() throws Exception { // This test verifies when two lineages consist of the same signers but have different - // capabilities the more restrictive capabilities are returned. + // capabilities, the capabilities of the provided lineage are returned. SigningDetails defaultCapabilitiesDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE); SigningDetails modifiedCapabilitiesDetails = createSigningDetailsWithLineageAndCapabilities( @@ -384,68 +388,135 @@ public class SigningDetailsTest { defaultCapabilitiesDetails); assertEquals(modifiedCapabilitiesDetails, result1); - assertTrue(result2 == modifiedCapabilitiesDetails); + assertEquals(defaultCapabilitiesDetails, result2); + } + + @Test + public void + mergeLineageWith_sameLineageDifferentCapsRestrictedRule_returnsLineageWithModifiedCaps() + throws Exception { + // This test verifies when two lineages consist of the same signers but have different + // capabilities, and the restricted merge rule is used, the more restrictive capabilities + // are returned. + SigningDetails defaultCapabilitiesDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + SigningDetails modifiedCapabilitiesDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{INSTALLED_DATA, INSTALLED_DATA, INSTALLED_DATA}); + + SigningDetails result1 = defaultCapabilitiesDetails.mergeLineageWith( + modifiedCapabilitiesDetails, MERGE_RESTRICTED_CAPABILITY); + SigningDetails result2 = modifiedCapabilitiesDetails.mergeLineageWith( + defaultCapabilitiesDetails, MERGE_RESTRICTED_CAPABILITY); + + assertEquals(modifiedCapabilitiesDetails, result1); + assertEquals(modifiedCapabilitiesDetails, result2); } @Test public void mergeLineageWith_overlappingLineageDiffCaps_returnsFullLineageWithModifiedCaps() throws Exception { - // This test verifies the following scenario: - // - First lineage has signers A -> B with modified capabilities for A and B - // - Second lineage has signers B -> C with modified capabilities for B and C - // The merged lineage should be A -> B -> C with the most restrictive capabilities for B - // since it is in both lineages. + // This test verifies the merge of two lineages with overlapping signers and modified caps + // returns the full lineage with expected capabilities based on the provided merge rule. int[] firstCapabilities = new int[]{INSTALLED_DATA | AUTH, INSTALLED_DATA | SHARED_USER_ID | PERMISSION}; int[] secondCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | AUTH, INSTALLED_DATA | SHARED_USER_ID | AUTH}; - int[] expectedCapabilities = + int[] expectedRestrictedCapabilities = new int[]{firstCapabilities[0], firstCapabilities[1] & secondCapabilities[0], secondCapabilities[1]}; + int[] expectedCapabilities1 = + new int[]{firstCapabilities[0], secondCapabilities[0], secondCapabilities[1]}; + int[] expectedCapabilities2 = + new int[]{firstCapabilities[0], firstCapabilities[1], secondCapabilities[1]}; SigningDetails firstDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities); SigningDetails secondDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, secondCapabilities); - SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities( + SigningDetails expectedRestrictedDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, - expectedCapabilities); + expectedRestrictedCapabilities); + SigningDetails expectedDetails1 = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + expectedCapabilities1); + SigningDetails expectedDetails2 = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + expectedCapabilities2); - SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails); - SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails); + SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails, + MERGE_OTHER_CAPABILITY); + SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails, + MERGE_SELF_CAPABILITY); + SigningDetails result3 = firstDetails.mergeLineageWith(secondDetails, + MERGE_SELF_CAPABILITY); + SigningDetails result4 = secondDetails.mergeLineageWith(firstDetails, + MERGE_OTHER_CAPABILITY); + SigningDetails result5 = firstDetails.mergeLineageWith(secondDetails, + MERGE_RESTRICTED_CAPABILITY); + SigningDetails result6 = secondDetails.mergeLineageWith(firstDetails, + MERGE_RESTRICTED_CAPABILITY); - assertEquals(expectedDetails, result1); - assertEquals(expectedDetails, result2); + assertEquals(expectedDetails1, result1); + assertEquals(expectedDetails1, result2); + assertEquals(expectedDetails2, result3); + assertEquals(expectedDetails2, result4); + assertEquals(expectedRestrictedDetails, result5); + assertEquals(expectedRestrictedDetails, result6); } @Test public void mergeLineageWith_subLineageModifiedCaps_returnsFullLineageWithModifiedCaps() throws Exception { - // This test verifies the following scenario: - // - First lineage has signers B -> C with modified capabilities - // - Second lineage has signers A -> B -> C -> D with modified capabilities - // The merged lineage should be A -> B -> C -> D with the most restrictive capabilities for - // B and C since they are in both lineages. + // This test verifies the merge of a full lineage and a subset of that lineage with + // modified caps returns the full lineage with expected capabilities based on the + // provided merge rule. int[] subCapabilities = new int[]{INSTALLED_DATA | SHARED_USER_ID | PERMISSION, DEFAULT_CAPABILITIES | ROLLBACK}; int[] fullCapabilities = new int[]{0, SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}; - int[] expectedCapabilities = + int[] expectedRestrictedCapabilities = new int[]{fullCapabilities[0], subCapabilities[0] & fullCapabilities[1], subCapabilities[1] & fullCapabilities[2], fullCapabilities[3]}; + int[] expectedCapabilities1 = + new int[]{fullCapabilities[0], fullCapabilities[1], fullCapabilities[2], + fullCapabilities[3]}; + int[] expectedCapabilities2 = + new int[]{fullCapabilities[0], subCapabilities[0], subCapabilities[1], + fullCapabilities[3]}; SigningDetails subLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{SECOND_SIGNATURE, THIRD_SIGNATURE}, subCapabilities); SigningDetails fullLineageDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE}, fullCapabilities); - SigningDetails expectedDetails = createSigningDetailsWithLineageAndCapabilities( + SigningDetails expectedRestrictedDetails = createSigningDetailsWithLineageAndCapabilities( new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE}, - expectedCapabilities); + expectedRestrictedCapabilities); + SigningDetails expectedDetails1 = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE}, + expectedCapabilities1); + SigningDetails expectedDetails2 = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE, FOURTH_SIGNATURE}, + expectedCapabilities2); - SigningDetails result1 = subLineageDetails.mergeLineageWith(fullLineageDetails); - SigningDetails result2 = fullLineageDetails.mergeLineageWith(subLineageDetails); + SigningDetails result1 = subLineageDetails.mergeLineageWith(fullLineageDetails, + MERGE_OTHER_CAPABILITY); + SigningDetails result2 = fullLineageDetails.mergeLineageWith(subLineageDetails, + MERGE_SELF_CAPABILITY); + SigningDetails result3 = subLineageDetails.mergeLineageWith(fullLineageDetails, + MERGE_SELF_CAPABILITY); + SigningDetails result4 = fullLineageDetails.mergeLineageWith(subLineageDetails, + MERGE_OTHER_CAPABILITY); + SigningDetails result5 = subLineageDetails.mergeLineageWith(fullLineageDetails, + MERGE_RESTRICTED_CAPABILITY); + SigningDetails result6 = fullLineageDetails.mergeLineageWith(subLineageDetails, + MERGE_RESTRICTED_CAPABILITY); - assertEquals(expectedDetails, result1); - assertEquals(expectedDetails, result2); + assertEquals(expectedDetails1, result1); + assertEquals(expectedDetails1, result2); + assertEquals(expectedDetails2, result3); + assertEquals(expectedDetails2, result4); + assertEquals(expectedRestrictedDetails, result5); + assertEquals(expectedRestrictedDetails, result6); } @Test @@ -465,6 +536,39 @@ public class SigningDetailsTest { assertTrue(result2 == secondLineageDetails); } + @Test + public void mergeLineageWith_modifiedCaps_returnsCapsFromProvidedLineage() + throws Exception { + // By default, when merging two lineage instances, the initial instance should represent a + // shared lineage while the provided lineage represents that of a newly installed / updated + // package. The shared lineage should contain any previous capability modifications from + // the default while the provided lineage has an opportunity to modify what was previously + // set. Initially, the most restrictive capabilities were always retained by the returned + // lineage, so apps had no mechanism to roll back a restriction to a previous signer. To + // allow this, a merge rule can be specified to indicate how differences in capabilities + // in common signers should be handled with the default using the capabilities from the + // provided lineage. + int[] firstCapabilities = new int[]{INSTALLED_DATA | PERMISSION | AUTH, + CURRENT_SIGNER_CAPABILITIES}; + int[] secondCapabilities = + new int[]{DEFAULT_CAPABILITIES, CURRENT_SIGNER_CAPABILITIES}; + SigningDetails firstDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities); + SigningDetails secondDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, secondCapabilities); + // By default, the resulting capabilities should be that of the provided lineage. + SigningDetails expectedDetails1 = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, secondCapabilities); + SigningDetails expectedDetails2 = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, firstCapabilities); + + SigningDetails result1 = firstDetails.mergeLineageWith(secondDetails); + SigningDetails result2 = secondDetails.mergeLineageWith(firstDetails); + + assertEquals(expectedDetails1, result1); + assertEquals(expectedDetails2, result2); + } + @Test public void hasCommonAncestor_noLineageSameSingleSigner_returnsTrue() throws Exception { // If neither SigningDetails have a lineage but they have the same single signer then diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index 0b69cd376bd18..5fc916f888f3f 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; +import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY; import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; @@ -176,6 +177,19 @@ final class ReconcilePackageUtils { SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith( signingDetails); if (mergedDetails != sharedSigningDetails) { + // Use the restricted merge rule with the signing lineages from the + // other packages in the sharedUserId to ensure if any revoke a + // capability from a previous signer then this is reflected in the + // shared lineage. + for (AndroidPackage androidPackage : sharedUserSetting.getPackages()) { + if (androidPackage.getPackageName() != null + && !androidPackage.getPackageName().equals( + parsedPackage.getPackageName())) { + mergedDetails = mergedDetails.mergeLineageWith( + androidPackage.getSigningDetails(), + MERGE_RESTRICTED_CAPABILITY); + } + } sharedUserSetting.signatures.mSigningDetails = mergedDetails; }