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.
+ *
+ * - 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 the
+ * following based on the {@code mergeRule}:
+ *
+ * - {@code MERGE_SELF_CAPABILITY} - lineage A -> B with {@code PERMISSION} revoked
+ * for A.
+ *
- {@code MERGE_OTHER_CAPABILITY} - lineage A -> B with {@code SHARED_USER_ID}
+ * revoked for A.
+ *
- {@code MERGE_RESTRICTED_CAPABILITY} - lineage A -> B with {@code PERMISSION} and
+ * {@code SHARED_USER_ID} 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;
}