blob: f492932a0d77464cc73c6bb91611256922f5442b [file] [log] [blame]
/*
* Copyright (C) 2019 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;
import static android.content.pm.UserInfo.FLAG_FULL;
import static android.content.pm.UserInfo.FLAG_GUEST;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.content.pm.UserInfo.FLAG_SYSTEM;
import static com.android.server.pm.UserSystemPackageInstaller.PACKAGE_WHITELIST_MODE_PROP;
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT;
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE;
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE;
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST;
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_LOG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.UserInfo;
import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.support.test.uiautomator.UiDevice;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* Tests for UserSystemPackageInstaller.
*
* <p>Run with:<pre>
* atest com.android.server.pm.UserSystemPackageInstallerTest
* </pre>
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
public class UserSystemPackageInstallerTest {
private static final String TAG = "UserSystemPackageInstallerTest";
private UserSystemPackageInstaller mUserSystemPackageInstaller;
private Context mContext;
/** Any users created during this test, for them to be removed when it's done. */
private final List<Integer> mRemoveUsers = new ArrayList<>();
/** Original value of PACKAGE_WHITELIST_MODE_PROP before the test, to reset at end. */
private final int mOriginalWhitelistMode = SystemProperties.getInt(
PACKAGE_WHITELIST_MODE_PROP, USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT);
@Before
public void setup() {
// Currently UserManagerService cannot be instantiated twice inside a VM without a cleanup
// TODO: Remove once UMS supports proper dependency injection
if (Looper.myLooper() == null) {
Looper.prepare();
}
LocalServices.removeServiceForTest(UserManagerInternal.class);
UserManagerService ums = new UserManagerService(InstrumentationRegistry.getContext());
mUserSystemPackageInstaller = new UserSystemPackageInstaller(ums);
mContext = InstrumentationRegistry.getTargetContext();
}
@After
public void tearDown() {
UserManager um = UserManager.get(mContext);
for (int userId : mRemoveUsers) {
um.removeUser(userId);
}
setUserTypePackageWhitelistMode(mOriginalWhitelistMode);
}
/**
* Subclass of SystemConfig without running the constructor.
*/
private class SystemConfigTestClass extends SystemConfig {
SystemConfigTestClass(boolean readPermissions) {
super(readPermissions);
}
}
/**
* Test that determineWhitelistedPackagesForUserTypes reads SystemConfig information properly.
*/
@Test
public void testDetermineWhitelistedPackagesForUserTypes() {
SystemConfig sysConfig = new SystemConfigTestClass(false) {
@Override
public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeWhitelist() {
ArrayMap<String, Set<String>> r = new ArrayMap<>();
r.put("com.android.package1", new ArraySet<>(Arrays.asList(
"PROFILE", "SYSTEM", "GUEST", "FULL", "invalid-garbage1")));
r.put("com.android.package2", new ArraySet<>(Arrays.asList(
"MANAGED_PROFILE")));
return r;
}
@Override
public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeBlacklist() {
ArrayMap<String, Set<String>> r = new ArrayMap<>();
r.put("com.android.package1", new ArraySet<>(Arrays.asList(
"FULL", "RESTRICTED", "invalid-garbage2")));
return r;
}
};
final ArrayMap<String, Integer> expectedOutput = getNewPackageToWhitelistedFlagsMap();
expectedOutput.put("com.android.package1",
UserInfo.FLAG_PROFILE | FLAG_SYSTEM | FLAG_GUEST);
expectedOutput.put("com.android.package2",
UserInfo.FLAG_MANAGED_PROFILE);
final ArrayMap<String, Integer> actualOutput =
mUserSystemPackageInstaller.determineWhitelistedPackagesForUserTypes(sysConfig);
assertEquals("Incorrect package-to-user mapping.", expectedOutput, actualOutput);
}
/**
* Test that determineWhitelistedPackagesForUserTypes does not include packages that were never
* whitelisted properly, but does include packages that were whitelisted but then blacklisted.
*/
@Test
public void testDetermineWhitelistedPackagesForUserTypes_noNetWhitelisting() {
SystemConfig sysConfig = new SystemConfigTestClass(false) {
@Override
public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeWhitelist() {
ArrayMap<String, Set<String>> r = new ArrayMap<>();
r.put("com.android.package1", new ArraySet<>(Arrays.asList("invalid1")));
// com.android.package2 has no whitelisting
r.put("com.android.package3", new ArraySet<>(Arrays.asList("PROFILE", "FULL")));
r.put("com.android.package4", new ArraySet<>(Arrays.asList("PROFILE")));
r.put("com.android.package5", new ArraySet<>());
// com.android.package6 has no whitelisting
return r;
}
@Override
public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeBlacklist() {
ArrayMap<String, Set<String>> r = new ArrayMap<>();
// com.android.package1 has no blacklisting
r.put("com.android.package2", new ArraySet<>(Arrays.asList("FULL")));
r.put("com.android.package3", new ArraySet<>(Arrays.asList("PROFILE", "FULL")));
r.put("com.android.package4", new ArraySet<>(Arrays.asList("PROFILE", "invalid4")));
// com.android.package5 has no blacklisting
r.put("com.android.package6", new ArraySet<>(Arrays.asList("invalid6")));
return r;
}
};
final ArrayMap<String, Integer> expectedOutput = getNewPackageToWhitelistedFlagsMap();
expectedOutput.put("com.android.package3", 0);
expectedOutput.put("com.android.package4", 0);
final ArrayMap<String, Integer> actualOutput =
mUserSystemPackageInstaller.determineWhitelistedPackagesForUserTypes(sysConfig);
assertEquals("Incorrect package-to-user mapping.", expectedOutput, actualOutput);
}
/**
* Tests that shouldInstallPackage correctly determines which packages should be installed.
*/
@Test
public void testShouldInstallPackage() {
final String packageName1 = "pkg1"; // whitelisted
final String packageName2 = "pkg2"; // whitelisted and blacklisted
final String packageName3 = "pkg3"; // whitelisted for a different user type
final String packageName4 = "pkg4"; // not whitelisted nor blacklisted at all
final ArrayMap<String, Integer> pkgFlgMap = new ArrayMap<>(); // Whitelist: pkgs per flags
pkgFlgMap.put(packageName1, FLAG_FULL);
pkgFlgMap.put(packageName2, 0);
pkgFlgMap.put(packageName3, FLAG_MANAGED_PROFILE);
// Whitelist of pkgs for this specific user, i.e. subset of pkgFlagMap for this user.
final Set<String> userWhitelist = new ArraySet<>();
userWhitelist.add(packageName1);
final UserSystemPackageInstaller uspi = new UserSystemPackageInstaller(null, pkgFlgMap);
final PackageParser.Package pkg1 = new PackageParser.Package(packageName1);
final PackageParser.Package pkg2 = new PackageParser.Package(packageName2);
final PackageParser.Package pkg3 = new PackageParser.Package(packageName3);
final PackageParser.Package pkg4 = new PackageParser.Package(packageName4);
// No implicit whitelist, so only install pkg1.
boolean implicit = false;
boolean isSysUser = false;
assertTrue(uspi.shouldInstallPackage(pkg1, pkgFlgMap, userWhitelist, implicit, isSysUser));
assertFalse(uspi.shouldInstallPackage(pkg2, pkgFlgMap, userWhitelist, implicit, isSysUser));
assertFalse(uspi.shouldInstallPackage(pkg3, pkgFlgMap, userWhitelist, implicit, isSysUser));
assertFalse(uspi.shouldInstallPackage(pkg4, pkgFlgMap, userWhitelist, implicit, isSysUser));
// Use implicit whitelist, so install pkg1 and pkg4
implicit = true;
isSysUser = false;
assertTrue(uspi.shouldInstallPackage(pkg1, pkgFlgMap, userWhitelist, implicit, isSysUser));
assertFalse(uspi.shouldInstallPackage(pkg2, pkgFlgMap, userWhitelist, implicit, isSysUser));
assertFalse(uspi.shouldInstallPackage(pkg3, pkgFlgMap, userWhitelist, implicit, isSysUser));
assertTrue(uspi.shouldInstallPackage(pkg4, pkgFlgMap, userWhitelist, implicit, isSysUser));
// For user 0 specifically, we always implicitly whitelist.
implicit = false;
isSysUser = true;
assertTrue(uspi.shouldInstallPackage(pkg1, pkgFlgMap, userWhitelist, implicit, isSysUser));
assertFalse(uspi.shouldInstallPackage(pkg2, pkgFlgMap, userWhitelist, implicit, isSysUser));
assertFalse(uspi.shouldInstallPackage(pkg3, pkgFlgMap, userWhitelist, implicit, isSysUser));
assertTrue(uspi.shouldInstallPackage(pkg4, pkgFlgMap, userWhitelist, implicit, isSysUser));
}
/**
* Tests that getWhitelistedPackagesForUserType works properly, assuming that
* mWhitelistedPackagesForUserTypes (i.e. determineWhitelistedPackagesForUserTypes) is correct.
*/
@Test
public void testGetWhitelistedPackagesForUserType() {
final String packageName1 = "pkg1"; // whitelisted for FULL
final String packageName2 = "pkg2"; // blacklisted whenever whitelisted
final String packageName3 = "pkg3"; // whitelisted for SYSTEM
final String packageName4 = "pkg4"; // whitelisted for FULL
final ArrayMap<String, Integer> pkgFlagMap = new ArrayMap<>(); // Whitelist: pkgs per flags
pkgFlagMap.put(packageName1, FLAG_FULL);
pkgFlagMap.put(packageName2, 0);
pkgFlagMap.put(packageName3, FLAG_SYSTEM);
pkgFlagMap.put(packageName4, FLAG_FULL);
// Whitelist of pkgs for this specific user, i.e. subset of pkgFlagMap for this user.
final Set<String> expectedUserWhitelist = new ArraySet<>();
expectedUserWhitelist.add(packageName1);
UserSystemPackageInstaller uspi = new UserSystemPackageInstaller(null, pkgFlagMap);
Set<String> output = uspi.getWhitelistedPackagesForUserType(FLAG_FULL);
assertEquals("Whitelist for FULL is the wrong size", 2, output.size());
assertTrue("Whitelist for FULL doesn't contain pkg1", output.contains(packageName1));
assertTrue("Whitelist for FULL doesn't contain pkg4", output.contains(packageName4));
output = uspi.getWhitelistedPackagesForUserType(FLAG_SYSTEM);
assertEquals("Whitelist for SYSTEM is the wrong size", 1, output.size());
assertTrue("Whitelist for SYSTEM doesn't contain pkg1", output.contains(packageName3));
}
/**
* Test that a newly created FULL user has the expected system packages.
*
* Assumes that SystemConfig and UserManagerService.determineWhitelistedPackagesForUserTypes
* work correctly (they are tested separately).
*/
@Test
public void testPackagesForCreateUser_full() {
final int userFlags = UserInfo.FLAG_FULL;
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
PackageManager pm = mContext.getPackageManager();
final SystemConfig sysConfig = new SystemConfigTestClass(true);
final ArrayMap<String, Integer> packageMap =
mUserSystemPackageInstaller.determineWhitelistedPackagesForUserTypes(sysConfig);
final Set<String> expectedPackages = new ArraySet<>(packageMap.size());
for (int i = 0; i < packageMap.size(); i++) {
if ((userFlags & packageMap.valueAt(i)) != 0) {
expectedPackages.add(packageMap.keyAt(i));
}
}
final UserManager um = UserManager.get(mContext);
final UserInfo user = um.createUser("Test User", userFlags);
assertNotNull(user);
mRemoveUsers.add(user.id);
final List<PackageInfo> packageInfos = pm.getInstalledPackagesAsUser(
PackageManager.MATCH_SYSTEM_ONLY
| PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
user.id);
final Set<String> actualPackages = new ArraySet<>(packageInfos.size());
for (PackageInfo p : packageInfos) {
actualPackages.add(p.packageName);
}
checkPackageDifferences(expectedPackages, actualPackages);
}
/** Asserts that actual is a subset of expected. */
private void checkPackageDifferences(Set<String> expected, Set<String> actual) {
final Set<String> uniqueToExpected = new ArraySet<>(expected);
uniqueToExpected.removeAll(actual);
final Set<String> uniqueToActual = new ArraySet<>(actual);
uniqueToActual.removeAll(expected);
Log.v(TAG, "Expected list uniquely has " + uniqueToExpected);
Log.v(TAG, "Actual list uniquely has " + uniqueToActual);
assertTrue("User's system packages includes non-whitelisted packages: " + uniqueToActual,
uniqueToActual.isEmpty());
}
/**
* Test that setEnableUserTypePackageWhitelist() has the correct effect.
*/
@Test
public void testSetWhitelistEnabledMode() {
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_LOG);
assertTrue(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertTrue(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode());
setUserTypePackageWhitelistMode(
USER_TYPE_PACKAGE_WHITELIST_MODE_LOG | USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
assertTrue(mUserSystemPackageInstaller.isLogMode());
assertTrue(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST
| USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertTrue(mUserSystemPackageInstaller.isEnforceMode());
assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode());
}
/** Sets the whitelist mode to the desired value via adb's setprop. */
private void setUserTypePackageWhitelistMode(int mode) {
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
try {
String result = uiDevice.executeShellCommand(String.format("setprop %s %d",
PACKAGE_WHITELIST_MODE_PROP, mode));
assertFalse("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ": " + result,
result != null && result.contains("Failed"));
} catch (IOException e) {
fail("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ":\n" + e);
}
}
private ArrayMap<String, Integer> getNewPackageToWhitelistedFlagsMap() {
final ArrayMap<String, Integer> pkgFlagMap = new ArrayMap<>();
// "android" is always treated as whitelisted, regardless of the xml file.
pkgFlagMap.put("android", FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.FLAG_PROFILE);
return pkgFlagMap;
}
}