Merge changes I2cf2e739,I9e4bd242,Ic8b93a8c,Ibccde458,I04942e14, ... into sc-dev
* changes: Fix domain verify restore and add signature check Support serializing package signatures for domain verification state Add domain verification CTS to TEST_MAPPING Fix DomainVerificationService deadlock Check installed and enabled state for package domain approval Revoke domain user selection when approved through shell
This commit is contained in:
@@ -32,6 +32,7 @@ import android.annotation.Nullable;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.overlay.OverlayPaths;
|
||||
import android.content.pm.parsing.ParsingPackageRead;
|
||||
import android.content.pm.parsing.component.ParsedMainComponent;
|
||||
import android.os.BaseBundle;
|
||||
import android.os.Debug;
|
||||
@@ -53,7 +54,6 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -310,6 +310,20 @@ public class PackageUserState {
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isPackageEnabled(@NonNull ParsingPackageRead pkg) {
|
||||
switch (this.enabled) {
|
||||
case COMPONENT_ENABLED_STATE_ENABLED:
|
||||
return true;
|
||||
case COMPONENT_ENABLED_STATE_DISABLED:
|
||||
case COMPONENT_ENABLED_STATE_DISABLED_USER:
|
||||
case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
|
||||
return false;
|
||||
default:
|
||||
case COMPONENT_ENABLED_STATE_DEFAULT:
|
||||
return pkg.isEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled(ComponentInfo componentInfo, int flags) {
|
||||
return isEnabled(componentInfo.applicationInfo.enabled, componentInfo.enabled,
|
||||
componentInfo.name, flags);
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
"include-filter": "com.android.server.pm.test.verify.domain"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CtsDomainVerificationDeviceTestCases"
|
||||
},
|
||||
{
|
||||
"name": "CtsDomainVerificationHostTestCases"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -356,6 +356,7 @@ import com.android.internal.util.CollectionUtils;
|
||||
import com.android.internal.util.ConcurrentUtils;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.internal.util.FunctionalUtils;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.permission.persistence.RuntimePermissionsPersistence;
|
||||
@@ -1686,9 +1687,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
private final DomainVerificationConnection mDomainVerificationConnection =
|
||||
new DomainVerificationConnection();
|
||||
|
||||
private class DomainVerificationConnection implements
|
||||
DomainVerificationService.Connection, DomainVerificationProxyV1.Connection,
|
||||
DomainVerificationProxyV2.Connection {
|
||||
private class DomainVerificationConnection implements DomainVerificationService.Connection,
|
||||
DomainVerificationProxyV1.Connection, DomainVerificationProxyV2.Connection {
|
||||
|
||||
@Override
|
||||
public void scheduleWriteSettings() {
|
||||
@@ -1732,22 +1732,77 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
return callingUid == getPackageUid(packageName, 0, callingUserId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PackageSetting getPackageSettingLocked(@NonNull String pkgName) {
|
||||
return PackageManagerService.this.getPackageSetting(pkgName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AndroidPackage getPackageLocked(@NonNull String pkgName) {
|
||||
return PackageManagerService.this.getPackage(pkgName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public AndroidPackage getPackage(@NonNull String packageName) {
|
||||
return getPackageLocked(packageName);
|
||||
return PackageManagerService.this.getPackage(packageName);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public void withPackageSettings(@NonNull Consumer<Function<String, PackageSetting>> block) {
|
||||
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> Output withPackageSettingsReturning(
|
||||
@NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
|
||||
block) {
|
||||
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) {
|
||||
return block.apply(snapshot::getPackageSetting);
|
||||
}
|
||||
} else {
|
||||
return block.apply(snapshot::getPackageSetting);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ExceptionType extends Exception> void withPackageSettingsThrowing(
|
||||
@NonNull ThrowingConsumer<Function<String, PackageSetting>, ExceptionType> block)
|
||||
throws ExceptionType {
|
||||
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 {
|
||||
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) {
|
||||
return block.apply(snapshot::getPackageSetting);
|
||||
}
|
||||
} else {
|
||||
return block.apply(snapshot::getPackageSetting);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2012,7 +2067,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
// Cached attributes. The names in this class are the same as the
|
||||
// names in PackageManagerService; see that class for documentation.
|
||||
private final Settings mSettings;
|
||||
protected final Settings mSettings;
|
||||
private final WatchedSparseIntArray mIsolatedOwners;
|
||||
private final WatchedArrayMap<String, AndroidPackage> mPackages;
|
||||
private final WatchedArrayMap<ComponentName, ParsedInstrumentation>
|
||||
@@ -4669,6 +4724,17 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
mLock = mService.mLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicilty snapshot {@link Settings#mPackages} for cases where the caller must not lock
|
||||
* in order to get package data. It is expected that the caller locks itself to be able
|
||||
* to block on changes to the package data and bring itself up to date once the change
|
||||
* propagates to it. Use with heavy caution.
|
||||
* @return
|
||||
*/
|
||||
private Map<String, PackageSetting> snapshotPackageSettings() {
|
||||
return mSettings.snapshot().mPackages;
|
||||
}
|
||||
|
||||
public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
|
||||
String resolvedType, int flags, int userId, int callingUid,
|
||||
String instantAppPkgName) {
|
||||
@@ -4800,7 +4866,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
// Compute read-only functions, based on live data. This attribute may be modified multiple
|
||||
// times during the PackageManagerService constructor but it should not be modified thereafter.
|
||||
private Computer mLiveComputer;
|
||||
private ComputerLocked mLiveComputer;
|
||||
// A lock-free cache for frequently called functions.
|
||||
private volatile Computer mSnapshotComputer;
|
||||
// If true, the snapshot is invalid (stale). The attribute is static since it may be
|
||||
@@ -21760,6 +21826,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
clearPackagePreferredActivities(ps.name, nextUserId);
|
||||
mPermissionManager.onPackageUninstalled(ps.name, ps.appId, pkg, sharedUserPkgs,
|
||||
nextUserId);
|
||||
mDomainVerificationManager.clearPackageForUser(ps.name, nextUserId);
|
||||
}
|
||||
|
||||
if (outInfo != null) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.verify.domain.DomainVerificationInfo;
|
||||
import android.content.pm.verify.domain.DomainVerificationManager;
|
||||
import android.content.pm.verify.domain.DomainVerificationState;
|
||||
import android.os.Binder;
|
||||
import android.os.UserHandle;
|
||||
import android.util.IndentingPrintWriter;
|
||||
@@ -35,8 +36,10 @@ import android.util.Pair;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.TypedXmlSerializer;
|
||||
|
||||
import com.android.internal.util.FunctionalUtils;
|
||||
import com.android.server.pm.PackageManagerService;
|
||||
import com.android.server.pm.PackageSetting;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.pm.Settings;
|
||||
import com.android.server.pm.verify.domain.models.DomainVerificationPkgState;
|
||||
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
|
||||
|
||||
@@ -46,6 +49,7 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface DomainVerificationManagerInternal {
|
||||
@@ -108,9 +112,10 @@ public interface DomainVerificationManagerInternal {
|
||||
int APPROVAL_LEVEL_INSTANT_APP = 5;
|
||||
|
||||
/**
|
||||
* Defines the possible values for {@link #approvalLevelForDomain(PackageSetting, Intent, int)}
|
||||
* which sorts packages by approval priority. A higher numerical value means the package should
|
||||
* override all lower values. This means that comparison using less/greater than IS valid.
|
||||
* Defines the possible values for
|
||||
* {@link #approvalLevelForDomain(PackageSetting, Intent, List, int, int)} which sorts packages
|
||||
* by approval priority. A higher numerical value means the package should override all lower
|
||||
* values. This means that comparison using less/greater than IS valid.
|
||||
*
|
||||
* Negative values are possible, although not implemented, reserved if explicit disable of a
|
||||
* package for a domain needs to be tracked.
|
||||
@@ -184,7 +189,7 @@ public interface DomainVerificationManagerInternal {
|
||||
/**
|
||||
* Migrates verification state from a previous install to a new one. It is expected that the
|
||||
* {@link PackageSetting#getDomainSetId()} already be set to the correct value, usually from
|
||||
* {@link #generateNewId()}. This will preserve {@link DomainVerificationManager#STATE_SUCCESS}
|
||||
* {@link #generateNewId()}. This will preserve {@link DomainVerificationState#STATE_SUCCESS}
|
||||
* domains under the assumption that the new package will pass the same server side config as
|
||||
* the previous package, as they have matching signatures.
|
||||
* <p>
|
||||
@@ -227,6 +232,11 @@ public interface DomainVerificationManagerInternal {
|
||||
*/
|
||||
void clearPackage(@NonNull String packageName);
|
||||
|
||||
/**
|
||||
* Remove all state for the given package for the given user.
|
||||
*/
|
||||
void clearPackageForUser(@NonNull String packageName, @UserIdInt int userId);
|
||||
|
||||
/**
|
||||
* Delete all the state for a user. This can be because the user has been removed from the
|
||||
* device, or simply that the state for a user should be deleted.
|
||||
@@ -271,7 +281,7 @@ public interface DomainVerificationManagerInternal {
|
||||
|
||||
/**
|
||||
* Until the legacy APIs are entirely removed, returns the legacy state from the previously
|
||||
* written info stored in {@link com.android.server.pm.Settings}.
|
||||
* written info stored in {@link Settings}.
|
||||
*/
|
||||
int getLegacyState(@NonNull String packageName, @UserIdInt int userId);
|
||||
|
||||
@@ -283,15 +293,12 @@ public interface DomainVerificationManagerInternal {
|
||||
* @param userId the specific user to print, or null to skip printing user selection
|
||||
* states, supports {@link android.os.UserHandle#USER_ALL}
|
||||
* @param pkgSettingFunction the method by which to retrieve package data; if this is called
|
||||
* from {@link com.android.server.pm.PackageManagerService}, it is
|
||||
* expected to pass in the snapshot of {@link PackageSetting} objects,
|
||||
* or if null is passed, the manager may decide to lock {@link
|
||||
* com.android.server.pm.PackageManagerService} through {@link
|
||||
* Connection#getPackageSettingLocked(String)}
|
||||
* from {@link PackageManagerService}, it is
|
||||
* expected to pass in the snapshot of {@link PackageSetting} objects
|
||||
*/
|
||||
void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
|
||||
@Nullable @UserIdInt Integer userId,
|
||||
@Nullable Function<String, PackageSetting> pkgSettingFunction)
|
||||
@NonNull Function<String, PackageSetting> pkgSettingFunction)
|
||||
throws NameNotFoundException;
|
||||
|
||||
@NonNull
|
||||
@@ -363,17 +370,46 @@ public interface DomainVerificationManagerInternal {
|
||||
*/
|
||||
void schedule(int code, @Nullable Object object);
|
||||
|
||||
// TODO(b/178733426): Make DomainVerificationService PMS snapshot aware so it can avoid
|
||||
// locking package state at all. This can be as simple as removing this method in favor of
|
||||
// accepting a PackageSetting function in at every method call, although should probably
|
||||
// be abstracted to a wrapper class.
|
||||
@Nullable
|
||||
PackageSetting getPackageSettingLocked(@NonNull String pkgName);
|
||||
/**
|
||||
* Run a function block that requires access to {@link PackageSetting} data. This will
|
||||
* ensure the {@link PackageManagerService} is taken before
|
||||
* {@link DomainVerificationManagerInternal}'s lock is taken to avoid deadlock.
|
||||
*/
|
||||
void withPackageSettings(@NonNull Consumer<Function<String, PackageSetting>> block);
|
||||
|
||||
@Nullable
|
||||
AndroidPackage getPackageLocked(@NonNull String pkgName);
|
||||
/**
|
||||
* Variant which returns a value to the caller.
|
||||
* @see #withPackageSettings(Consumer)
|
||||
*/
|
||||
<Output> Output withPackageSettingsReturning(
|
||||
@NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageSetting>, Output>
|
||||
block);
|
||||
|
||||
/**
|
||||
* Variant which throws.
|
||||
* @see #withPackageSettings(Consumer)
|
||||
*/
|
||||
<ExceptionType extends Exception> void withPackageSettingsThrowing(
|
||||
@NonNull ThrowingConsumer<Function<String, PackageSetting>, ExceptionType> block)
|
||||
throws ExceptionType;
|
||||
|
||||
/**
|
||||
* Variant which returns a value to the caller and throws.
|
||||
* @see #withPackageSettings(Consumer)
|
||||
*/
|
||||
<Output, ExceptionType extends Exception> Output withPackageSettingsReturningThrowing(
|
||||
@NonNull ThrowingFunction<Function<String, PackageSetting>, Output, ExceptionType>
|
||||
block) throws ExceptionType;
|
||||
|
||||
@UserIdInt
|
||||
int[] getAllUserIds();
|
||||
|
||||
interface ThrowingConsumer<Input, ExceptionType extends Exception> {
|
||||
void accept(Input input) throws ExceptionType;
|
||||
}
|
||||
|
||||
interface ThrowingFunction<Input, Output, ExceptionType extends Exception> {
|
||||
Output apply(Input input) throws ExceptionType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<DomainVerificationPkgState> attached,
|
||||
@NonNull ArrayMap<String, DomainVerificationPkgState> pending,
|
||||
@NonNull ArrayMap<String, DomainVerificationPkgState> restored) throws IOException {
|
||||
@NonNull ArrayMap<String, DomainVerificationPkgState> restored,
|
||||
@Nullable Function<String, String> 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<DomainVerificationPkgState> states) throws IOException {
|
||||
@NonNull Collection<DomainVerificationPkgState> states,
|
||||
@Nullable Function<String, String> 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<String, String> 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)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<DomainVerificationPkgState> liveState)
|
||||
throws IOException {
|
||||
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> liveState,
|
||||
@NonNull Function<String, String> 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<DomainVerificationPkgState> liveState)
|
||||
@@ -155,16 +157,20 @@ class DomainVerificationSettings {
|
||||
// mergePkgState runs, without the merge part.
|
||||
ArrayMap<String, Integer> stateMap = newState.getStateMap();
|
||||
int size = stateMap.size();
|
||||
for (int index = 0; index < size; index++) {
|
||||
for (int index = size - 1; index >= 0; index--) {
|
||||
Integer stateInteger = stateMap.valueAt(index);
|
||||
if (stateInteger != null) {
|
||||
int state = stateInteger;
|
||||
if (state == DomainVerificationState.STATE_SUCCESS
|
||||
|| state == DomainVerificationState.STATE_RESTORED) {
|
||||
stateMap.setValueAt(index, state);
|
||||
stateMap.setValueAt(index, DomainVerificationState.STATE_RESTORED);
|
||||
} else {
|
||||
stateMap.removeAt(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mRestoredPkgStates.put(pkgName, newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,17 +247,41 @@ class DomainVerificationSettings {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeUser(@UserIdInt int userId) {
|
||||
int pendingSize = mPendingPkgStates.size();
|
||||
for (int index = 0; index < pendingSize; index++) {
|
||||
mPendingPkgStates.valueAt(index).removeUser(userId);
|
||||
public void removePackage(@NonNull String packageName) {
|
||||
synchronized (mLock) {
|
||||
mPendingPkgStates.remove(packageName);
|
||||
mRestoredPkgStates.remove(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/170746586): Restored assumes user IDs match, which is probably not the case
|
||||
// on a new device
|
||||
int restoredSize = mRestoredPkgStates.size();
|
||||
for (int index = 0; index < restoredSize; index++) {
|
||||
mRestoredPkgStates.valueAt(index).removeUser(userId);
|
||||
public void removePackageForUser(@NonNull String packageName, @UserIdInt int userId) {
|
||||
synchronized (mLock) {
|
||||
final DomainVerificationPkgState pendingPkgState = mPendingPkgStates.get(packageName);
|
||||
if (pendingPkgState != null) {
|
||||
pendingPkgState.removeUser(userId);
|
||||
}
|
||||
// TODO(b/170746586): Restored assumes user IDs match, which is probably not the case
|
||||
// on a new device
|
||||
final DomainVerificationPkgState restoredPkgState = mRestoredPkgStates.get(packageName);
|
||||
if (restoredPkgState != null) {
|
||||
restoredPkgState.removeUser(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeUser(@UserIdInt int userId) {
|
||||
synchronized (mLock) {
|
||||
int pendingSize = mPendingPkgStates.size();
|
||||
for (int index = 0; index < pendingSize; index++) {
|
||||
mPendingPkgStates.valueAt(index).removeUser(userId);
|
||||
}
|
||||
|
||||
// TODO(b/170746586): Restored assumes user IDs match, which is probably not the case
|
||||
// on a new device
|
||||
int restoredSize = mRestoredPkgStates.size();
|
||||
for (int index = 0; index < restoredSize; index++) {
|
||||
mRestoredPkgStates.valueAt(index).removeUser(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ public class DomainVerificationShell {
|
||||
pw.println(" must be declared by the package for this to work. This command will not");
|
||||
pw.println(" report a failure for domains that could not be applied.");
|
||||
pw.println(" --user <USER_ID>: the user to change selections for");
|
||||
pw.println(" --package <PACKAGE>: the package to set, or \"all\" to set all packages");
|
||||
pw.println(" --package <PACKAGE>: the package to set");
|
||||
pw.println(" <ENABLED>: whether or not to approve the domain");
|
||||
pw.println(" <DOMAINS>: space separated list of domains to change, or \"all\" to");
|
||||
pw.println(" change every domain.");
|
||||
@@ -226,8 +226,6 @@ public class DomainVerificationShell {
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
commandHandler.getErrPrintWriter().println("Error: no package specified");
|
||||
return false;
|
||||
} else if (packageName.equalsIgnoreCase("all")) {
|
||||
packageName = null;
|
||||
}
|
||||
|
||||
if (userId == null) {
|
||||
@@ -478,13 +476,15 @@ public class DomainVerificationShell {
|
||||
* Variant for use by PackageManagerShellCommand to allow the system/developer to override
|
||||
* the state for a domain.
|
||||
*
|
||||
* @param packageName the package whose state to change, or all packages if non is
|
||||
* specified
|
||||
* If an approval fails because of a higher level owner, this method will silently skip the
|
||||
* domain.
|
||||
*
|
||||
* @param packageName the package whose state to change
|
||||
* @param enabled whether the domain is now approved by the user
|
||||
* @param domains the set of domains to change, or null to affect all domains
|
||||
*/
|
||||
void setDomainVerificationUserSelectionInternal(@UserIdInt int userId,
|
||||
@Nullable String packageName, boolean enabled, @Nullable ArraySet<String> domains)
|
||||
@NonNull String packageName, boolean enabled, @Nullable ArraySet<String> domains)
|
||||
throws PackageManager.NameNotFoundException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
"include-filter": "com.android.server.pm.test.verify.domain"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CtsDomainVerificationDeviceTestCases"
|
||||
},
|
||||
{
|
||||
"name": "CtsDomainVerificationHostTestCases"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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<DomainVerificationInternalUserState> 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<String,Integer> stateMap,
|
||||
@NonNull SparseArray<DomainVerificationInternalUserState> userStates) {
|
||||
@NonNull SparseArray<DomainVerificationInternalUserState> 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<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState> 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<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState>)\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<java.lang.String,java.lang.Integer> mStateMap\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState> 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<com.android.server.pm.verify.domain.models.DomainVerificationInternalUserState>)\nclass DomainVerificationPkgState extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.util.SparseArray
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.android.server.pm.PackageSetting
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage
|
||||
import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
|
||||
import com.android.server.pm.verify.domain.DomainVerificationEnforcer
|
||||
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
|
||||
import com.android.server.pm.verify.domain.DomainVerificationService
|
||||
@@ -98,10 +99,13 @@ class DomainVerificationEnforcerTest {
|
||||
mockThrowOnUnmocked {
|
||||
whenever(callingUid) { callingUidInt.get() }
|
||||
whenever(callingUserId) { callingUserIdInt.get() }
|
||||
whenever(getPackageSettingLocked(VISIBLE_PKG)) { visiblePkgSetting }
|
||||
whenever(getPackageLocked(VISIBLE_PKG)) { visiblePkg }
|
||||
whenever(getPackageSettingLocked(INVISIBLE_PKG)) { invisiblePkgSetting }
|
||||
whenever(getPackageLocked(INVISIBLE_PKG)) { invisiblePkg }
|
||||
mockPackageSettings {
|
||||
when (it) {
|
||||
VISIBLE_PKG -> visiblePkgSetting
|
||||
INVISIBLE_PKG -> invisiblePkgSetting
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
whenever(schedule(anyInt(), any()))
|
||||
whenever(scheduleWriteSettings())
|
||||
whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
|
||||
@@ -290,6 +294,7 @@ class DomainVerificationEnforcerTest {
|
||||
fun mockPkg(packageName: String) = mockThrowOnUnmocked<AndroidPackage> {
|
||||
whenever(this.packageName) { packageName }
|
||||
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
|
||||
whenever(isEnabled) { true }
|
||||
whenever(activities) {
|
||||
listOf(
|
||||
ParsedActivity().apply {
|
||||
@@ -330,11 +335,8 @@ class DomainVerificationEnforcerTest {
|
||||
whenever(getName()) { packageName }
|
||||
whenever(getPkg()) { mockPkg(packageName) }
|
||||
whenever(this.domainSetId) { domainSetId }
|
||||
whenever(userState) {
|
||||
SparseArray<PackageUserState>().apply {
|
||||
this[0] = PackageUserState()
|
||||
}
|
||||
}
|
||||
whenever(readUserState(0)) { PackageUserState() }
|
||||
whenever(readUserState(1)) { PackageUserState() }
|
||||
whenever(getInstantApp(anyInt())) { false }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.pm.test.verify.domain
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageUserState
|
||||
import android.content.pm.parsing.component.ParsedActivity
|
||||
import android.content.pm.parsing.component.ParsedIntentInfo
|
||||
import android.content.pm.verify.domain.DomainVerificationInfo
|
||||
@@ -31,6 +32,7 @@ import android.os.Process
|
||||
import android.util.ArraySet
|
||||
import com.android.server.pm.PackageSetting
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage
|
||||
import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
|
||||
import com.android.server.pm.verify.domain.DomainVerificationService
|
||||
import com.android.server.testutils.mockThrowOnUnmocked
|
||||
import com.android.server.testutils.whenever
|
||||
@@ -41,6 +43,7 @@ import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.ArgumentMatchers.anyLong
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class DomainVerificationManagerApiTest {
|
||||
@@ -267,7 +270,16 @@ class DomainVerificationManagerApiTest {
|
||||
|
||||
@Test
|
||||
fun getOwnersForDomain() {
|
||||
val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
|
||||
val pkg1User0Enabled = AtomicBoolean(true)
|
||||
|
||||
val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2), pkgUserState0 = {
|
||||
mockThrowOnUnmocked {
|
||||
whenever(isPackageEnabled(any())) {
|
||||
pkg1User0Enabled.get()
|
||||
}
|
||||
installed = true
|
||||
}
|
||||
})
|
||||
val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
|
||||
|
||||
val service = makeService(pkg1, pkg2).apply {
|
||||
@@ -276,9 +288,20 @@ class DomainVerificationManagerApiTest {
|
||||
|
||||
assertThat(service.getOwnersForDomain(DOMAIN_1, 0)).isEmpty()
|
||||
|
||||
service.setStatus(pkg1.domainSetId, setOf(DOMAIN_1), DomainVerificationInfo.STATE_SUCCESS)
|
||||
|
||||
service.setStatus(pkg2.domainSetId, setOf(DOMAIN_1), DomainVerificationInfo.STATE_SUCCESS)
|
||||
assertThat(
|
||||
service.setStatus(
|
||||
pkg1.domainSetId,
|
||||
setOf(DOMAIN_1),
|
||||
DomainVerificationInfo.STATE_SUCCESS
|
||||
)
|
||||
).isEqualTo(DomainVerificationManager.STATUS_OK)
|
||||
assertThat(
|
||||
service.setStatus(
|
||||
pkg2.domainSetId,
|
||||
setOf(DOMAIN_1),
|
||||
DomainVerificationInfo.STATE_SUCCESS
|
||||
)
|
||||
).isEqualTo(DomainVerificationManager.STATUS_OK)
|
||||
|
||||
service.setUserSelection(pkg1.domainSetId, setOf(DOMAIN_2), true, 0)
|
||||
|
||||
@@ -295,15 +318,65 @@ class DomainVerificationManagerApiTest {
|
||||
assertThat(it.single().packageName).isEqualTo(pkg1.getName())
|
||||
assertThat(it.single().isOverrideable).isEqualTo(true)
|
||||
}
|
||||
assertThat(service.getOwnersForDomain(DOMAIN_2, 1)).isEmpty()
|
||||
|
||||
assertThat(service.getOwnersForDomain(DOMAIN_2, 1)).isEmpty()
|
||||
service.setUserSelection(pkg1.domainSetId, setOf(DOMAIN_2), true, 1)
|
||||
service.getOwnersForDomain(DOMAIN_2, 1).let {
|
||||
assertThat(it).hasSize(1)
|
||||
assertThat(it.single().packageName).isEqualTo(pkg1.getName())
|
||||
assertThat(it.single().isOverrideable).isEqualTo(true)
|
||||
}
|
||||
|
||||
// "Uninstall" the package from user 0 and ensure it's stripped from the results
|
||||
pkg1User0Enabled.set(false)
|
||||
service.clearPackageForUser(pkg1.getName(), 0)
|
||||
|
||||
service.getOwnersForDomain(DOMAIN_1, 0).let {
|
||||
assertThat(it).hasSize(1)
|
||||
assertThat(it.single().packageName).isEqualTo(pkg2.getName())
|
||||
assertThat(it.single().isOverrideable).isEqualTo(false)
|
||||
}
|
||||
|
||||
// Domain 2 user selection gone for user 0
|
||||
assertThat(service.getOwnersForDomain(DOMAIN_2, 0)).isEmpty()
|
||||
|
||||
// Domain 2 user selection still around for user 1
|
||||
service.getOwnersForDomain(DOMAIN_2, 1).let {
|
||||
assertThat(it).hasSize(1)
|
||||
assertThat(it.single().packageName).isEqualTo(pkg1.getName())
|
||||
assertThat(it.single().isOverrideable).isEqualTo(true)
|
||||
}
|
||||
|
||||
// Now assert for user 1 that it was unaffected by the change to user 0
|
||||
service.getOwnersForDomain(DOMAIN_1, 1).let {
|
||||
assertThat(it).hasSize(2)
|
||||
assertThat(it[0].packageName).isEqualTo(pkg1.getName())
|
||||
assertThat(it[0].isOverrideable).isEqualTo(false)
|
||||
assertThat(it[1].packageName).isEqualTo(pkg2.getName())
|
||||
assertThat(it[1].isOverrideable).isEqualTo(false)
|
||||
}
|
||||
|
||||
service.setUserSelection(pkg1.domainSetId, setOf(DOMAIN_2), true, 0)
|
||||
|
||||
service.getOwnersForDomain(DOMAIN_2, 1).let {
|
||||
assertThat(it).hasSize(1)
|
||||
assertThat(it.single().packageName).isEqualTo(pkg1.getName())
|
||||
assertThat(it.single().isOverrideable).isEqualTo(true)
|
||||
}
|
||||
|
||||
// "Reinstall" the package to user 0
|
||||
pkg1User0Enabled.set(false)
|
||||
|
||||
// This state should have been cleared when the package was uninstalled
|
||||
assertThat(service.getOwnersForDomain(DOMAIN_2, 0)).isEmpty()
|
||||
|
||||
// Other package unaffected
|
||||
service.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_2), true, 0)
|
||||
service.getOwnersForDomain(DOMAIN_2, 0).let {
|
||||
assertThat(it).hasSize(1)
|
||||
assertThat(it.single().packageName).isEqualTo(pkg2.getName())
|
||||
assertThat(it.single().isOverrideable).isEqualTo(true)
|
||||
}
|
||||
assertThat(service.getOwnersForDomain(DOMAIN_2, 1)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -373,21 +446,23 @@ class DomainVerificationManagerApiTest {
|
||||
whenever(callingUid) { Process.ROOT_UID }
|
||||
whenever(callingUserId) { 0 }
|
||||
|
||||
whenever(getPackageSettingLocked(anyString())) {
|
||||
pkgSettingFunction(arguments[0] as String)
|
||||
}
|
||||
whenever(getPackageLocked(anyString())) {
|
||||
pkgSettingFunction(arguments[0] as String)?.getPkg()
|
||||
mockPackageSettings {
|
||||
pkgSettingFunction(it)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun mockPkgSetting(pkgName: String, domainSetId: UUID, domains: List<String> = listOf(
|
||||
DOMAIN_1, DOMAIN_2
|
||||
)) = mockThrowOnUnmocked<PackageSetting> {
|
||||
private fun mockPkgSetting(
|
||||
pkgName: String,
|
||||
domainSetId: UUID,
|
||||
domains: List<String> = listOf(DOMAIN_1, DOMAIN_2),
|
||||
pkgUserState0: PackageSetting.() -> PackageUserState = { PackageUserState() },
|
||||
pkgUserState1: PackageSetting.() -> PackageUserState = { PackageUserState() }
|
||||
) = mockThrowOnUnmocked<PackageSetting> {
|
||||
val pkg = mockThrowOnUnmocked<AndroidPackage> {
|
||||
whenever(packageName) { pkgName }
|
||||
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
|
||||
whenever(isEnabled) { true }
|
||||
|
||||
val activityList = listOf(
|
||||
ParsedActivity().apply {
|
||||
@@ -416,6 +491,8 @@ class DomainVerificationManagerApiTest {
|
||||
whenever(this.domainSetId) { domainSetId }
|
||||
whenever(getInstantApp(anyInt())) { false }
|
||||
whenever(firstInstallTime) { 0L }
|
||||
whenever(readUserState(0)) { pkgUserState0() }
|
||||
whenever(readUserState(1)) { pkgUserState1() }
|
||||
}
|
||||
|
||||
fun DomainVerificationService.addPackages(vararg pkgSettings: PackageSetting) =
|
||||
|
||||
@@ -18,8 +18,11 @@ package com.android.server.pm.test.verify.domain
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageUserState
|
||||
import android.content.pm.Signature
|
||||
import android.content.pm.parsing.component.ParsedActivity
|
||||
import android.content.pm.parsing.component.ParsedIntentInfo
|
||||
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED
|
||||
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_NO_RESPONSE
|
||||
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_SUCCESS
|
||||
import android.content.pm.verify.domain.DomainVerificationInfo.STATE_UNMODIFIABLE
|
||||
@@ -35,12 +38,12 @@ import android.util.ArraySet
|
||||
import android.util.Xml
|
||||
import com.android.server.pm.PackageSetting
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage
|
||||
import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
|
||||
import com.android.server.pm.verify.domain.DomainVerificationService
|
||||
import com.android.server.testutils.mockThrowOnUnmocked
|
||||
import com.android.server.testutils.whenever
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.ArgumentMatchers.anyLong
|
||||
@@ -54,6 +57,10 @@ class DomainVerificationPackageTest {
|
||||
private const val PKG_TWO = "com.test.two"
|
||||
private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
|
||||
private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
|
||||
private const val SIGNATURE_ONE = "AA"
|
||||
private const val DIGEST_ONE =
|
||||
"BCEEF655B5A034911F1C3718CE056531B45EF03B4C7B1F15629E867294011A7D"
|
||||
private const val SIGNATURE_TWO = "BB"
|
||||
|
||||
private val DOMAIN_BASE = DomainVerificationPackageTest::class.java.packageName
|
||||
private val DOMAIN_1 = "one.$DOMAIN_BASE"
|
||||
@@ -64,8 +71,8 @@ class DomainVerificationPackageTest {
|
||||
private const val USER_ID = 0
|
||||
}
|
||||
|
||||
private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE)
|
||||
private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO)
|
||||
private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE, SIGNATURE_ONE)
|
||||
private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO)
|
||||
|
||||
@Test
|
||||
fun addPackageFirstTime() {
|
||||
@@ -93,6 +100,104 @@ class DomainVerificationPackageTest {
|
||||
.containsExactly(pkg1.getName())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addPackageRestoredMatchingSignature() {
|
||||
// language=XML
|
||||
val xml = """
|
||||
<?xml?>
|
||||
<domain-verifications>
|
||||
<active>
|
||||
<package-state
|
||||
packageName="${pkg1.getName()}"
|
||||
id="${pkg1.domainSetId}"
|
||||
signature="$DIGEST_ONE"
|
||||
>
|
||||
<state>
|
||||
<domain name="$DOMAIN_1" state="1"/>
|
||||
</state>
|
||||
</package-state>
|
||||
</active>
|
||||
</domain-verifications>
|
||||
"""
|
||||
|
||||
val service = makeService(pkg1, pkg2)
|
||||
service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
|
||||
service.addPackage(pkg1)
|
||||
val info = service.getInfo(pkg1.getName())
|
||||
assertThat(info.packageName).isEqualTo(pkg1.getName())
|
||||
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
|
||||
assertThat(info.hostToStateMap).containsExactlyEntriesIn(
|
||||
mapOf(
|
||||
DOMAIN_1 to STATE_MODIFIABLE_VERIFIED,
|
||||
DOMAIN_2 to STATE_NO_RESPONSE,
|
||||
)
|
||||
)
|
||||
|
||||
val userState = service.getUserState(pkg1.getName())
|
||||
assertThat(userState.packageName).isEqualTo(pkg1.getName())
|
||||
assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
|
||||
assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
|
||||
assertThat(userState.user.identifier).isEqualTo(USER_ID)
|
||||
assertThat(userState.hostToStateMap).containsExactlyEntriesIn(
|
||||
mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_VERIFIED,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(service.queryValidVerificationPackageNames())
|
||||
.containsExactly(pkg1.getName())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addPackageRestoredMismatchSignature() {
|
||||
// language=XML
|
||||
val xml = """
|
||||
<?xml?>
|
||||
<domain-verifications>
|
||||
<active>
|
||||
<package-state
|
||||
packageName="${pkg1.getName()}"
|
||||
id="${pkg1.domainSetId}"
|
||||
signature="INVALID_SIGNATURE"
|
||||
>
|
||||
<state>
|
||||
<domain name="$DOMAIN_1" state="1"/>
|
||||
</state>
|
||||
</package-state>
|
||||
</active>
|
||||
</domain-verifications>
|
||||
"""
|
||||
|
||||
val service = makeService(pkg1, pkg2)
|
||||
service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
|
||||
service.addPackage(pkg1)
|
||||
val info = service.getInfo(pkg1.getName())
|
||||
assertThat(info.packageName).isEqualTo(pkg1.getName())
|
||||
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
|
||||
assertThat(info.hostToStateMap).containsExactlyEntriesIn(
|
||||
mapOf(
|
||||
DOMAIN_1 to STATE_NO_RESPONSE,
|
||||
DOMAIN_2 to STATE_NO_RESPONSE,
|
||||
)
|
||||
)
|
||||
|
||||
val userState = service.getUserState(pkg1.getName())
|
||||
assertThat(userState.packageName).isEqualTo(pkg1.getName())
|
||||
assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
|
||||
assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
|
||||
assertThat(userState.user.identifier).isEqualTo(USER_ID)
|
||||
assertThat(userState.hostToStateMap).containsExactlyEntriesIn(
|
||||
mapOf(
|
||||
DOMAIN_1 to DOMAIN_STATE_NONE,
|
||||
DOMAIN_2 to DOMAIN_STATE_NONE,
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(service.queryValidVerificationPackageNames())
|
||||
.containsExactly(pkg1.getName())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addPackageActive() {
|
||||
// language=XML
|
||||
@@ -151,10 +256,9 @@ class DomainVerificationPackageTest {
|
||||
@Test
|
||||
fun migratePackageDropDomain() {
|
||||
val pkgName = PKG_ONE
|
||||
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
|
||||
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4))
|
||||
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
|
||||
listOf(DOMAIN_1, DOMAIN_2))
|
||||
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE,
|
||||
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3, DOMAIN_4))
|
||||
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
|
||||
|
||||
// Test 4 domains:
|
||||
// 1 will be approved and preserved, 2 will be selected and preserved,
|
||||
@@ -213,8 +317,8 @@ class DomainVerificationPackageTest {
|
||||
@Test
|
||||
fun migratePackageDropAll() {
|
||||
val pkgName = PKG_ONE
|
||||
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, listOf(DOMAIN_1, DOMAIN_2))
|
||||
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, emptyList())
|
||||
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
|
||||
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, emptyList())
|
||||
|
||||
val map = mutableMapOf<String, PackageSetting>()
|
||||
val service = makeService { map[it] }
|
||||
@@ -255,10 +359,9 @@ class DomainVerificationPackageTest {
|
||||
@Test
|
||||
fun migratePackageAddDomain() {
|
||||
val pkgName = PKG_ONE
|
||||
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE,
|
||||
listOf(DOMAIN_1, DOMAIN_2))
|
||||
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO,
|
||||
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
|
||||
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, listOf(DOMAIN_1, DOMAIN_2))
|
||||
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO,
|
||||
listOf(DOMAIN_1, DOMAIN_2, DOMAIN_3))
|
||||
|
||||
// Test 3 domains:
|
||||
// 1 will be verified and preserved, 2 will be selected and preserved,
|
||||
@@ -307,8 +410,8 @@ class DomainVerificationPackageTest {
|
||||
@Test
|
||||
fun migratePackageAddAll() {
|
||||
val pkgName = PKG_ONE
|
||||
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, emptyList())
|
||||
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, listOf(DOMAIN_1, DOMAIN_2))
|
||||
val pkgBefore = mockPkgSetting(pkgName, UUID_ONE, SIGNATURE_ONE, emptyList())
|
||||
val pkgAfter = mockPkgSetting(pkgName, UUID_TWO, SIGNATURE_TWO, listOf(DOMAIN_1, DOMAIN_2))
|
||||
|
||||
val map = mutableMapOf<String, PackageSetting>()
|
||||
val service = makeService { map[it] }
|
||||
@@ -379,21 +482,22 @@ class DomainVerificationPackageTest {
|
||||
whenever(callingUid) { Process.ROOT_UID }
|
||||
whenever(callingUserId) { 0 }
|
||||
|
||||
whenever(getPackageSettingLocked(anyString())) {
|
||||
pkgSettingFunction(arguments[0] as String)!!
|
||||
}
|
||||
whenever(getPackageLocked(anyString())) {
|
||||
pkgSettingFunction(arguments[0] as String)!!.getPkg()
|
||||
mockPackageSettings {
|
||||
pkgSettingFunction(it)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun mockPkgSetting(pkgName: String, domainSetId: UUID, domains: List<String> = listOf(
|
||||
DOMAIN_1, DOMAIN_2
|
||||
)) = mockThrowOnUnmocked<PackageSetting> {
|
||||
private fun mockPkgSetting(
|
||||
pkgName: String,
|
||||
domainSetId: UUID,
|
||||
signature: String,
|
||||
domains: List<String> = listOf(DOMAIN_1, DOMAIN_2)
|
||||
) = mockThrowOnUnmocked<PackageSetting> {
|
||||
val pkg = mockThrowOnUnmocked<AndroidPackage> {
|
||||
whenever(packageName) { pkgName }
|
||||
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
|
||||
whenever(isEnabled) { true }
|
||||
|
||||
val activityList = listOf(
|
||||
ParsedActivity().apply {
|
||||
@@ -422,5 +526,7 @@ class DomainVerificationPackageTest {
|
||||
whenever(this.domainSetId) { domainSetId }
|
||||
whenever(getInstantApp(anyInt())) { false }
|
||||
whenever(firstInstallTime) { 0L }
|
||||
whenever(readUserState(USER_ID)) { PackageUserState() }
|
||||
whenever(signatures) { arrayOf(Signature(signature)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<DomainVerificationStateMap<DomainVerificationPkgState>,
|
||||
ArrayMap<String, DomainVerificationPkgState>,
|
||||
ArrayMap<String, DomainVerificationPkgState>> {
|
||||
val attached = DomainVerificationStateMap<DomainVerificationPkgState>().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<String, DomainVerificationPkgState>().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<String, DomainVerificationPkgState>().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"
|
||||
>
|
||||
<state>
|
||||
<domain name="example.com" state="${
|
||||
@@ -191,6 +250,7 @@ class DomainVerificationPersistenceTest {
|
||||
packageName="${stateOne.packageName}"
|
||||
id="${stateOne.id}"
|
||||
hasAutoVerifyDomains="true"
|
||||
signature="RESTORED"
|
||||
>
|
||||
<state/>
|
||||
<user-states>
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.util.ArraySet
|
||||
import android.util.SparseArray
|
||||
import com.android.server.pm.PackageSetting
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage
|
||||
import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
|
||||
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
|
||||
import com.android.server.pm.verify.domain.DomainVerificationService
|
||||
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
|
||||
@@ -192,6 +193,7 @@ class DomainVerificationSettingsMutationTest {
|
||||
fun mockPkg() = mockThrowOnUnmocked<AndroidPackage> {
|
||||
whenever(packageName) { TEST_PKG }
|
||||
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
|
||||
whenever(isEnabled) { true }
|
||||
whenever(activities) {
|
||||
listOf(
|
||||
ParsedActivity().apply {
|
||||
@@ -233,11 +235,8 @@ class DomainVerificationSettingsMutationTest {
|
||||
whenever(getName()) { TEST_PKG }
|
||||
whenever(getPkg()) { mockPkg() }
|
||||
whenever(domainSetId) { TEST_UUID }
|
||||
whenever(userState) {
|
||||
SparseArray<PackageUserState>().apply {
|
||||
this[0] = PackageUserState()
|
||||
}
|
||||
}
|
||||
whenever(readUserState(0)) { PackageUserState() }
|
||||
whenever(readUserState(10)) { PackageUserState() }
|
||||
whenever(getInstantApp(anyInt())) { false }
|
||||
}
|
||||
}
|
||||
@@ -258,8 +257,12 @@ class DomainVerificationSettingsMutationTest {
|
||||
mockThrowOnUnmocked {
|
||||
whenever(callingUid) { TEST_UID }
|
||||
whenever(callingUserId) { TEST_USER_ID }
|
||||
whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting() }
|
||||
whenever(getPackageLocked(TEST_PKG)) { mockPkg() }
|
||||
mockPackageSettings {
|
||||
when (it) {
|
||||
TEST_PKG -> mockPkgSetting()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
whenever(schedule(anyInt(), any()))
|
||||
whenever(scheduleWriteSettings())
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm.test.verify.domain
|
||||
|
||||
import com.android.internal.util.FunctionalUtils
|
||||
import com.android.server.pm.PackageSetting
|
||||
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
|
||||
import com.android.server.testutils.whenever
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import java.util.function.Consumer
|
||||
import java.util.function.Function
|
||||
|
||||
internal object DomainVerificationTestUtils {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun DomainVerificationManagerInternal.Connection.mockPackageSettings(
|
||||
block: (String) -> PackageSetting?
|
||||
) {
|
||||
whenever(withPackageSettings(any())) {
|
||||
(arguments[0] as Consumer<Function<String, PackageSetting?>>).accept { block(it) }
|
||||
}
|
||||
whenever(withPackageSettingsReturning<Any>(any())) {
|
||||
(arguments[0] as FunctionalUtils.ThrowingFunction<Function<String, PackageSetting?>, *>)
|
||||
.apply { block(it) }
|
||||
}
|
||||
whenever(withPackageSettingsThrowing<Exception>(any())) {
|
||||
(arguments[0] as DomainVerificationManagerInternal.Connection.ThrowingConsumer<
|
||||
Function<String, PackageSetting?>, *>)
|
||||
.accept { block(it) }
|
||||
}
|
||||
whenever(withPackageSettingsReturningThrowing<Any, Exception>(any())) {
|
||||
(arguments[0] as DomainVerificationManagerInternal.Connection.ThrowingFunction<
|
||||
Function<String, PackageSetting?>, *, *>)
|
||||
.apply { block(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.server.pm.test.verify.domain
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageUserState
|
||||
import android.content.pm.parsing.component.ParsedActivity
|
||||
import android.content.pm.parsing.component.ParsedIntentInfo
|
||||
import android.content.pm.verify.domain.DomainVerificationManager
|
||||
@@ -29,6 +30,7 @@ import android.os.Process
|
||||
import android.util.ArraySet
|
||||
import com.android.server.pm.PackageSetting
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage
|
||||
import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
|
||||
import com.android.server.pm.verify.domain.DomainVerificationService
|
||||
import com.android.server.testutils.mockThrowOnUnmocked
|
||||
import com.android.server.testutils.whenever
|
||||
@@ -82,10 +84,13 @@ class DomainVerificationUserStateOverrideTest {
|
||||
// Need to provide an internal UID so some permission checks are ignored
|
||||
whenever(callingUid) { Process.ROOT_UID }
|
||||
whenever(callingUserId) { 0 }
|
||||
whenever(getPackageSettingLocked(PKG_ONE)) { pkg1 }
|
||||
whenever(getPackageSettingLocked(PKG_TWO)) { pkg2 }
|
||||
whenever(getPackageLocked(PKG_ONE)) { pkg1.getPkg() }
|
||||
whenever(getPackageLocked(PKG_TWO)) { pkg2.getPkg() }
|
||||
mockPackageSettings {
|
||||
when (it) {
|
||||
PKG_ONE -> pkg1
|
||||
PKG_TWO -> pkg2
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
})
|
||||
addPackage(pkg1)
|
||||
addPackage(pkg2)
|
||||
@@ -100,6 +105,7 @@ class DomainVerificationUserStateOverrideTest {
|
||||
val pkg = mockThrowOnUnmocked<AndroidPackage> {
|
||||
whenever(packageName) { pkgName }
|
||||
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
|
||||
whenever(isEnabled) { true }
|
||||
|
||||
val activityList = listOf(
|
||||
ParsedActivity().apply {
|
||||
@@ -137,6 +143,8 @@ class DomainVerificationUserStateOverrideTest {
|
||||
whenever(this.domainSetId) { domainSetId }
|
||||
whenever(getInstantApp(anyInt())) { false }
|
||||
whenever(firstInstallTime) { 0L }
|
||||
whenever(readUserState(0)) { PackageUserState() }
|
||||
whenever(readUserState(1)) { PackageUserState() }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user