Check installed and enabled state for package domain approval

If the package isn't installed or isn't enabled for a user, it cannot
be approved for that user.

Also hooks into package uninstall for a single user to remove the
domain state for that package for that user.

Bug: 183226822

Test: atest DomainVerificationManagerApiTest#getOwnersForDomain
Test: atest com.android.server.pm.test.verify.domain

Change-Id: I04942e1491d470fdd41e99f207bbf85baae87d4c
This commit is contained in:
Winson
2021-03-19 13:28:31 -07:00
parent efdf07b668
commit 5f5d4f6071
10 changed files with 187 additions and 29 deletions

View File

@@ -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);

View File

@@ -21759,6 +21759,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) {

View File

@@ -227,6 +227,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.

View File

@@ -29,6 +29,7 @@ import android.content.Intent;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageUserState;
import android.content.pm.ResolveInfo;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.verify.domain.DomainOwner;
@@ -1042,6 +1043,21 @@ public class DomainVerificationService extends SystemService
public void clearPackage(@NonNull String packageName) {
synchronized (mLock) {
mAttachedPkgStates.remove(packageName);
mSettings.removePackage(packageName);
}
mConnection.scheduleWriteSettings();
}
@Override
public void clearPackageForUser(@NonNull String packageName, @UserIdInt int userId) {
synchronized (mLock) {
final DomainVerificationPkgState pkgState = mAttachedPkgStates.get(packageName);
if (pkgState != null) {
pkgState.removeUser(userId);
}
mSettings.removePackageForUser(packageName, userId);
}
mConnection.scheduleWriteSettings();
@@ -1544,6 +1560,22 @@ public class DomainVerificationService extends SystemService
String packageName = pkgSetting.getName();
final AndroidPackage pkg = pkgSetting.getPkg();
final PackageUserState pkgUserState = pkgSetting.readUserState(userId);
if (pkgUserState == null) {
if (DEBUG_APPROVAL) {
debugApproval(packageName, debugObject, userId, false,
"PackageUserState unavailable");
}
return APPROVAL_LEVEL_NONE;
}
if (!pkgUserState.installed || !pkgUserState.isPackageEnabled(pkg)) {
if (DEBUG_APPROVAL) {
debugApproval(packageName, debugObject, userId, false, "package not enabled");
}
return APPROVAL_LEVEL_NONE;
}
// Should never be null, but if it is, skip this and assume that v2 is enabled
if (pkg != null && !DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg,
SETTINGS_API_V2)) {

View File

@@ -241,17 +241,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);
}
}
}

View File

@@ -290,6 +290,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 +331,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 }
}
}

View File

@@ -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
@@ -41,6 +42,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 +269,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 +287,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 +317,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
@@ -382,12 +454,17 @@ class DomainVerificationManagerApiTest {
})
}
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 +493,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) =

View File

@@ -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.DomainVerificationInfo.STATE_NO_RESPONSE
@@ -394,6 +395,7 @@ class DomainVerificationPackageTest {
val pkg = mockThrowOnUnmocked<AndroidPackage> {
whenever(packageName) { pkgName }
whenever(targetSdkVersion) { Build.VERSION_CODES.S }
whenever(isEnabled) { true }
val activityList = listOf(
ParsedActivity().apply {
@@ -422,5 +424,6 @@ class DomainVerificationPackageTest {
whenever(this.domainSetId) { domainSetId }
whenever(getInstantApp(anyInt())) { false }
whenever(firstInstallTime) { 0L }
whenever(readUserState(USER_ID)) { PackageUserState() }
}
}

View File

@@ -192,6 +192,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 +234,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 }
}
}

View File

@@ -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
@@ -100,6 +101,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 +139,8 @@ class DomainVerificationUserStateOverrideTest {
whenever(this.domainSetId) { domainSetId }
whenever(getInstantApp(anyInt())) { false }
whenever(firstInstallTime) { 0L }
whenever(readUserState(0)) { PackageUserState() }
whenever(readUserState(1)) { PackageUserState() }
}
@Test