From d65f0926dc58707ab50c3c70a3171a8a5831c1ac Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 31 Mar 2021 12:35:51 -0700 Subject: [PATCH] Support serializing package signatures for domain verification state To prepare for backup/restore changes which need to verify that the package signatures match. Bug: 182207215 Test: atest DomainVerificationPersistenceTest Change-Id: I9e4bd24256604b649acf2c925cce2ac65331fa2c --- .../domain/DomainVerificationPersistence.java | 52 ++++++--- .../domain/DomainVerificationService.java | 26 ++++- .../domain/DomainVerificationSettings.java | 10 +- .../models/DomainVerificationPkgState.java | 59 ++++++++-- .../DomainVerificationPersistenceTest.kt | 110 ++++++++++++++---- 5 files changed, 206 insertions(+), 51 deletions(-) diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java index abb8d2fb6e1e4..f0ad98c26ede3 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java @@ -18,10 +18,12 @@ package com.android.server.pm.verify.domain; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.Signature; import android.content.pm.verify.domain.DomainVerificationState; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.PackageUtils; import android.util.SparseArray; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; @@ -36,6 +38,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.Collection; import java.util.UUID; +import java.util.function.Function; public class DomainVerificationPersistence { @@ -49,6 +52,7 @@ public class DomainVerificationPersistence { private static final String ATTR_PACKAGE_NAME = "packageName"; private static final String ATTR_ID = "id"; private static final String ATTR_HAS_AUTO_VERIFY_DOMAINS = "hasAutoVerifyDomains"; + private static final String ATTR_SIGNATURE = "signature"; private static final String TAG_USER_STATES = "user-states"; public static final String TAG_USER_STATE = "user-state"; @@ -62,10 +66,18 @@ public class DomainVerificationPersistence { public static final String ATTR_NAME = "name"; public static final String ATTR_STATE = "state"; + /** + * @param pkgNameToSignature Converts package name to a string representation of its signature. + * Usually this is the SHA-256 hash from + * {@link PackageUtils#computeSignaturesSha256Digest(Signature[])}, + * but can be an arbitrary string for testing purposes. Pass non-null + * to write out signatures, or null to ignore. + */ public static void writeToXml(@NonNull TypedXmlSerializer xmlSerializer, @NonNull DomainVerificationStateMap attached, @NonNull ArrayMap pending, - @NonNull ArrayMap restored) throws IOException { + @NonNull ArrayMap restored, + @Nullable Function pkgNameToSignature) throws IOException { try (SettingsXml.Serializer serializer = SettingsXml.serializer(xmlSerializer)) { try (SettingsXml.WriteSection ignored = serializer.startSection( TAG_DOMAIN_VERIFICATIONS)) { @@ -86,25 +98,26 @@ public class DomainVerificationPersistence { } try (SettingsXml.WriteSection activeSection = serializer.startSection(TAG_ACTIVE)) { - writePackageStates(activeSection, active); + writePackageStates(activeSection, active, pkgNameToSignature); } try (SettingsXml.WriteSection restoredSection = serializer.startSection( TAG_RESTORED)) { - writePackageStates(restoredSection, restored.values()); + writePackageStates(restoredSection, restored.values(), pkgNameToSignature); } } } } private static void writePackageStates(@NonNull SettingsXml.WriteSection section, - @NonNull Collection states) throws IOException { + @NonNull Collection states, + @Nullable Function pkgNameToSignature) throws IOException { if (states.isEmpty()) { return; } for (DomainVerificationPkgState state : states) { - writePkgStateToXml(section, state); + writePkgStateToXml(section, state, pkgNameToSignature); } } @@ -146,11 +159,12 @@ public class DomainVerificationPersistence { * been entered. */ @Nullable - public static DomainVerificationPkgState createPkgStateFromXml( + private static DomainVerificationPkgState createPkgStateFromXml( @NonNull SettingsXml.ReadSection section) { String packageName = section.getString(ATTR_PACKAGE_NAME); String idString = section.getString(ATTR_ID); boolean hasAutoVerifyDomains = section.getBoolean(ATTR_HAS_AUTO_VERIFY_DOMAINS); + String signature = section.getString(ATTR_SIGNATURE); if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(idString)) { return null; } @@ -172,7 +186,7 @@ public class DomainVerificationPersistence { } return new DomainVerificationPkgState(packageName, id, hasAutoVerifyDomains, stateMap, - userStates); + userStates, signature); } private static void readUserStates(@NonNull SettingsXml.ReadSection section, @@ -196,14 +210,26 @@ public class DomainVerificationPersistence { } } - public static void writePkgStateToXml(@NonNull SettingsXml.WriteSection parentSection, - @NonNull DomainVerificationPkgState pkgState) throws IOException { + private static void writePkgStateToXml(@NonNull SettingsXml.WriteSection parentSection, + @NonNull DomainVerificationPkgState pkgState, + @Nullable Function pkgNameToSignature) throws IOException { + String packageName = pkgState.getPackageName(); + String signature = pkgNameToSignature == null + ? null : pkgNameToSignature.apply(packageName); + if (signature == null) { + // If a package isn't available to get its signature, fallback to the previously stored + // result, which can occur if the package has been marked for restore but hasn't + // been installed on the new device yet. + signature = pkgState.getBackupSignatureHash(); + } + try (SettingsXml.WriteSection ignored = parentSection.startSection(TAG_PACKAGE_STATE) - .attribute(ATTR_PACKAGE_NAME, pkgState.getPackageName()) + .attribute(ATTR_PACKAGE_NAME, packageName) .attribute(ATTR_ID, pkgState.getId().toString()) .attribute(ATTR_HAS_AUTO_VERIFY_DOMAINS, - pkgState.isHasAutoVerifyDomains())) { + pkgState.isHasAutoVerifyDomains()) + .attribute(ATTR_SIGNATURE, signature)) { writeStateMap(parentSection, pkgState.getStateMap()); writeUserStates(parentSection, pkgState.getUserStates()); } @@ -245,7 +271,7 @@ public class DomainVerificationPersistence { * entered. */ @Nullable - public static DomainVerificationInternalUserState createUserStateFromXml( + private static DomainVerificationInternalUserState createUserStateFromXml( @NonNull SettingsXml.ReadSection section) { int userId = section.getInt(ATTR_USER_ID); if (userId == -1) { @@ -274,7 +300,7 @@ public class DomainVerificationPersistence { } } - public static void writeUserStateToXml(@NonNull SettingsXml.WriteSection parentSection, + private static void writeUserStateToXml(@NonNull SettingsXml.WriteSection parentSection, @NonNull DomainVerificationInternalUserState userState) throws IOException { try (SettingsXml.WriteSection section = parentSection.startSection(TAG_USER_STATE) diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java index 81adbe197ee27..b0ee46d54f269 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java @@ -43,6 +43,7 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; +import android.util.PackageUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -842,7 +843,8 @@ public class DomainVerificationService extends SystemService + ", new pkg = " + newPkg, new Exception()); DomainVerificationPkgState newPkgState = new DomainVerificationPkgState( - pkgName, newDomainSetId, true, newStateMap, newUserStates); + pkgName, newDomainSetId, true, newStateMap, newUserStates, + null /* signature */); mAttachedPkgStates.put(pkgName, newDomainSetId, newPkgState); return; } @@ -894,7 +896,8 @@ public class DomainVerificationService extends SystemService sendBroadcast = hasAutoVerifyDomains && needsBroadcast; mAttachedPkgStates.put(pkgName, newDomainSetId, new DomainVerificationPkgState( - pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates)); + pkgName, newDomainSetId, hasAutoVerifyDomains, newStateMap, newUserStates, + null /* signature */)); } if (sendBroadcast) { @@ -1019,9 +1022,22 @@ public class DomainVerificationService extends SystemService @Override public void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException { - synchronized (mLock) { - mSettings.writeSettings(serializer, mAttachedPkgStates); - } + mConnection.withPackageSettingsThrowing(pkgSettings -> { + synchronized (mLock) { + mSettings.writeSettings(serializer, mAttachedPkgStates, pkgName -> { + PackageSetting pkgSetting = pkgSettings.apply(pkgName); + if (pkgSetting == null) { + // If querying for a user restored package that isn't installed on the + // device yet, there will be no signature to write out. In that case, + // it's expected that this returns null and it falls back to the restored + // state's stored signature if it exists. + return null; + } + + return PackageUtils.computeSignaturesSha256Digest(pkgSetting.getSignatures()); + }); + } + }); mLegacySettings.writeSettings(serializer); } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java index d3cb0b33f45bf..44ffc5c146c57 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java @@ -36,6 +36,7 @@ import com.android.server.pm.verify.domain.models.DomainVerificationStateMap; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.function.Function; class DomainVerificationSettings { @@ -68,18 +69,19 @@ class DomainVerificationSettings { public void writeSettings(@NonNull TypedXmlSerializer xmlSerializer, - @NonNull DomainVerificationStateMap liveState) - throws IOException { + @NonNull DomainVerificationStateMap liveState, + @NonNull Function pkgSignatureFunction) throws IOException { synchronized (mLock) { DomainVerificationPersistence.writeToXml(xmlSerializer, liveState, - mPendingPkgStates, mRestoredPkgStates); + mPendingPkgStates, mRestoredPkgStates, pkgSignatureFunction); } } /** * Parses a previously stored set of states and merges them with {@param liveState}, directly * mutating the values. This is intended for reading settings written by {@link - * #writeSettings(TypedXmlSerializer, DomainVerificationStateMap)} on the same device setup. + * #writeSettings(TypedXmlSerializer, DomainVerificationStateMap, Function)} on the same device + * setup. */ public void readSettings(@NonNull TypedXmlPullParser parser, @NonNull DomainVerificationStateMap liveState) diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java index 40c70915f3cea..6c56360177d84 100644 --- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java +++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java @@ -19,6 +19,7 @@ package com.android.server.pm.verify.domain.models; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.pm.Signature; import android.content.pm.verify.domain.DomainVerificationState; import android.util.ArrayMap; import android.util.SparseArray; @@ -63,15 +64,28 @@ public class DomainVerificationPkgState { @NonNull private final SparseArray mUserStates; + /** + * If previously recorded, the SHA-256 signing cert digest of the package to attach to. + * When doing restoration of a previously backed up state, if the signature does not + * match the package being scanned/installed on device, it will be rejected. + * + * It's assumed the domain verification agent will eventually re-verify this domain + * and revoke if necessary. + * + * @see android.util.PackageUtils#computeSignaturesSha256Digest(Signature[]) + */ + @Nullable + private final String mBackupSignatureHash; + public DomainVerificationPkgState(@NonNull String packageName, @NonNull UUID id, boolean hasAutoVerifyDomains) { - this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0)); + this(packageName, id, hasAutoVerifyDomains, new ArrayMap<>(0), new SparseArray<>(0), null); } public DomainVerificationPkgState(@NonNull DomainVerificationPkgState pkgState, @NonNull UUID id, boolean hasAutoVerifyDomains) { this(pkgState.getPackageName(), id, hasAutoVerifyDomains, pkgState.getStateMap(), - pkgState.getUserStates()); + pkgState.getUserStates(), null); } @Nullable @@ -109,7 +123,7 @@ public class DomainVerificationPkgState { - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -137,6 +151,13 @@ public class DomainVerificationPkgState { * * TODO(b/159952358): Hide the state map entirely from the caller, to allow optimizations, * such as storing no state when the package is marked as a linked app in SystemConfig. + * @param backupSignatureHash + * If previously recorded, the SHA-256 signing cert digest of the package to attach to. + * When doing restoration of a previously backed up state, if the signature does not + * match the package being scanned/installed on device, it will be rejected. + * + * It's assumed the domain verification agent will eventually re-verify this domain + * and revoke if necessary. */ @DataClass.Generated.Member public DomainVerificationPkgState( @@ -144,7 +165,8 @@ public class DomainVerificationPkgState { @NonNull UUID id, boolean hasAutoVerifyDomains, @NonNull ArrayMap stateMap, - @NonNull SparseArray userStates) { + @NonNull SparseArray userStates, + @Nullable String backupSignatureHash) { this.mPackageName = packageName; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPackageName); @@ -158,6 +180,7 @@ public class DomainVerificationPkgState { this.mUserStates = userStates; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mUserStates); + this.mBackupSignatureHash = backupSignatureHash; // onConstructed(); // You can define this method to get a callback } @@ -201,6 +224,21 @@ public class DomainVerificationPkgState { return mUserStates; } + /** + * If previously recorded, the SHA-256 signing cert digest of the package to attach to. + * When doing restoration of a previously backed up state, if the signature does not + * match the package being scanned/installed on device, it will be rejected. + * + * It's assumed the domain verification agent will eventually re-verify this domain + * and revoke if necessary. + * + * @see android.util.PackageUtils#computeSignaturesSha256Digest(Signature[]) + */ + @DataClass.Generated.Member + public @Nullable String getBackupSignatureHash() { + return mBackupSignatureHash; + } + @Override @DataClass.Generated.Member public String toString() { @@ -212,7 +250,8 @@ public class DomainVerificationPkgState { "id = " + mId + ", " + "hasAutoVerifyDomains = " + mHasAutoVerifyDomains + ", " + "stateMap = " + mStateMap + ", " + - "userStates = " + mUserStates + + "userStates = " + mUserStates + ", " + + "backupSignatureHash = " + mBackupSignatureHash + " }"; } @@ -233,7 +272,8 @@ public class DomainVerificationPkgState { && Objects.equals(mId, that.mId) && mHasAutoVerifyDomains == that.mHasAutoVerifyDomains && Objects.equals(mStateMap, that.mStateMap) - && userStatesEquals(that.mUserStates); + && userStatesEquals(that.mUserStates) + && Objects.equals(mBackupSignatureHash, that.mBackupSignatureHash); } @Override @@ -248,14 +288,15 @@ public class DomainVerificationPkgState { _hash = 31 * _hash + Boolean.hashCode(mHasAutoVerifyDomains); _hash = 31 * _hash + Objects.hashCode(mStateMap); _hash = 31 * _hash + userStatesHashCode(); + _hash = 31 * _hash + Objects.hashCode(mBackupSignatureHash); return _hash; } @DataClass.Generated( - time = 1614818362549L, - codegenVersion = "1.0.22", + time = 1617315369614L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray mUserStates\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getUserState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getOrCreateUserState(int)\npublic void setId(java.util.UUID)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userStatesHashCode()\nprivate boolean userStatesEquals(android.util.SparseArray)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull java.util.UUID mId\nprivate final boolean mHasAutoVerifyDomains\nprivate final @android.annotation.NonNull android.util.ArrayMap mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray mUserStates\nprivate final @android.annotation.Nullable java.lang.String mBackupSignatureHash\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getUserState(int)\npublic @android.annotation.Nullable com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState getOrCreateUserState(int)\npublic void removeUser(int)\npublic void removeAllUsers()\nprivate int userStatesHashCode()\nprivate boolean userStatesEquals(android.util.SparseArray)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt index f8fda12f806d8..7ffbbf6af159e 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt @@ -18,6 +18,7 @@ package com.android.server.pm.test.verify.domain import android.content.pm.verify.domain.DomainVerificationState import android.util.ArrayMap +import android.util.SparseArray import android.util.TypedXmlPullParser import android.util.TypedXmlSerializer import android.util.Xml @@ -84,23 +85,32 @@ class DomainVerificationPersistenceTest { @JvmField val tempFolder = TemporaryFolder() - @Test - fun writeAndReadBackNormal() { + private fun mockWriteValues( + pkgNameToSignature: (String) -> String? = { null } + ): Triple, + ArrayMap, + ArrayMap> { val attached = DomainVerificationStateMap().apply { - mockPkgState(0).let { put(it.packageName, it.id, it) } - mockPkgState(1).let { put(it.packageName, it.id, it) } + mockPkgState(0, pkgNameToSignature).let { put(it.packageName, it.id, it) } + mockPkgState(1, pkgNameToSignature).let { put(it.packageName, it.id, it) } } val pending = ArrayMap().apply { - mockPkgState(2).let { put(it.packageName, it) } - mockPkgState(3).let { put(it.packageName, it) } + mockPkgState(2, pkgNameToSignature).let { put(it.packageName, it) } + mockPkgState(3, pkgNameToSignature).let { put(it.packageName, it) } } val restored = ArrayMap().apply { - mockPkgState(4).let { put(it.packageName, it) } - mockPkgState(5).let { put(it.packageName, it) } + mockPkgState(4, pkgNameToSignature).let { put(it.packageName, it) } + mockPkgState(5, pkgNameToSignature).let { put(it.packageName, it) } } + return Triple(attached, pending, restored) + } + + @Test + fun writeAndReadBackNormal() { + val (attached, pending, restored) = mockWriteValues() val file = tempFolder.newFile().writeXml { - DomainVerificationPersistence.writeToXml(it, attached, pending, restored) + DomainVerificationPersistence.writeToXml(it, attached, pending, restored, null) } val xml = file.readText() @@ -114,9 +124,57 @@ class DomainVerificationPersistenceTest { assertWithMessage(xml).that(readRestored.values).containsExactlyElementsIn(restored.values) } + @Test + fun writeAndReadBackWithSignature() { + val (attached, pending, restored) = mockWriteValues() + val file = tempFolder.newFile().writeXml { + DomainVerificationPersistence.writeToXml(it, attached, pending, restored) { + "SIGNATURE_$it" + } + } + + val (readActive, readRestored) = file.readXml { + DomainVerificationPersistence.readFromXml(it) + } + + // Assign the signatures to a fresh set of data structures, to ensure the previous write + // call did not use the signatures from the data structure. This is because the method is + // intended to optionally append signatures, regardless of if the existing data structures + // contain them or not. + val (attached2, pending2, restored2) = mockWriteValues { "SIGNATURE_$it" } + + assertThat(readActive.values) + .containsExactlyElementsIn(attached2.values() + pending2.values) + assertThat(readRestored.values).containsExactlyElementsIn(restored2.values) + + (readActive + readRestored).forEach { (_, value) -> + assertThat(value.backupSignatureHash).isEqualTo("SIGNATURE_${value.packageName}") + } + } + + @Test + fun writeStateSignatureIfFunctionReturnsNull() { + val (attached, pending, restored) = mockWriteValues { "SIGNATURE_$it" } + val file = tempFolder.newFile().writeXml { + DomainVerificationPersistence.writeToXml(it, attached, pending, restored) { null } + } + + val (readActive, readRestored) = file.readXml { + DomainVerificationPersistence.readFromXml(it) + } + + assertThat(readActive.values) + .containsExactlyElementsIn(attached.values() + pending.values) + assertThat(readRestored.values).containsExactlyElementsIn(restored.values) + + (readActive + readRestored).forEach { (_, value) -> + assertThat(value.backupSignatureHash).isEqualTo("SIGNATURE_${value.packageName}") + } + } + @Test fun readMalformed() { - val stateZero = mockEmptyPkgState(0).apply { + val stateZero = mockEmptyPkgState(0, pkgNameToSignature = { "ACTIVE" }).apply { stateMap["example.com"] = DomainVerificationState.STATE_SUCCESS stateMap["example.org"] = DomainVerificationState.STATE_FIRST_VERIFIER_DEFINED @@ -128,7 +186,7 @@ class DomainVerificationPersistenceTest { isLinkHandlingAllowed = true } } - val stateOne = mockEmptyPkgState(1).apply { + val stateOne = mockEmptyPkgState(1, pkgNameToSignature = { "RESTORED" }).apply { // It's valid to have a user selection without any autoVerify domains userStates[1] = DomainVerificationInternalUserState(1).apply { addHosts(setOf("example-user1.com", "example-user1.org")) @@ -156,6 +214,7 @@ class DomainVerificationPersistenceTest { packageName="${stateZero.packageName}" id="${stateZero.id}" hasAutoVerifyDomains="true" + signature="ACTIVE" > @@ -226,20 +286,30 @@ class DomainVerificationPersistenceTest { private fun mockEmptyPkgState( id: Int, - hasAutoVerifyDomains: Boolean = true + hasAutoVerifyDomains: Boolean = true, + pkgNameToSignature: (String) -> String? = { null } ): DomainVerificationPkgState { val pkgName = pkgName(id) val domainSetId = UUID(0L, id.toLong()) - return DomainVerificationPkgState(pkgName, domainSetId, hasAutoVerifyDomains) + return DomainVerificationPkgState( + pkgName, + domainSetId, + hasAutoVerifyDomains, + ArrayMap(), + SparseArray(), + pkgNameToSignature(pkgName) + ) } - private fun mockPkgState(id: Int) = mockEmptyPkgState(id).apply { - stateMap["$packageName.com"] = id - userStates[id] = DomainVerificationInternalUserState(id).apply { - addHosts(setOf("$packageName-user.com")) - isLinkHandlingAllowed = true - } - } + private fun mockPkgState(id: Int, pkgNameToSignature: (String) -> String? = { null }) = + mockEmptyPkgState(id, pkgNameToSignature = pkgNameToSignature) + .apply { + stateMap["$packageName.com"] = id + userStates[id] = DomainVerificationInternalUserState(id).apply { + addHosts(setOf("$packageName-user.com")) + isLinkHandlingAllowed = true + } + } private fun pkgName(id: Int) = "$PKG_PREFIX.pkg$id" }