Merge "Implement domain verification backup and restore" into sc-dev
This commit is contained in:
@@ -310,8 +310,8 @@ interface IPackageManager {
|
||||
void restorePreferredActivities(in byte[] backup, int userId);
|
||||
byte[] getDefaultAppsBackup(int userId);
|
||||
void restoreDefaultApps(in byte[] backup, int userId);
|
||||
byte[] getIntentFilterVerificationBackup(int userId);
|
||||
void restoreIntentFilterVerification(in byte[] backup, int userId);
|
||||
byte[] getDomainVerificationBackup(int userId);
|
||||
void restoreDomainVerification(in byte[] backup, int userId);
|
||||
|
||||
/**
|
||||
* Report the set of 'Home' activity candidates, plus (if any) which of them
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package com.android.server.backup;
|
||||
|
||||
import android.annotation.StringDef;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.backup.BlobBackupHelper;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.os.UserHandle;
|
||||
import android.content.pm.verify.domain.DomainVerificationManager;
|
||||
import android.util.Slog;
|
||||
|
||||
public class PreferredActivityBackupHelper extends BlobBackupHelper {
|
||||
@@ -27,7 +29,7 @@ public class PreferredActivityBackupHelper extends BlobBackupHelper {
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
// current schema of the backup state blob
|
||||
private static final int STATE_VERSION = 3;
|
||||
private static final int STATE_VERSION = 4;
|
||||
|
||||
// key under which the preferred-activity state blob is committed to backup
|
||||
private static final String KEY_PREFERRED = "preferred-activity";
|
||||
@@ -35,14 +37,41 @@ public class PreferredActivityBackupHelper extends BlobBackupHelper {
|
||||
// key for default-browser [etc] state
|
||||
private static final String KEY_DEFAULT_APPS = "default-apps";
|
||||
|
||||
// intent-filter verification state
|
||||
/**
|
||||
* Intent-filter verification state
|
||||
* @deprecated Replaced by {@link #KEY_DOMAIN_VERIFICATION}, retained to ensure the key is
|
||||
* never reused.
|
||||
*/
|
||||
@Deprecated
|
||||
private static final String KEY_INTENT_VERIFICATION = "intent-verification";
|
||||
|
||||
public PreferredActivityBackupHelper() {
|
||||
super(STATE_VERSION,
|
||||
KEY_PREFERRED,
|
||||
KEY_DEFAULT_APPS,
|
||||
KEY_INTENT_VERIFICATION);
|
||||
/**
|
||||
* State for {@link DomainVerificationManager}.
|
||||
*/
|
||||
private static final String KEY_DOMAIN_VERIFICATION = "domain-verification";
|
||||
|
||||
private static final String[] KEYS = new String[] {
|
||||
KEY_PREFERRED,
|
||||
KEY_DEFAULT_APPS,
|
||||
KEY_INTENT_VERIFICATION,
|
||||
KEY_DOMAIN_VERIFICATION
|
||||
};
|
||||
|
||||
@StringDef(value = {
|
||||
KEY_PREFERRED,
|
||||
KEY_DEFAULT_APPS,
|
||||
KEY_INTENT_VERIFICATION,
|
||||
KEY_DOMAIN_VERIFICATION
|
||||
})
|
||||
private @interface Key {
|
||||
}
|
||||
|
||||
@UserIdInt
|
||||
private final int mUserId;
|
||||
|
||||
public PreferredActivityBackupHelper(@UserIdInt int userId) {
|
||||
super(STATE_VERSION, KEYS);
|
||||
mUserId = userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,14 +81,16 @@ public class PreferredActivityBackupHelper extends BlobBackupHelper {
|
||||
Slog.d(TAG, "Handling backup of " + key);
|
||||
}
|
||||
try {
|
||||
// TODO: http://b/22388012
|
||||
switch (key) {
|
||||
case KEY_PREFERRED:
|
||||
return pm.getPreferredActivityBackup(UserHandle.USER_SYSTEM);
|
||||
return pm.getPreferredActivityBackup(mUserId);
|
||||
case KEY_DEFAULT_APPS:
|
||||
return pm.getDefaultAppsBackup(UserHandle.USER_SYSTEM);
|
||||
return pm.getDefaultAppsBackup(mUserId);
|
||||
case KEY_INTENT_VERIFICATION:
|
||||
return pm.getIntentFilterVerificationBackup(UserHandle.USER_SYSTEM);
|
||||
// Deprecated
|
||||
return null;
|
||||
case KEY_DOMAIN_VERIFICATION:
|
||||
return pm.getDomainVerificationBackup(mUserId);
|
||||
default:
|
||||
Slog.w(TAG, "Unexpected backup key " + key);
|
||||
}
|
||||
@@ -70,22 +101,24 @@ public class PreferredActivityBackupHelper extends BlobBackupHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyRestoredPayload(String key, byte[] payload) {
|
||||
protected void applyRestoredPayload(@Key String key, byte[] payload) {
|
||||
IPackageManager pm = AppGlobals.getPackageManager();
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Handling restore of " + key);
|
||||
}
|
||||
try {
|
||||
// TODO: http://b/22388012
|
||||
switch (key) {
|
||||
case KEY_PREFERRED:
|
||||
pm.restorePreferredActivities(payload, UserHandle.USER_SYSTEM);
|
||||
pm.restorePreferredActivities(payload, mUserId);
|
||||
break;
|
||||
case KEY_DEFAULT_APPS:
|
||||
pm.restoreDefaultApps(payload, UserHandle.USER_SYSTEM);
|
||||
pm.restoreDefaultApps(payload, mUserId);
|
||||
break;
|
||||
case KEY_INTENT_VERIFICATION:
|
||||
pm.restoreIntentFilterVerification(payload, UserHandle.USER_SYSTEM);
|
||||
// Deprecated
|
||||
break;
|
||||
case KEY_DOMAIN_VERIFICATION:
|
||||
pm.restoreDomainVerification(payload, mUserId);
|
||||
break;
|
||||
default:
|
||||
Slog.w(TAG, "Unexpected restore key " + key);
|
||||
|
||||
@@ -94,7 +94,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
|
||||
mUserId = user.getIdentifier();
|
||||
|
||||
addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this, mUserId));
|
||||
addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
|
||||
addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(mUserId));
|
||||
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(mUserId));
|
||||
addHelper(PERMISSION_HELPER, new PermissionBackupHelper(mUserId));
|
||||
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
|
||||
|
||||
@@ -1781,6 +1781,24 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
|
||||
withPackageSettingsThrowing2(
|
||||
@NonNull Throwing2Consumer<Function<String, PackageSetting>, ExceptionOne,
|
||||
ExceptionTwo> block) throws ExceptionOne, ExceptionTwo {
|
||||
final Computer snapshot = snapshotComputer();
|
||||
|
||||
// This method needs to either lock or not lock consistently throughout the method,
|
||||
// so if the live computer is returned, force a wrapping sync block.
|
||||
if (snapshot == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
block.accept(snapshot::getPackageSetting);
|
||||
}
|
||||
} else {
|
||||
block.accept(snapshot::getPackageSetting);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <Output, ExceptionType extends Exception> Output
|
||||
withPackageSettingsReturningThrowing(@NonNull ThrowingFunction<Function<String,
|
||||
@@ -22459,21 +22477,44 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getIntentFilterVerificationBackup(int userId) {
|
||||
public byte[] getDomainVerificationBackup(int userId) {
|
||||
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
|
||||
throw new SecurityException("Only the system may call getIntentFilterVerificationBackup()");
|
||||
throw new SecurityException("Only the system may call getDomainVerificationBackup()");
|
||||
}
|
||||
|
||||
// TODO(b/170746586)
|
||||
return null;
|
||||
try {
|
||||
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||
TypedXmlSerializer serializer = Xml.resolveSerializer(output);
|
||||
mDomainVerificationManager.writeSettings(serializer, true, userId);
|
||||
return output.toByteArray();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (DEBUG_BACKUP) {
|
||||
Slog.e(TAG, "Unable to write domain verification for backup", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restoreIntentFilterVerification(byte[] backup, int userId) {
|
||||
public void restoreDomainVerification(byte[] backup, int userId) {
|
||||
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
|
||||
throw new SecurityException("Only the system may call restorePreferredActivities()");
|
||||
}
|
||||
// TODO(b/170746586)
|
||||
|
||||
try {
|
||||
ByteArrayInputStream input = new ByteArrayInputStream(backup);
|
||||
TypedXmlPullParser parser = Xml.resolvePullParser(input);
|
||||
|
||||
// User ID input isn't necessary here as it assumes the user integers match and that
|
||||
// the only states inside the backup XML are for the target user.
|
||||
mDomainVerificationManager.restoreSettings(parser);
|
||||
input.close();
|
||||
} catch (Exception e) {
|
||||
if (DEBUG_BACKUP) {
|
||||
Slog.e(TAG, "Exception restoring domain verification: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2339,7 +2339,8 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
}
|
||||
|
||||
mDomainVerificationManager.writeSettings(serializer);
|
||||
mDomainVerificationManager.writeSettings(serializer, false /* includeSignatures */,
|
||||
UserHandle.USER_ALL);
|
||||
|
||||
mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
|
||||
|
||||
@@ -2913,13 +2914,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
|
||||
str.close();
|
||||
|
||||
} catch (XmlPullParserException e) {
|
||||
mReadMessages.append("Error reading: " + e.toString());
|
||||
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
|
||||
Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
|
||||
|
||||
} catch (java.io.IOException e) {
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
mReadMessages.append("Error reading: " + e.toString());
|
||||
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
|
||||
Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
|
||||
|
||||
@@ -234,8 +234,8 @@ public interface DomainVerificationManagerInternal {
|
||||
* This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
|
||||
* lock. This should never be called from within the domain verification classes themselves.
|
||||
* <p>
|
||||
* This will NOT call {@link #writeSettings(TypedXmlSerializer)}. That must be handled by the
|
||||
* caller.
|
||||
* This will NOT call {@link #writeSettings(TypedXmlSerializer, boolean, int)}. That must be
|
||||
* handled by the caller.
|
||||
*/
|
||||
void addPackage(@NonNull PackageSetting newPkgSetting);
|
||||
|
||||
@@ -249,20 +249,27 @@ public interface DomainVerificationManagerInternal {
|
||||
* This will mutate internal {@link DomainVerificationPkgState} and so will hold the internal
|
||||
* lock. This should never be called from within the domain verification classes themselves.
|
||||
* <p>
|
||||
* This will NOT call {@link #writeSettings(TypedXmlSerializer)}. That must be handled by the
|
||||
* caller.
|
||||
* This will NOT call {@link #writeSettings(TypedXmlSerializer, boolean, int)}. That must be
|
||||
* handled by the caller.
|
||||
*/
|
||||
void migrateState(@NonNull PackageSetting oldPkgSetting, @NonNull PackageSetting newPkgSetting);
|
||||
|
||||
/**
|
||||
* Serializes the entire internal state. This is equivalent to a full backup of the existing
|
||||
* verification state. This write includes legacy state, as a sibling tag the modern state.
|
||||
*
|
||||
* @param includeSignatures Whether to include the package signatures in the output, mainly
|
||||
* used for backing up the user settings and ensuring they're
|
||||
* re-attached to the same package.
|
||||
* @param userId The user to write out. Supports {@link UserHandle#USER_ALL} if all users
|
||||
* should be written.
|
||||
*/
|
||||
void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException;
|
||||
void writeSettings(@NonNull TypedXmlSerializer serializer, boolean includeSignatures,
|
||||
@UserIdInt int userId) throws IOException;
|
||||
|
||||
/**
|
||||
* Read back a list of {@link DomainVerificationPkgState}s previously written by {@link
|
||||
* #writeSettings(TypedXmlSerializer)}. Assumes that the
|
||||
* #writeSettings(TypedXmlSerializer, boolean, int)}. Assumes that the
|
||||
* {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS} tag has already been entered.
|
||||
* <p>
|
||||
* This is expected to only be used to re-attach states for packages already known to be on the
|
||||
@@ -298,7 +305,7 @@ public interface DomainVerificationManagerInternal {
|
||||
|
||||
/**
|
||||
* Restore a list of {@link DomainVerificationPkgState}s previously written by {@link
|
||||
* #writeSettings(TypedXmlSerializer)}. Assumes that the
|
||||
* #writeSettings(TypedXmlSerializer, boolean, int)}. Assumes that the
|
||||
* {@link DomainVerificationPersistence#TAG_DOMAIN_VERIFICATIONS}
|
||||
* tag has already been entered.
|
||||
* <p>
|
||||
@@ -403,7 +410,7 @@ public interface DomainVerificationManagerInternal {
|
||||
|
||||
/**
|
||||
* Notify that a settings change has been made and that eventually
|
||||
* {@link #writeSettings(TypedXmlSerializer)} should be invoked by the parent.
|
||||
* {@link #writeSettings(TypedXmlSerializer, boolean, int)} should be invoked by the parent.
|
||||
*/
|
||||
void scheduleWriteSettings();
|
||||
|
||||
@@ -446,6 +453,15 @@ public interface DomainVerificationManagerInternal {
|
||||
@NonNull ThrowingConsumer<Function<String, PackageSetting>, ExceptionType> block)
|
||||
throws ExceptionType;
|
||||
|
||||
/**
|
||||
* Variant which throws 2 exceptions.
|
||||
* @see #withPackageSettings(Consumer)
|
||||
*/
|
||||
<ExceptionOne extends Exception, ExceptionTwo extends Exception> void
|
||||
withPackageSettingsThrowing2(
|
||||
@NonNull Throwing2Consumer<Function<String, PackageSetting>, ExceptionOne,
|
||||
ExceptionTwo> block) throws ExceptionOne, ExceptionTwo;
|
||||
|
||||
/**
|
||||
* Variant which returns a value to the caller and throws.
|
||||
* @see #withPackageSettings(Consumer)
|
||||
@@ -461,6 +477,11 @@ public interface DomainVerificationManagerInternal {
|
||||
void accept(Input input) throws ExceptionType;
|
||||
}
|
||||
|
||||
interface Throwing2Consumer<Input, ExceptionOne extends Exception,
|
||||
ExceptionTwo extends Exception> {
|
||||
void accept(Input input) throws ExceptionOne, ExceptionTwo;
|
||||
}
|
||||
|
||||
interface ThrowingFunction<Input, Output, ExceptionType extends Exception> {
|
||||
Output apply(Input input) throws ExceptionType;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ package com.android.server.pm.verify.domain;
|
||||
|
||||
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.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
@@ -77,7 +79,8 @@ public class DomainVerificationPersistence {
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> attached,
|
||||
@NonNull ArrayMap<String, DomainVerificationPkgState> pending,
|
||||
@NonNull ArrayMap<String, DomainVerificationPkgState> restored,
|
||||
@Nullable Function<String, String> pkgNameToSignature) throws IOException {
|
||||
@UserIdInt int userId, @Nullable Function<String, String> pkgNameToSignature)
|
||||
throws IOException {
|
||||
try (SettingsXml.Serializer serializer = SettingsXml.serializer(xmlSerializer)) {
|
||||
try (SettingsXml.WriteSection ignored = serializer.startSection(
|
||||
TAG_DOMAIN_VERIFICATIONS)) {
|
||||
@@ -98,26 +101,27 @@ public class DomainVerificationPersistence {
|
||||
}
|
||||
|
||||
try (SettingsXml.WriteSection activeSection = serializer.startSection(TAG_ACTIVE)) {
|
||||
writePackageStates(activeSection, active, pkgNameToSignature);
|
||||
writePackageStates(activeSection, active, userId, pkgNameToSignature);
|
||||
}
|
||||
|
||||
try (SettingsXml.WriteSection restoredSection = serializer.startSection(
|
||||
TAG_RESTORED)) {
|
||||
writePackageStates(restoredSection, restored.values(), pkgNameToSignature);
|
||||
writePackageStates(restoredSection, restored.values(), userId,
|
||||
pkgNameToSignature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void writePackageStates(@NonNull SettingsXml.WriteSection section,
|
||||
@NonNull Collection<DomainVerificationPkgState> states,
|
||||
@NonNull Collection<DomainVerificationPkgState> states, int userId,
|
||||
@Nullable Function<String, String> pkgNameToSignature) throws IOException {
|
||||
if (states.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (DomainVerificationPkgState state : states) {
|
||||
writePkgStateToXml(section, state, pkgNameToSignature);
|
||||
writePkgStateToXml(section, state, userId, pkgNameToSignature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +215,7 @@ public class DomainVerificationPersistence {
|
||||
}
|
||||
|
||||
private static void writePkgStateToXml(@NonNull SettingsXml.WriteSection parentSection,
|
||||
@NonNull DomainVerificationPkgState pkgState,
|
||||
@NonNull DomainVerificationPkgState pkgState, @UserIdInt int userId,
|
||||
@Nullable Function<String, String> pkgNameToSignature) throws IOException {
|
||||
String packageName = pkgState.getPackageName();
|
||||
String signature = pkgNameToSignature == null
|
||||
@@ -231,11 +235,12 @@ public class DomainVerificationPersistence {
|
||||
pkgState.isHasAutoVerifyDomains())
|
||||
.attribute(ATTR_SIGNATURE, signature)) {
|
||||
writeStateMap(parentSection, pkgState.getStateMap());
|
||||
writeUserStates(parentSection, pkgState.getUserStates());
|
||||
writeUserStates(parentSection, userId, pkgState.getUserStates());
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeUserStates(@NonNull SettingsXml.WriteSection parentSection,
|
||||
@UserIdInt int userId,
|
||||
@NonNull SparseArray<DomainVerificationInternalUserState> states) throws IOException {
|
||||
int size = states.size();
|
||||
if (size == 0) {
|
||||
@@ -243,8 +248,15 @@ public class DomainVerificationPersistence {
|
||||
}
|
||||
|
||||
try (SettingsXml.WriteSection section = parentSection.startSection(TAG_USER_STATES)) {
|
||||
for (int index = 0; index < size; index++) {
|
||||
writeUserStateToXml(section, states.valueAt(index));
|
||||
if (userId == UserHandle.USER_ALL) {
|
||||
for (int index = 0; index < size; index++) {
|
||||
writeUserStateToXml(section, states.valueAt(index));
|
||||
}
|
||||
} else {
|
||||
DomainVerificationInternalUserState userState = states.get(userId);
|
||||
if (userState != null) {
|
||||
writeUserStateToXml(section, userState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,7 +290,7 @@ public class DomainVerificationPersistence {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean allowLinkHandling = section.getBoolean(ATTR_ALLOW_LINK_HANDLING, true);
|
||||
boolean allowLinkHandling = section.getBoolean(ATTR_ALLOW_LINK_HANDLING, false);
|
||||
ArraySet<String> enabledHosts = new ArraySet<>();
|
||||
|
||||
SettingsXml.ChildSection child = section.children();
|
||||
|
||||
@@ -129,10 +129,10 @@ public class DomainVerificationService extends SystemService
|
||||
private final PlatformCompat mPlatformCompat;
|
||||
|
||||
@NonNull
|
||||
private final DomainVerificationSettings mSettings;
|
||||
private final DomainVerificationCollector mCollector;
|
||||
|
||||
@NonNull
|
||||
private final DomainVerificationCollector mCollector;
|
||||
private final DomainVerificationSettings mSettings;
|
||||
|
||||
@NonNull
|
||||
private final DomainVerificationEnforcer mEnforcer;
|
||||
@@ -159,8 +159,8 @@ public class DomainVerificationService extends SystemService
|
||||
super(context);
|
||||
mSystemConfig = systemConfig;
|
||||
mPlatformCompat = platformCompat;
|
||||
mSettings = new DomainVerificationSettings();
|
||||
mCollector = new DomainVerificationCollector(platformCompat, systemConfig);
|
||||
mSettings = new DomainVerificationSettings(mCollector);
|
||||
mEnforcer = new DomainVerificationEnforcer(context);
|
||||
mDebug = new DomainVerificationDebug(mCollector);
|
||||
mShell = new DomainVerificationShell(this);
|
||||
@@ -1045,21 +1045,29 @@ public class DomainVerificationService extends SystemService
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSettings(@NonNull TypedXmlSerializer serializer) throws IOException {
|
||||
public void writeSettings(@NonNull TypedXmlSerializer serializer, boolean includeSignatures,
|
||||
@UserIdInt int userId)
|
||||
throws IOException {
|
||||
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;
|
||||
}
|
||||
Function<String, String> pkgNameToSignature = null;
|
||||
if (includeSignatures) {
|
||||
pkgNameToSignature = 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());
|
||||
});
|
||||
return PackageUtils.computeSignaturesSha256Digest(
|
||||
pkgSetting.getSignatures());
|
||||
};
|
||||
}
|
||||
|
||||
mSettings.writeSettings(serializer, mAttachedPkgStates, userId, pkgNameToSignature);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1067,11 +1075,14 @@ public class DomainVerificationService extends SystemService
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readSettings(@NonNull TypedXmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
synchronized (mLock) {
|
||||
mSettings.readSettings(parser, mAttachedPkgStates);
|
||||
}
|
||||
public void readSettings(@NonNull TypedXmlPullParser parser) throws IOException,
|
||||
XmlPullParserException {
|
||||
mConnection.<IOException, XmlPullParserException>withPackageSettingsThrowing2(
|
||||
pkgSettings -> {
|
||||
synchronized (mLock) {
|
||||
mSettings.readSettings(parser, mAttachedPkgStates, pkgSettings);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1083,9 +1094,12 @@ public class DomainVerificationService extends SystemService
|
||||
@Override
|
||||
public void restoreSettings(@NonNull TypedXmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
synchronized (mLock) {
|
||||
mSettings.restoreSettings(parser, mAttachedPkgStates);
|
||||
}
|
||||
mConnection.<IOException, XmlPullParserException>withPackageSettingsThrowing2(
|
||||
pkgSettings -> {
|
||||
synchronized (mLock) {
|
||||
mSettings.restoreSettings(parser, mAttachedPkgStates, pkgSettings);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2030,6 +2044,15 @@ public class DomainVerificationService extends SystemService
|
||||
mConnection.withPackageSettingsThrowing(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
|
||||
withPackageSettingsThrowing2(
|
||||
@NonNull Throwing2Consumer<Function<String, PackageSetting>, ExceptionOne,
|
||||
ExceptionTwo> block) throws ExceptionOne, ExceptionTwo {
|
||||
enforceLocking();
|
||||
mConnection.withPackageSettingsThrowing2(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <Output, ExceptionType extends Exception> Output
|
||||
withPackageSettingsReturningThrowing(@NonNull ThrowingFunction<Function<String,
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.pm.verify.domain.DomainVerificationState;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.SparseArray;
|
||||
@@ -29,6 +28,8 @@ import android.util.TypedXmlSerializer;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.pm.PackageSetting;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState;
|
||||
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
|
||||
import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
|
||||
@@ -36,10 +37,15 @@ import com.android.server.pm.verify.domain.models.DomainVerificationStateMap;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
class DomainVerificationSettings {
|
||||
|
||||
@NonNull
|
||||
private final DomainVerificationCollector mCollector;
|
||||
|
||||
/**
|
||||
* States read from disk that have yet to attach to a package, but are expected to, generally in
|
||||
* the context of scanning packages already on disk. This is expected to be empty once the boot
|
||||
@@ -67,24 +73,35 @@ class DomainVerificationSettings {
|
||||
*/
|
||||
private final Object mLock = new Object();
|
||||
|
||||
public DomainVerificationSettings(@NonNull DomainVerificationCollector collector) {
|
||||
mCollector = collector;
|
||||
}
|
||||
|
||||
public void writeSettings(@NonNull TypedXmlSerializer xmlSerializer,
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
|
||||
@NonNull Function<String, String> pkgSignatureFunction) throws IOException {
|
||||
@NonNull Function<String, String> pkgSignatureFunction) {
|
||||
|
||||
}
|
||||
|
||||
public void writeSettings(@NonNull TypedXmlSerializer xmlSerializer,
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
|
||||
@UserIdInt int userId, @NonNull Function<String, String> pkgSignatureFunction)
|
||||
throws IOException {
|
||||
synchronized (mLock) {
|
||||
DomainVerificationPersistence.writeToXml(xmlSerializer, liveState,
|
||||
mPendingPkgStates, mRestoredPkgStates, pkgSignatureFunction);
|
||||
mPendingPkgStates, mRestoredPkgStates, userId, 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, Function)} on the same device
|
||||
* setup.
|
||||
* #writeSettings(TypedXmlSerializer, DomainVerificationStateMap, int, Function)} on the same
|
||||
* device setup.
|
||||
*/
|
||||
public void readSettings(@NonNull TypedXmlPullParser parser,
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
|
||||
@NonNull Function<String, PackageSetting> pkgSettingFunction)
|
||||
throws IOException, XmlPullParserException {
|
||||
DomainVerificationPersistence.ReadResult result =
|
||||
DomainVerificationPersistence.readFromXml(parser);
|
||||
@@ -101,7 +118,7 @@ class DomainVerificationSettings {
|
||||
// This branch should never be possible. Settings should be read from disk
|
||||
// before any states are attached. But just in case, handle it.
|
||||
if (!existingState.getId().equals(pkgState.getId())) {
|
||||
mergePkgState(existingState, pkgState);
|
||||
mergePkgState(existingState, pkgState, pkgSettingFunction);
|
||||
}
|
||||
} else {
|
||||
mPendingPkgStates.put(pkgName, pkgState);
|
||||
@@ -121,7 +138,8 @@ class DomainVerificationSettings {
|
||||
* mutating the values. This is intended for restoration across device setups.
|
||||
*/
|
||||
public void restoreSettings(@NonNull TypedXmlPullParser parser,
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState)
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
|
||||
@NonNull Function<String, PackageSetting> pkgSettingFunction)
|
||||
throws IOException, XmlPullParserException {
|
||||
// TODO(b/170746586): Restoration assumes user IDs match, which is probably not the case on
|
||||
// a new device.
|
||||
@@ -148,7 +166,7 @@ class DomainVerificationSettings {
|
||||
}
|
||||
|
||||
if (existingState != null) {
|
||||
mergePkgState(existingState, newState);
|
||||
mergePkgState(existingState, newState, pkgSettingFunction);
|
||||
} else {
|
||||
// If there's no existing state, that means the new state has to be transformed
|
||||
// in preparation for attaching to brand new package that may eventually be
|
||||
@@ -190,31 +208,34 @@ class DomainVerificationSettings {
|
||||
* specific error codes are fresher than the restored state. Essentially state is only restored
|
||||
* to grant additional verifications to an app.
|
||||
* <p>
|
||||
* For user selection state, presence in either state will be considered an enabled host. NOTE:
|
||||
* only {@link UserHandle#USER_SYSTEM} is merged. There is no restore path in place for
|
||||
* multiple users.
|
||||
* <p>
|
||||
* TODO(b/170746586): Figure out the restore path for multiple users
|
||||
* <p>
|
||||
* This will mutate {@param oldState} to contain the merged state.
|
||||
* For user selection state, presence in either state will be considered an enabled host. This
|
||||
* assumes that all user IDs on the device match. If this isn't the case, then restore may set
|
||||
* unexpected values.
|
||||
*
|
||||
* NOTE: This will mutate {@param oldState} to contain the merged state.
|
||||
*/
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
|
||||
public static void mergePkgState(@NonNull DomainVerificationPkgState oldState,
|
||||
@NonNull DomainVerificationPkgState newState) {
|
||||
public void mergePkgState(@NonNull DomainVerificationPkgState oldState,
|
||||
@NonNull DomainVerificationPkgState newState,
|
||||
@NonNull Function<String, PackageSetting> pkgSettingFunction) {
|
||||
PackageSetting pkgSetting = pkgSettingFunction.apply(oldState.getPackageName());
|
||||
AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg();
|
||||
Set<String> validDomains = pkg == null
|
||||
? Collections.emptySet() : mCollector.collectValidAutoVerifyDomains(pkg);
|
||||
|
||||
ArrayMap<String, Integer> oldStateMap = oldState.getStateMap();
|
||||
ArrayMap<String, Integer> newStateMap = newState.getStateMap();
|
||||
int size = newStateMap.size();
|
||||
for (int index = 0; index < size; index++) {
|
||||
String domain = newStateMap.keyAt(index);
|
||||
Integer newStateCode = newStateMap.valueAt(index);
|
||||
Integer oldStateCodeInteger = oldStateMap.get(domain);
|
||||
if (oldStateCodeInteger == null) {
|
||||
if (!validDomains.contains(domain)) {
|
||||
// Cannot add domains to an app
|
||||
continue;
|
||||
}
|
||||
|
||||
int oldStateCode = oldStateCodeInteger;
|
||||
if (oldStateCode == DomainVerificationState.STATE_NO_RESPONSE) {
|
||||
Integer oldStateCode = oldStateMap.get(domain);
|
||||
if (oldStateCode == null || oldStateCode == DomainVerificationState.STATE_NO_RESPONSE) {
|
||||
if (newStateCode == DomainVerificationState.STATE_SUCCESS
|
||||
|| newStateCode == DomainVerificationState.STATE_RESTORED) {
|
||||
oldStateMap.put(domain, DomainVerificationState.STATE_RESTORED);
|
||||
@@ -228,21 +249,21 @@ class DomainVerificationSettings {
|
||||
SparseArray<DomainVerificationInternalUserState> newSelectionStates =
|
||||
newState.getUserStates();
|
||||
|
||||
DomainVerificationInternalUserState newUserState =
|
||||
newSelectionStates.get(UserHandle.USER_SYSTEM);
|
||||
if (newUserState != null) {
|
||||
ArraySet<String> newEnabledHosts = newUserState.getEnabledHosts();
|
||||
DomainVerificationInternalUserState oldUserState =
|
||||
oldSelectionStates.get(UserHandle.USER_SYSTEM);
|
||||
|
||||
boolean linkHandlingAllowed = newUserState.isLinkHandlingAllowed();
|
||||
if (oldUserState == null) {
|
||||
oldUserState = new DomainVerificationInternalUserState(UserHandle.USER_SYSTEM,
|
||||
newEnabledHosts, linkHandlingAllowed);
|
||||
oldSelectionStates.put(UserHandle.USER_SYSTEM, oldUserState);
|
||||
} else {
|
||||
oldUserState.addHosts(newEnabledHosts)
|
||||
.setLinkHandlingAllowed(linkHandlingAllowed);
|
||||
final int userStateSize = newSelectionStates.size();
|
||||
for (int index = 0; index < userStateSize; index++) {
|
||||
int userId = newSelectionStates.keyAt(index);
|
||||
DomainVerificationInternalUserState newUserState = newSelectionStates.valueAt(index);
|
||||
if (newUserState != null) {
|
||||
ArraySet<String> newEnabledHosts = newUserState.getEnabledHosts();
|
||||
DomainVerificationInternalUserState oldUserState = oldSelectionStates.get(userId);
|
||||
boolean linkHandlingAllowed = newUserState.isLinkHandlingAllowed();
|
||||
if (oldUserState == null) {
|
||||
oldSelectionStates.put(userId, new DomainVerificationInternalUserState(userId,
|
||||
newEnabledHosts, linkHandlingAllowed));
|
||||
} else {
|
||||
oldUserState.addHosts(newEnabledHosts)
|
||||
.setLinkHandlingAllowed(linkHandlingAllowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.ArgumentMatchers.anyLong
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.UUID
|
||||
|
||||
class DomainVerificationPackageTest {
|
||||
@@ -513,12 +515,141 @@ class DomainVerificationPackageTest {
|
||||
assertThat(service.queryValidVerificationPackageNames()).containsExactly(pkgName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun backupAndRestore() {
|
||||
// This test acts as a proxy for true user restore through PackageManager,
|
||||
// as that's much harder to test for real.
|
||||
|
||||
val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
|
||||
val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO,
|
||||
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
|
||||
val serviceBefore = makeService(pkg1, pkg2)
|
||||
serviceBefore.addPackage(pkg1)
|
||||
serviceBefore.addPackage(pkg2)
|
||||
|
||||
serviceBefore.setStatus(pkg1.domainSetId, setOf(DOMAIN_1), STATE_SUCCESS)
|
||||
serviceBefore.setDomainVerificationLinkHandlingAllowed(pkg1.getName(), false, 1)
|
||||
serviceBefore.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_2), true, 0)
|
||||
serviceBefore.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_3), true, 1)
|
||||
|
||||
fun assertExpectedState(service: DomainVerificationService) {
|
||||
service.assertState(
|
||||
pkg1, userId = 0, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_VERIFIED,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
)
|
||||
)
|
||||
|
||||
service.assertState(
|
||||
pkg1, userId = 1, linkHandingAllowed = false, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_VERIFIED,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
)
|
||||
)
|
||||
|
||||
service.assertState(
|
||||
pkg2, userId = 0, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_2 to DOMAIN_STATE_SELECTED,
|
||||
DOMAIN_3 to DOMAIN_STATE_NONE
|
||||
)
|
||||
)
|
||||
|
||||
service.assertState(
|
||||
pkg2, userId = 1, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_3 to DOMAIN_STATE_SELECTED,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
assertExpectedState(serviceBefore)
|
||||
|
||||
val backupUser0 = ByteArrayOutputStream().use {
|
||||
serviceBefore.writeSettings(Xml.resolveSerializer(it), true, 0)
|
||||
it.toByteArray()
|
||||
}
|
||||
|
||||
val backupUser1 = ByteArrayOutputStream().use {
|
||||
serviceBefore.writeSettings(Xml.resolveSerializer(it), true, 1)
|
||||
it.toByteArray()
|
||||
}
|
||||
|
||||
val serviceAfter = makeService(pkg1, pkg2)
|
||||
serviceAfter.addPackage(pkg1)
|
||||
serviceAfter.addPackage(pkg2)
|
||||
|
||||
// Check the state is default before the restoration applies
|
||||
listOf(0, 1).forEach {
|
||||
serviceAfter.assertState(
|
||||
pkg1, userId = it, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
listOf(0, 1).forEach {
|
||||
serviceAfter.assertState(
|
||||
pkg2, userId = it, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_3 to DOMAIN_STATE_NONE,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ByteArrayInputStream(backupUser1).use {
|
||||
serviceAfter.restoreSettings(Xml.resolvePullParser(it))
|
||||
}
|
||||
|
||||
// Assert user 1 was restored
|
||||
serviceAfter.assertState(
|
||||
pkg1, userId = 1, linkHandingAllowed = false, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_VERIFIED,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
)
|
||||
)
|
||||
|
||||
serviceAfter.assertState(
|
||||
pkg2, userId = 1, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_3 to DOMAIN_STATE_SELECTED,
|
||||
)
|
||||
)
|
||||
|
||||
// User 0 has domain verified (since that's not user-specific)
|
||||
serviceAfter.assertState(
|
||||
pkg1, userId = 0, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_VERIFIED,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
)
|
||||
)
|
||||
|
||||
// But user 0 is missing any user selected state
|
||||
serviceAfter.assertState(
|
||||
pkg2, userId = 0, hostToStateMap = mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_3 to DOMAIN_STATE_NONE,
|
||||
)
|
||||
)
|
||||
|
||||
ByteArrayInputStream(backupUser0).use {
|
||||
serviceAfter.restoreSettings(Xml.resolvePullParser(it))
|
||||
}
|
||||
|
||||
assertExpectedState(serviceAfter)
|
||||
}
|
||||
|
||||
private fun DomainVerificationService.getInfo(pkgName: String) =
|
||||
getDomainVerificationInfo(pkgName)
|
||||
.also { assertThat(it).isNotNull() }!!
|
||||
|
||||
private fun DomainVerificationService.getUserState(pkgName: String) =
|
||||
getDomainVerificationUserState(pkgName, USER_ID)
|
||||
private fun DomainVerificationService.getUserState(pkgName: String, userId: Int = USER_ID) =
|
||||
getDomainVerificationUserState(pkgName, userId)
|
||||
.also { assertThat(it).isNotNull() }!!
|
||||
|
||||
private fun makeService(
|
||||
@@ -600,8 +731,24 @@ class DomainVerificationPackageTest {
|
||||
whenever(this.domainSetId) { domainSetId }
|
||||
whenever(getInstantApp(anyInt())) { false }
|
||||
whenever(firstInstallTime) { 0L }
|
||||
whenever(readUserState(USER_ID)) { PackageUserState() }
|
||||
whenever(readUserState(0)) { PackageUserState() }
|
||||
whenever(readUserState(1)) { PackageUserState() }
|
||||
whenever(signatures) { arrayOf(Signature(signature)) }
|
||||
whenever(isSystem) { isSystemApp }
|
||||
}
|
||||
|
||||
private fun DomainVerificationService.assertState(
|
||||
pkg: PackageSetting,
|
||||
userId: Int,
|
||||
linkHandingAllowed: Boolean = true,
|
||||
hostToStateMap: Map<String, Int>
|
||||
) {
|
||||
getUserState(pkg.getName(), userId).apply {
|
||||
assertThat(this.packageName).isEqualTo(pkg.getName())
|
||||
assertThat(this.identifier).isEqualTo(pkg.domainSetId)
|
||||
assertThat(this.isLinkHandlingAllowed).isEqualTo(linkHandingAllowed)
|
||||
assertThat(this.user.identifier).isEqualTo(userId)
|
||||
assertThat(this.hostToStateMap).containsExactlyEntriesIn(hostToStateMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.pm.test.verify.domain
|
||||
|
||||
import android.content.pm.verify.domain.DomainVerificationState
|
||||
import android.os.UserHandle
|
||||
import android.util.ArrayMap
|
||||
import android.util.SparseArray
|
||||
import android.util.TypedXmlPullParser
|
||||
@@ -110,7 +111,8 @@ class DomainVerificationPersistenceTest {
|
||||
fun writeAndReadBackNormal() {
|
||||
val (attached, pending, restored) = mockWriteValues()
|
||||
val file = tempFolder.newFile().writeXml {
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored, null)
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored,
|
||||
UserHandle.USER_ALL, null)
|
||||
}
|
||||
|
||||
val xml = file.readText()
|
||||
@@ -128,7 +130,8 @@ class DomainVerificationPersistenceTest {
|
||||
fun writeAndReadBackWithSignature() {
|
||||
val (attached, pending, restored) = mockWriteValues()
|
||||
val file = tempFolder.newFile().writeXml {
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored) {
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored,
|
||||
UserHandle.USER_ALL) {
|
||||
"SIGNATURE_$it"
|
||||
}
|
||||
}
|
||||
@@ -156,7 +159,8 @@ class DomainVerificationPersistenceTest {
|
||||
fun writeStateSignatureIfFunctionReturnsNull() {
|
||||
val (attached, pending, restored) = mockWriteValues { "SIGNATURE_$it" }
|
||||
val file = tempFolder.newFile().writeXml {
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored) { null }
|
||||
DomainVerificationPersistence.writeToXml(it, attached, pending, restored,
|
||||
UserHandle.USER_ALL) { null }
|
||||
}
|
||||
|
||||
val (readActive, readRestored) = file.readXml {
|
||||
@@ -254,7 +258,7 @@ class DomainVerificationPersistenceTest {
|
||||
>
|
||||
<state/>
|
||||
<user-states>
|
||||
<user-state userId="1" allowLinkHandling="false">
|
||||
<user-state userId="1">
|
||||
<enabled-hosts>
|
||||
<host name="example-user1.com"/>
|
||||
<host name="example-user1.org"/>
|
||||
|
||||
@@ -42,6 +42,11 @@ internal object DomainVerificationTestUtils {
|
||||
Function<String, PackageSetting?>, *>)
|
||||
.accept { block(it) }
|
||||
}
|
||||
whenever(withPackageSettingsThrowing2<Exception, Exception>(any())) {
|
||||
(arguments[0] as DomainVerificationManagerInternal.Connection.Throwing2Consumer<
|
||||
Function<String, PackageSetting?>, *, *>)
|
||||
.accept { block(it) }
|
||||
}
|
||||
whenever(withPackageSettingsReturningThrowing<Any, Exception>(any())) {
|
||||
(arguments[0] as DomainVerificationManagerInternal.Connection.ThrowingFunction<
|
||||
Function<String, PackageSetting?>, *, *>)
|
||||
|
||||
Reference in New Issue
Block a user