summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl4
-rw-r--r--core/java/com/android/server/backup/PreferredActivityBackupHelper.java67
-rw-r--r--services/core/java/com/android/server/backup/SystemBackupAgent.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java53
-rw-r--r--services/core/java/com/android/server/pm/Settings.java11
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java37
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationPersistence.java32
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java69
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationSettings.java95
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt153
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPersistenceTest.kt12
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt5
12 files changed, 421 insertions, 119 deletions
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 7fe2a41cc4b9..7221ae97c57f 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -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
diff --git a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
index 80636706a9a6..503c71990adb 100644
--- a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
+++ b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java
@@ -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);
diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index d98298cbef5a..fa1820456fb9 100644
--- a/services/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -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));
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 321c5cabc1c1..b270e841923b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1789,6 +1789,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,
PackageSetting>, Output, ExceptionType> block) throws ExceptionType {
@@ -22618,21 +22636,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
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4823c29b96f9..8ebb8a203841 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2323,7 +2323,8 @@ public final class Settings implements Watchable, Snappable {
}
}
- mDomainVerificationManager.writeSettings(serializer);
+ mDomainVerificationManager.writeSettings(serializer, false /* includeSignatures */,
+ UserHandle.USER_ALL);
mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
@@ -2900,13 +2901,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);
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 0f99e1963f28..34e3efb06ecc 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -181,8 +181,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);
@@ -196,20 +196,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
@@ -245,7 +252,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>
@@ -350,7 +357,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();
@@ -394,6 +401,15 @@ public interface DomainVerificationManagerInternal {
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)
*/
@@ -408,6 +424,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;
}
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 f0ad98c26ede..e803457fcb97 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,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();
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 4ae79a209524..1d20a1510c44 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
@@ -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);
@@ -1026,21 +1026,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);
}
});
@@ -1048,11 +1056,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
@@ -1064,9 +1075,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
@@ -1893,6 +1907,15 @@ public class DomainVerificationService extends SystemService
}
@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,
PackageSetting>, Output, ExceptionType> block) throws ExceptionType {
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 c8e95b585b7a..3b2990e33963 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
@@ -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) {
+
+ }
public void writeSettings(@NonNull TypedXmlSerializer xmlSerializer,
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
- @NonNull Function<String, String> pkgSignatureFunction) throws IOException {
+ @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);
+ }
}
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 1097c45f8a9f..752024d6ad98 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -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 {
@@ -449,12 +451,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(vararg pkgSettings: PackageSetting) =
@@ -526,7 +657,23 @@ 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)) }
}
+
+ 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)
+ }
+ }
}
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 7ffbbf6af159..ad652dff901c 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
@@ -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"/>
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt
index e1da727fb64f..00986f741eea 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationTestUtils.kt
@@ -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?>, *, *>)