Merge "Whitelist packages for user types"
This commit is contained in:
committed by
Android (Google) Code Review
commit
be0f335f5a
@@ -22,6 +22,7 @@
|
||||
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
|
||||
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
|
||||
@@ -38,8 +38,10 @@ import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.IProgressListener;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.perftests.utils.ShellHelper;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
@@ -85,6 +87,14 @@ public class UserLifecycleTests {
|
||||
|
||||
private static final String DUMMY_PACKAGE_NAME = "perftests.multiuser.apps.dummyapp";
|
||||
|
||||
// Copy of UserSystemPackageInstaller whitelist mode constants.
|
||||
private static final String PACKAGE_WHITELIST_MODE_PROP =
|
||||
"persist.debug.user.package_whitelist_mode";
|
||||
private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0;
|
||||
private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b001;
|
||||
private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b100;
|
||||
private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1;
|
||||
|
||||
private UserManager mUm;
|
||||
private ActivityManager mAm;
|
||||
private IActivityManager mIam;
|
||||
@@ -442,6 +452,55 @@ public class UserLifecycleTests {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This is just a POC. Do this properly and add more.
|
||||
/** Tests starting (unlocking) a newly-created profile using the user-type-pkg-whitelist. */
|
||||
@Test
|
||||
public void managedProfileUnlock_usingWhitelist() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
final int origMode = getUserTypePackageWhitelistMode();
|
||||
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE
|
||||
| USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST);
|
||||
|
||||
try {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createManagedProfile();
|
||||
mRunner.resumeTiming();
|
||||
|
||||
startUserInBackground(userId);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
} finally {
|
||||
setUserTypePackageWhitelistMode(origMode);
|
||||
}
|
||||
}
|
||||
/** Tests starting (unlocking) a newly-created profile NOT using the user-type-pkg-whitelist. */
|
||||
@Test
|
||||
public void managedProfileUnlock_notUsingWhitelist() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
final int origMode = getUserTypePackageWhitelistMode();
|
||||
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE);
|
||||
|
||||
try {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createManagedProfile();
|
||||
mRunner.resumeTiming();
|
||||
|
||||
startUserInBackground(userId);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
} finally {
|
||||
setUserTypePackageWhitelistMode(origMode);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a new user, returning its userId. */
|
||||
private int createUserNoFlags() {
|
||||
return createUserWithFlags(/* flags= */ 0);
|
||||
@@ -458,6 +517,10 @@ public class UserLifecycleTests {
|
||||
private int createManagedProfile() {
|
||||
final UserInfo userInfo = mUm.createProfileForUser("TestProfile",
|
||||
UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
|
||||
if (userInfo == null) {
|
||||
throw new IllegalStateException("Creating managed profile failed. Most likely there is "
|
||||
+ "already a pre-existing profile on the device.");
|
||||
}
|
||||
mUsersToRemove.add(userInfo.id);
|
||||
return userInfo.id;
|
||||
}
|
||||
@@ -627,6 +690,20 @@ public class UserLifecycleTests {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the PACKAGE_WHITELIST_MODE_PROP System Property. */
|
||||
private int getUserTypePackageWhitelistMode() {
|
||||
return SystemProperties.getInt(PACKAGE_WHITELIST_MODE_PROP,
|
||||
USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT);
|
||||
}
|
||||
|
||||
/** Sets the PACKAGE_WHITELIST_MODE_PROP System Property to the given value. */
|
||||
private void setUserTypePackageWhitelistMode(int mode) {
|
||||
String result = ShellHelper.runShellCommand(
|
||||
String.format("setprop %s %d", PACKAGE_WHITELIST_MODE_PROP, mode));
|
||||
attestFalse("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ": " + result,
|
||||
result != null && result.contains("Failed"));
|
||||
}
|
||||
|
||||
private void removeUser(int userId) {
|
||||
try {
|
||||
mUm.removeUser(userId);
|
||||
|
||||
@@ -307,6 +307,17 @@ public abstract class PackageManagerInternal {
|
||||
*/
|
||||
public abstract String getNameForUid(int uid);
|
||||
|
||||
/**
|
||||
* Marks a package as installed (or not installed) for a given user.
|
||||
*
|
||||
* @param pkg the package whose installation is to be set
|
||||
* @param userId the user for whom to set it
|
||||
* @param installed the new installed state
|
||||
* @return true if the installed state changed as a result
|
||||
*/
|
||||
public abstract boolean setInstalled(PackageParser.Package pkg,
|
||||
@UserIdInt int userId, boolean installed);
|
||||
|
||||
/**
|
||||
* Request to perform the second phase of ephemeral resolution.
|
||||
* @param responseObj The response of the first phase of ephemeral resolution
|
||||
|
||||
@@ -125,6 +125,13 @@ public class UserInfo implements Parcelable {
|
||||
*/
|
||||
public static final int FLAG_SYSTEM = 0x00000800;
|
||||
|
||||
/**
|
||||
* Indicates that this user is some sort of profile. Right now, the only profile type is
|
||||
* {@link #FLAG_MANAGED_PROFILE}, but this can include other types of profiles too if any
|
||||
* are created in the future. This is therefore not a flag, but an OR of several flags.
|
||||
*/
|
||||
public static final int PROFILE_FLAGS_MASK = FLAG_MANAGED_PROFILE;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
@@ -50,6 +51,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Loads global system configuration info.
|
||||
@@ -209,6 +211,10 @@ public class SystemConfig {
|
||||
|
||||
private final ArraySet<String> mBugreportWhitelistedPackages = new ArraySet<>();
|
||||
|
||||
// Map of packagesNames to userTypes. Stored temporarily until cleared by UserManagerService().
|
||||
private ArrayMap<String, Set<String>> mPackageToUserTypeWhitelist = new ArrayMap<>();
|
||||
private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>();
|
||||
|
||||
public static SystemConfig getInstance() {
|
||||
if (!isSystemProcess()) {
|
||||
Slog.wtf(TAG, "SystemConfig is being accessed by a process other than "
|
||||
@@ -359,7 +365,48 @@ public class SystemConfig {
|
||||
return mBugreportWhitelistedPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets map of packagesNames to userTypes, dictating on which user types each package should be
|
||||
* initially installed, and then removes this map from SystemConfig.
|
||||
* Called by UserManagerService when it is constructed.
|
||||
*/
|
||||
public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeWhitelist() {
|
||||
ArrayMap<String, Set<String>> r = mPackageToUserTypeWhitelist;
|
||||
mPackageToUserTypeWhitelist = new ArrayMap<>(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets map of packagesNames to userTypes, dictating on which user types each package should NOT
|
||||
* be initially installed, even if they are whitelisted, and then removes this map from
|
||||
* SystemConfig.
|
||||
* Called by UserManagerService when it is constructed.
|
||||
*/
|
||||
public ArrayMap<String, Set<String>> getAndClearPackageToUserTypeBlacklist() {
|
||||
ArrayMap<String, Set<String>> r = mPackageToUserTypeBlacklist;
|
||||
mPackageToUserTypeBlacklist = new ArrayMap<>(0);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only use for testing. Do NOT use in production code.
|
||||
* @param readPermissions false to create an empty SystemConfig; true to read the permissions.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected SystemConfig(boolean readPermissions) {
|
||||
if (readPermissions) {
|
||||
Slog.w(TAG, "Constructing a test SystemConfig");
|
||||
readAllPermissions();
|
||||
} else {
|
||||
Slog.w(TAG, "Constructing an empty test SystemConfig");
|
||||
}
|
||||
}
|
||||
|
||||
SystemConfig() {
|
||||
readAllPermissions();
|
||||
}
|
||||
|
||||
private void readAllPermissions() {
|
||||
// Read configuration from system
|
||||
readPermissions(Environment.buildPath(
|
||||
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
|
||||
@@ -419,7 +466,8 @@ public class SystemConfig {
|
||||
Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL);
|
||||
}
|
||||
|
||||
void readPermissions(File libraryDir, int permissionFlag) {
|
||||
@VisibleForTesting
|
||||
public void readPermissions(File libraryDir, int permissionFlag) {
|
||||
// Read permissions from given directory.
|
||||
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
|
||||
if (permissionFlag == ALLOW_ALL) {
|
||||
@@ -954,6 +1002,11 @@ public class SystemConfig {
|
||||
}
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
} break;
|
||||
case "install-in-user-type": {
|
||||
// NB: We allow any directory permission to declare install-in-user-type.
|
||||
readInstallInUserType(parser,
|
||||
mPackageToUserTypeWhitelist, mPackageToUserTypeBlacklist);
|
||||
} break;
|
||||
default: {
|
||||
Slog.w(TAG, "Tag " + name + " is unknown in "
|
||||
+ permFile + " at " + parser.getPositionDescription());
|
||||
@@ -1091,6 +1144,53 @@ public class SystemConfig {
|
||||
}
|
||||
}
|
||||
|
||||
private void readInstallInUserType(XmlPullParser parser,
|
||||
Map<String, Set<String>> doInstallMap,
|
||||
Map<String, Set<String>> nonInstallMap)
|
||||
throws IOException, XmlPullParserException {
|
||||
final String packageName = parser.getAttributeValue(null, "package");
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
Slog.w(TAG, "package is required for <install-in-user-type> in "
|
||||
+ parser.getPositionDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> userTypesYes = doInstallMap.get(packageName);
|
||||
Set<String> userTypesNo = nonInstallMap.get(packageName);
|
||||
final int depth = parser.getDepth();
|
||||
while (XmlUtils.nextElementWithin(parser, depth)) {
|
||||
final String name = parser.getName();
|
||||
if ("install-in".equals(name)) {
|
||||
final String userType = parser.getAttributeValue(null, "user-type");
|
||||
if (TextUtils.isEmpty(userType)) {
|
||||
Slog.w(TAG, "user-type is required for <install-in-user-type> in "
|
||||
+ parser.getPositionDescription());
|
||||
continue;
|
||||
}
|
||||
if (userTypesYes == null) {
|
||||
userTypesYes = new ArraySet<>();
|
||||
doInstallMap.put(packageName, userTypesYes);
|
||||
}
|
||||
userTypesYes.add(userType);
|
||||
} else if ("do-not-install-in".equals(name)) {
|
||||
final String userType = parser.getAttributeValue(null, "user-type");
|
||||
if (TextUtils.isEmpty(userType)) {
|
||||
Slog.w(TAG, "user-type is required for <install-in-user-type> in "
|
||||
+ parser.getPositionDescription());
|
||||
continue;
|
||||
}
|
||||
if (userTypesNo == null) {
|
||||
userTypesNo = new ArraySet<>();
|
||||
nonInstallMap.put(packageName, userTypesNo);
|
||||
}
|
||||
userTypesNo.add(userType);
|
||||
} else {
|
||||
Slog.w(TAG, "unrecognized tag in <install-in-user-type> in "
|
||||
+ parser.getPositionDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void readOemPermissions(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
final String packageName = parser.getAttributeValue(null, "package");
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
|
||||
@@ -2527,6 +2527,16 @@
|
||||
will be locked. -->
|
||||
<bool name="config_multiuserDelayUserDataLocking">false</bool>
|
||||
|
||||
<!-- Whether to only install system packages on a user if they're whitelisted for that user
|
||||
type. These are flags and can be freely combined.
|
||||
0 (0b000) - disable whitelist (install all system packages; no logging)
|
||||
1 (0b001) - enforce (only install system packages if they are whitelisted)
|
||||
2 (0b010) - log (log when a non-whitelisted package is run)
|
||||
4 (0b100) - treat any package not mentioned in the whitelist file as implicitly whitelisted
|
||||
Note: This list must be kept current with PACKAGE_WHITELIST_MODE_PROP in
|
||||
frameworks/base/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java -->
|
||||
<integer name="config_userTypePackageWhitelistMode">5</integer> <!-- 0b101 -->
|
||||
|
||||
<!-- Whether UI for multi user should be shown -->
|
||||
<bool name="config_enableMultiUserUI">false</bool>
|
||||
|
||||
|
||||
@@ -511,6 +511,7 @@
|
||||
<java-symbol type="integer" name="config_multiuserMaximumUsers" />
|
||||
<java-symbol type="integer" name="config_multiuserMaxRunningUsers" />
|
||||
<java-symbol type="bool" name="config_multiuserDelayUserDataLocking" />
|
||||
<java-symbol type="integer" name="config_userTypePackageWhitelistMode"/>
|
||||
<java-symbol type="integer" name="config_safe_media_volume_index" />
|
||||
<java-symbol type="integer" name="config_safe_media_volume_usb_mB" />
|
||||
<java-symbol type="integer" name="config_mobile_mtu" />
|
||||
|
||||
@@ -21,6 +21,12 @@ prebuilt_etc {
|
||||
src: "framework-sysconfig.xml",
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "preinstalled-packages-platform.xml",
|
||||
sub_dir: "sysconfig",
|
||||
src: "preinstalled-packages-platform.xml",
|
||||
}
|
||||
|
||||
prebuilt_etc {
|
||||
name: "hiddenapi-package-whitelist.xml",
|
||||
sub_dir: "sysconfig",
|
||||
|
||||
90
data/etc/preinstalled-packages-platform.xml
Normal file
90
data/etc/preinstalled-packages-platform.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
|
||||
<!--
|
||||
This XML file declares which system packages should be initially installed for new users based on
|
||||
the type of user. All system packages on the device should ideally have an entry in an xml file
|
||||
(keys by its manifest name).
|
||||
|
||||
Main user-types (every user will be at least one of these types) are:
|
||||
SYSTEM (user 0)
|
||||
FULL (any non-profile human user)
|
||||
PROFILE (profile human user)
|
||||
|
||||
Additional optional types are: GUEST, RESTRICTED, MANAGED_PROFILE, EPHEMERAL, DEMO
|
||||
|
||||
The meaning of each of these user types is delineated by flags in
|
||||
frameworks/base/core/java/android/content/pm/UserInfo.java.
|
||||
See frameworks/base/services/core/java/com/android/server/pm/UserSystemPackageInstaller#getFlagsFromUserTypes
|
||||
|
||||
The following three examples should cover most normal cases:
|
||||
|
||||
1. For a system package to be pre-installed only in user 0:
|
||||
|
||||
<install-in-user-type package="com.android.example">
|
||||
<install-in user-type="SYSTEM">
|
||||
</install-in-user-type>
|
||||
|
||||
|
||||
2. For a system package to be pre-installed on all human users (e.g. a web browser), i.e. to be
|
||||
installed on any user of type type FULL or PROFILE (since this covers all human users):
|
||||
|
||||
<install-in-user-type package="com.android.example">
|
||||
<install-in user-type="FULL">
|
||||
<install-in user-type="PROFILE">
|
||||
</install-in-user-type>
|
||||
|
||||
|
||||
3. For a system package to be pre-installed on all human users except for profile users (e.g. a
|
||||
wallpaper app, since profiles cannot display wallpaper):
|
||||
|
||||
<install-in-user-type package="com.android.example">
|
||||
<install-in user-type="FULL">
|
||||
</install-in-user-type>
|
||||
|
||||
|
||||
Some system packages truly are required to be on all users, regardless of type, in which case use:
|
||||
<install-in-user-type package="com.android.example">
|
||||
<install-in user-type="SYSTEM">
|
||||
<install-in user-type="FULL">
|
||||
<install-in user-type="PROFILE">
|
||||
</install-in-user-type>
|
||||
|
||||
More fine-grained options are also available (see below). Additionally, packages can blacklist
|
||||
user types. Blacklists override any whitelisting (in any file).
|
||||
E.g.
|
||||
<install-in-user-type package="com.android.example">
|
||||
<install-in user-type="FULL" />
|
||||
<do-not-install-in user-type="GUEST" />
|
||||
</install-in-user-type>
|
||||
|
||||
If a user is of type FULL and GUEST, this package will NOT be installed, because the
|
||||
'do-not-install-in' takes precedence over 'install-in'.
|
||||
|
||||
The way that a device treats system packages that do not have any entry (for any user type) at all
|
||||
is determined by the config resource value config_userTypePackageWhitelistMode.
|
||||
See frameworks/base/core/res/res/values/config.xml#config_userTypePackageWhitelistMode.
|
||||
|
||||
Changes to the whitelist during system updates can result in installing new system packages
|
||||
to pre-existing users, but cannot uninstall system packages from pre-existing users.
|
||||
-->
|
||||
<config>
|
||||
<install-in-user-type package="com.android.providers.settings">
|
||||
<install-in user-type="SYSTEM" />
|
||||
<install-in user-type="FULL" />
|
||||
<install-in user-type="PROFILE" />
|
||||
</install-in-user-type>
|
||||
</config>
|
||||
@@ -137,7 +137,6 @@ import android.content.IntentSender;
|
||||
import android.content.IntentSender.SendIntentException;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.AppsQueryHelper;
|
||||
import android.content.pm.AuxiliaryResolveInfo;
|
||||
import android.content.pm.ChangedPackages;
|
||||
import android.content.pm.ComponentInfo;
|
||||
@@ -2473,54 +2472,21 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore);
|
||||
t.traceEnd(); // "create package manager"
|
||||
|
||||
m.enableSystemUserPackages();
|
||||
m.installWhitelistedSystemPackages();
|
||||
ServiceManager.addService("package", m);
|
||||
final PackageManagerNative pmn = m.new PackageManagerNative();
|
||||
ServiceManager.addService("package_native", pmn);
|
||||
return m;
|
||||
}
|
||||
|
||||
private void enableSystemUserPackages() {
|
||||
if (!UserManager.isSplitSystemUser()) {
|
||||
return;
|
||||
}
|
||||
// For system user, enable apps based on the following conditions:
|
||||
// - app is whitelisted or belong to one of these groups:
|
||||
// -- system app which has no launcher icons
|
||||
// -- system app which has INTERACT_ACROSS_USERS permission
|
||||
// -- system IME app
|
||||
// - app is not in the blacklist
|
||||
AppsQueryHelper queryHelper = new AppsQueryHelper(this);
|
||||
Set<String> enableApps = new ArraySet<>();
|
||||
enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
|
||||
| AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM
|
||||
| AppsQueryHelper.GET_IMES, /* systemAppsOnly */ true, UserHandle.SYSTEM));
|
||||
ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
|
||||
enableApps.addAll(wlApps);
|
||||
enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_REQUIRED_FOR_SYSTEM_USER,
|
||||
/* systemAppsOnly */ false, UserHandle.SYSTEM));
|
||||
ArraySet<String> blApps = SystemConfig.getInstance().getSystemUserBlacklistedApps();
|
||||
enableApps.removeAll(blApps);
|
||||
Log.i(TAG, "Applications installed for system user: " + enableApps);
|
||||
List<String> allAps = queryHelper.queryApps(0, /* systemAppsOnly */ false,
|
||||
UserHandle.SYSTEM);
|
||||
final int allAppsSize = allAps.size();
|
||||
/** Install/uninstall system packages for all users based on their user-type, as applicable. */
|
||||
private void installWhitelistedSystemPackages() {
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < allAppsSize; i++) {
|
||||
String pName = allAps.get(i);
|
||||
PackageSetting pkgSetting = mSettings.mPackages.get(pName);
|
||||
// Should not happen, but we shouldn't be failing if it does
|
||||
if (pkgSetting == null) {
|
||||
continue;
|
||||
}
|
||||
boolean install = enableApps.contains(pName);
|
||||
if (pkgSetting.getInstalled(UserHandle.USER_SYSTEM) != install) {
|
||||
Log.i(TAG, (install ? "Installing " : "Uninstalling ") + pName
|
||||
+ " for system user");
|
||||
pkgSetting.setInstalled(install, UserHandle.USER_SYSTEM);
|
||||
}
|
||||
final boolean scheduleWrite = mUserManager.installWhitelistedSystemPackages(
|
||||
isFirstBoot(), isDeviceUpgrading());
|
||||
if (scheduleWrite) {
|
||||
scheduleWritePackageRestrictionsLocked(UserHandle.USER_ALL);
|
||||
}
|
||||
scheduleWritePackageRestrictionsLocked(UserHandle.USER_SYSTEM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22318,10 +22284,19 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/** Called by UserManagerService */
|
||||
void createNewUser(int userId, String[] disallowedPackages) {
|
||||
/**
|
||||
* Called by UserManagerService.
|
||||
*
|
||||
* @param installablePackages system packages that should be initially installed for this user,
|
||||
* or {@code null} if all system packages should be installed
|
||||
* @param disallowedPackages packages that should not be initially installed. Takes precedence
|
||||
* over installablePackages.
|
||||
*/
|
||||
void createNewUser(int userId, @Nullable Set<String> installablePackages,
|
||||
String[] disallowedPackages) {
|
||||
synchronized (mInstallLock) {
|
||||
mSettings.createNewUserLI(this, mInstaller, userId, disallowedPackages);
|
||||
mSettings.createNewUserLI(this, mInstaller, userId,
|
||||
installablePackages, disallowedPackages);
|
||||
}
|
||||
synchronized (mLock) {
|
||||
scheduleWritePackageRestrictionsLocked(userId);
|
||||
@@ -23129,6 +23104,19 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
return PackageManagerService.this.getNameForUid(uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setInstalled(PackageParser.Package pkg, @UserIdInt int userId,
|
||||
boolean installed) {
|
||||
synchronized (mLock) {
|
||||
final PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
|
||||
if (ps.getInstalled(userId) != installed) {
|
||||
ps.setInstalled(installed, userId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
|
||||
Intent origIntent, String resolvedType, String callingPackage,
|
||||
|
||||
@@ -4014,8 +4014,9 @@ public final class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
void createNewUserLI(@NonNull PackageManagerService service,
|
||||
@NonNull Installer installer, int userHandle, String[] disallowedPackages) {
|
||||
void createNewUserLI(@NonNull PackageManagerService service, @NonNull Installer installer,
|
||||
@UserIdInt int userHandle, @Nullable Set<String> installablePackages,
|
||||
String[] disallowedPackages) {
|
||||
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
|
||||
Trace.TRACE_TAG_PACKAGE_MANAGER);
|
||||
t.traceBegin("createNewUser-" + userHandle);
|
||||
@@ -4025,6 +4026,7 @@ public final class Settings {
|
||||
String[] seinfos;
|
||||
int[] targetSdkVersions;
|
||||
int packagesCount;
|
||||
final boolean skipPackageWhitelist = installablePackages == null;
|
||||
synchronized (mLock) {
|
||||
Collection<PackageSetting> packages = mPackages.values();
|
||||
packagesCount = packages.size();
|
||||
@@ -4040,6 +4042,7 @@ public final class Settings {
|
||||
continue;
|
||||
}
|
||||
final boolean shouldInstall = ps.isSystem() &&
|
||||
(skipPackageWhitelist || installablePackages.contains(ps.name)) &&
|
||||
!ArrayUtils.contains(disallowedPackages, ps.name) &&
|
||||
!ps.pkg.applicationInfo.hiddenUntilInstalled;
|
||||
// Only system apps are initially installed.
|
||||
|
||||
@@ -250,6 +250,9 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
|
||||
private static final IBinder mUserRestriconToken = new Binder();
|
||||
|
||||
/** Installs system packages based on user-type. */
|
||||
private final UserSystemPackageInstaller mSystemPackageInstaller;
|
||||
|
||||
/**
|
||||
* Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
|
||||
*/
|
||||
@@ -550,6 +553,7 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
readUserListLP();
|
||||
sInstance = this;
|
||||
}
|
||||
mSystemPackageInstaller = new UserSystemPackageInstaller(this);
|
||||
mLocalService = new LocalService();
|
||||
LocalServices.addService(UserManagerInternal.class, mLocalService);
|
||||
mLockPatternUtils = new LockPatternUtils(mContext);
|
||||
@@ -2842,8 +2846,10 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
|
||||
t.traceEnd();
|
||||
|
||||
final Set<String> installablePackages =
|
||||
mSystemPackageInstaller.getInstallablePackagesForUserType(flags);
|
||||
t.traceBegin("PM.createNewUser");
|
||||
mPm.createNewUser(userId, disallowedPackages);
|
||||
mPm.createNewUser(userId, installablePackages, disallowedPackages);
|
||||
t.traceEnd();
|
||||
|
||||
userInfo.partial = false;
|
||||
@@ -2877,6 +2883,11 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
/** Install/uninstall system packages for all users based on their user-type, as applicable. */
|
||||
boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) {
|
||||
return mSystemPackageInstaller.installWhitelistedSystemPackages(isFirstBoot, isUpgrade);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
UserData putUserInfo(UserInfo userInfo) {
|
||||
final UserData userData = new UserData();
|
||||
@@ -3863,6 +3874,10 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
|
||||
pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
|
||||
pw.println(" User version: " + mUserVersion);
|
||||
|
||||
// Dump package whitelist
|
||||
pw.println();
|
||||
mSystemPackageInstaller.dump(pw);
|
||||
}
|
||||
|
||||
private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
|
||||
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* 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 android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemConfig;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Responsible for un/installing system packages based on user type.
|
||||
*
|
||||
* <p>Uses the SystemConfig's install-in-user-type whitelist;
|
||||
* see {@link SystemConfig#getAndClearPackageToUserTypeWhitelist} and
|
||||
* {@link SystemConfig#getAndClearPackageToUserTypeBlacklist}.
|
||||
*
|
||||
* <p>If {@link #isEnforceMode()} is false, then all system packages are always installed for all
|
||||
* users. The following applies when it is true.
|
||||
*
|
||||
* Any package can be in one of three states in the SystemConfig whitelist
|
||||
* <ol>
|
||||
* <li>Explicitly blacklisted for a particular user type</li>
|
||||
* <li>Explicitly whitelisted for a particular user type</li>
|
||||
* <li>Not mentioned at all, for any user type (neither whitelisted nor blacklisted)</li>
|
||||
* </ol>
|
||||
* Blacklisting always takes precedence - if a package is blacklisted for a particular user,
|
||||
* it won't be installed on that type of user (even if it is also whitelisted for that user).
|
||||
* Next comes whitelisting - if it is whitelisted for a particular user, it will be installed on
|
||||
* that type of user (as long as it isn't blacklisted).
|
||||
* Finally, if the package is not mentioned at all (i.e. neither whitelisted nor blacklisted for
|
||||
* any user types) in the SystemConfig 'install-in-user-type' lists
|
||||
* then:
|
||||
* <ul>
|
||||
* <li>If {@link #isImplicitWhitelistMode()}, the package is implicitly treated as whitelisted
|
||||
* for all users</li>
|
||||
* <li>Otherwise, the package is implicitly treated as blacklisted for all non-SYSTEM users</li>
|
||||
* <li>Either way, for {@link UserHandle#USER_SYSTEM}, the package will be implicitly
|
||||
* whitelisted so that it can be used for local development purposes.</li>
|
||||
* </ul>
|
||||
*/
|
||||
class UserSystemPackageInstaller {
|
||||
private static final String TAG = "UserManagerService";
|
||||
|
||||
/**
|
||||
* System Property whether to only install system packages on a user if they're whitelisted for
|
||||
* that user type. These are flags and can be freely combined.
|
||||
* <ul>
|
||||
* <li> 0 (0b000) - disable whitelist (install all system packages; no logging)</li>
|
||||
* <li> 1 (0b001) - enforce (only install system packages if they are whitelisted)</li>
|
||||
* <li> 2 (0b010) - log (log when a non-whitelisted package is run)</li>
|
||||
* <li> 4 (0b100) - implicitly whitelist any package not mentioned in the whitelist</li>
|
||||
* <li>-1 - use device default (as defined in res/res/values/config.xml)</li>
|
||||
* </ul>
|
||||
* Note: This list must be kept current with config_userTypePackageWhitelistMode in
|
||||
* frameworks/base/core/res/res/values/config.xml
|
||||
*/
|
||||
static final String PACKAGE_WHITELIST_MODE_PROP = "persist.debug.user.package_whitelist_mode";
|
||||
static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0;
|
||||
static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b001;
|
||||
static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0b010;
|
||||
static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b100;
|
||||
static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1;
|
||||
|
||||
@IntDef(flag = true, prefix = "USER_TYPE_PACKAGE_WHITELIST_MODE_", value = {
|
||||
USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE,
|
||||
USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
|
||||
USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
|
||||
USER_TYPE_PACKAGE_WHITELIST_MODE_LOG,
|
||||
USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface PackageWhitelistMode {}
|
||||
|
||||
/**
|
||||
* Maps system package manifest names to the user flags on which they should be initially
|
||||
* installed.
|
||||
* <p>Packages that are whitelisted, but then blacklisted so that they aren't to be installed on
|
||||
* any user, are purposefully still present in this list.
|
||||
*/
|
||||
private final ArrayMap<String, Integer> mWhitelitsedPackagesForUserTypes;
|
||||
|
||||
private final UserManagerService mUm;
|
||||
|
||||
UserSystemPackageInstaller(UserManagerService ums) {
|
||||
mUm = ums;
|
||||
mWhitelitsedPackagesForUserTypes =
|
||||
determineWhitelistedPackagesForUserTypes(SystemConfig.getInstance());
|
||||
}
|
||||
|
||||
/** Constructor for testing purposes. */
|
||||
@VisibleForTesting
|
||||
UserSystemPackageInstaller(UserManagerService ums, ArrayMap<String, Integer> whitelist) {
|
||||
mUm = ums;
|
||||
mWhitelitsedPackagesForUserTypes = whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* During OTAs and first boot, install/uninstall all system packages for all users based on the
|
||||
* user's UserInfo flags and the SystemConfig whitelist.
|
||||
* We do NOT uninstall packages during an OTA though.
|
||||
*
|
||||
* This is responsible for enforcing the whitelist for pre-existing users (i.e. USER_SYSTEM);
|
||||
* enforcement for new users is done when they are created in UserManagerService.createUser().
|
||||
*/
|
||||
boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) {
|
||||
final int mode = getWhitelistMode();
|
||||
checkWhitelistedSystemPackages(mode);
|
||||
if (!isUpgrade && !isFirstBoot) {
|
||||
return false;
|
||||
}
|
||||
Slog.i(TAG, "Reviewing whitelisted packages due to "
|
||||
+ (isFirstBoot ? "[firstBoot]" : "") + (isUpgrade ? "[upgrade]" : ""));
|
||||
final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
|
||||
// Install/uninstall system packages per user.
|
||||
for (int userId : mUm.getUserIds()) {
|
||||
final Set<String> userWhitelist = getInstallablePackagesForUserId(userId);
|
||||
pmInt.forEachPackage(pkg -> {
|
||||
if (!pkg.isSystem()) {
|
||||
return;
|
||||
}
|
||||
final boolean install =
|
||||
(userWhitelist == null || userWhitelist.contains(pkg.packageName))
|
||||
&& !pkg.applicationInfo.hiddenUntilInstalled;
|
||||
if (isUpgrade && !isFirstBoot && !install) {
|
||||
return; // To be careful, we don’t uninstall apps during OTAs
|
||||
}
|
||||
final boolean changed = pmInt.setInstalled(pkg, userId, install);
|
||||
if (changed) {
|
||||
Slog.i(TAG, (install ? "Installed " : "Uninstalled ")
|
||||
+ pkg.packageName + " for user " + userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the system packages and the mWhitelistedPackagesForUserTypes whitelist are
|
||||
* in 1-to-1 correspondence.
|
||||
*/
|
||||
private void checkWhitelistedSystemPackages(@PackageWhitelistMode int mode) {
|
||||
if (!isLogMode(mode) && !isEnforceMode(mode)) {
|
||||
return;
|
||||
}
|
||||
Slog.v(TAG, "Checking that all system packages are whitelisted.");
|
||||
final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages();
|
||||
PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
|
||||
|
||||
// Check whether all whitelisted packages are indeed on the system.
|
||||
for (String pkgName : allWhitelistedPackages) {
|
||||
PackageParser.Package pkg = pmInt.getPackage(pkgName);
|
||||
if (pkg == null) {
|
||||
Slog.w(TAG, pkgName + " is whitelisted but not present.");
|
||||
} else if (!pkg.isSystem()) {
|
||||
Slog.w(TAG, pkgName + " is whitelisted and present but not a system package.");
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether all system packages are indeed whitelisted.
|
||||
if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) {
|
||||
return;
|
||||
}
|
||||
final boolean doWtf = isEnforceMode(mode);
|
||||
pmInt.forEachPackage(pkg -> {
|
||||
if (pkg.isSystem() && !allWhitelistedPackages.contains(pkg.manifestPackageName)) {
|
||||
final String msg = "System package " + pkg.manifestPackageName
|
||||
+ " is not whitelisted using 'install-in-user-type' in SystemConfig "
|
||||
+ "for any user types!";
|
||||
if (doWtf) {
|
||||
Slog.wtf(TAG, msg);
|
||||
} else {
|
||||
Slog.e(TAG, msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Whether to only install system packages in new users for which they are whitelisted. */
|
||||
boolean isEnforceMode() {
|
||||
return isEnforceMode(getWhitelistMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to log a warning concerning potential problems with the user-type package whitelist.
|
||||
*/
|
||||
boolean isLogMode() {
|
||||
return isLogMode(getWhitelistMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to treat all packages that are not mentioned at all in the whitelist to be implicitly
|
||||
* whitelisted for all users.
|
||||
*/
|
||||
boolean isImplicitWhitelistMode() {
|
||||
return isImplicitWhitelistMode(getWhitelistMode());
|
||||
}
|
||||
|
||||
/** See {@link #isEnforceMode()}. */
|
||||
private static boolean isEnforceMode(int whitelistMode) {
|
||||
return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE) != 0;
|
||||
}
|
||||
|
||||
/** See {@link #isLogMode()}. */
|
||||
private static boolean isLogMode(int whitelistMode) {
|
||||
return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_LOG) != 0;
|
||||
}
|
||||
|
||||
/** See {@link #isImplicitWhitelistMode()}. */
|
||||
private static boolean isImplicitWhitelistMode(int whitelistMode) {
|
||||
return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST) != 0;
|
||||
}
|
||||
|
||||
/** Gets the PackageWhitelistMode for use of {@link #mWhitelitsedPackagesForUserTypes}. */
|
||||
private @PackageWhitelistMode int getWhitelistMode() {
|
||||
final int runtimeMode = SystemProperties.getInt(
|
||||
PACKAGE_WHITELIST_MODE_PROP, USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT);
|
||||
if (runtimeMode != USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT) {
|
||||
return runtimeMode;
|
||||
}
|
||||
return Resources.getSystem()
|
||||
.getInteger(com.android.internal.R.integer.config_userTypePackageWhitelistMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the system packages names that should be installed on the given user.
|
||||
* See {@link #getInstallablePackagesForUserType(int)}.
|
||||
*/
|
||||
private @Nullable Set<String> getInstallablePackagesForUserId(@UserIdInt int userId) {
|
||||
return getInstallablePackagesForUserType(mUm.getUserInfo(userId).flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the system package names that should be installed on a user with the given flags, as
|
||||
* determined by SystemConfig, the whitelist mode, and the apps actually on the device.
|
||||
* Names are the {@link PackageParser.Package#packageName}, not necessarily the manifest names.
|
||||
*
|
||||
* Returns null if all system packages should be installed (due enforce-mode being off).
|
||||
*/
|
||||
@Nullable Set<String> getInstallablePackagesForUserType(int flags) {
|
||||
final int mode = getWhitelistMode();
|
||||
if (!isEnforceMode(mode)) {
|
||||
return null;
|
||||
}
|
||||
final boolean isSystemUser = (flags & UserInfo.FLAG_SYSTEM) != 0;
|
||||
final boolean isImplicitWhitelistMode = isImplicitWhitelistMode(mode);
|
||||
final Set<String> whitelistedPackages = getWhitelistedPackagesForUserType(flags);
|
||||
|
||||
final Set<String> installPackages = new ArraySet<>();
|
||||
final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
|
||||
pmInt.forEachPackage(pkg -> {
|
||||
if (!pkg.isSystem()) {
|
||||
return;
|
||||
}
|
||||
if (shouldInstallPackage(pkg, mWhitelitsedPackagesForUserTypes,
|
||||
whitelistedPackages, isImplicitWhitelistMode, isSystemUser)) {
|
||||
// Although the whitelist uses manifest names, this function returns packageNames.
|
||||
installPackages.add(pkg.packageName);
|
||||
}
|
||||
});
|
||||
return installPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given system package should be installed on the given user, based on the
|
||||
* the given whitelist of system packages.
|
||||
*
|
||||
* @param sysPkg the system package. Must be a system package; no verification for this is done.
|
||||
* @param userTypeWhitelist map of package manifest names to user flags on which they should be
|
||||
* installed
|
||||
* @param userWhitelist set of package manifest names that should be installed on this
|
||||
* particular user. This must be consistent with userTypeWhitelist, but is
|
||||
* passed in separately to avoid repeatedly calculating it from
|
||||
* userTypeWhitelist.
|
||||
* @param isImplicitWhitelistMode whether non-mentioned packages are implicitly whitelisted.
|
||||
* @param isSystemUser whether the user is USER_SYSTEM (which gets special treatment).
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean shouldInstallPackage(PackageParser.Package sysPkg,
|
||||
@NonNull ArrayMap<String, Integer> userTypeWhitelist,
|
||||
@NonNull Set<String> userWhitelist, boolean isImplicitWhitelistMode,
|
||||
boolean isSystemUser) {
|
||||
|
||||
final String pkgName = sysPkg.manifestPackageName;
|
||||
boolean install = (isImplicitWhitelistMode && !userTypeWhitelist.containsKey(pkgName))
|
||||
|| userWhitelist.contains(pkgName);
|
||||
|
||||
// For the purposes of local development, any package that isn't even mentioned in the
|
||||
// whitelist at all is implicitly treated as whitelisted for the SYSTEM user.
|
||||
if (!install && isSystemUser && !userTypeWhitelist.containsKey(pkgName)) {
|
||||
install = true;
|
||||
Slog.e(TAG, "System package " + pkgName + " is not mentioned "
|
||||
+ "in SystemConfig's 'install-in-user-type' but we are "
|
||||
+ "implicitly treating it as whitelisted for the SYSTEM user.");
|
||||
}
|
||||
return install;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the package manifest names that are whitelisted for a user with the given flags,
|
||||
* as determined by SystemConfig.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@NonNull Set<String> getWhitelistedPackagesForUserType(int flags) {
|
||||
Set<String> installablePkgs = new ArraySet<>(mWhitelitsedPackagesForUserTypes.size());
|
||||
for (int i = 0; i < mWhitelitsedPackagesForUserTypes.size(); i++) {
|
||||
String pkgName = mWhitelitsedPackagesForUserTypes.keyAt(i);
|
||||
int whitelistedUserTypes = mWhitelitsedPackagesForUserTypes.valueAt(i);
|
||||
if ((flags & whitelistedUserTypes) != 0) {
|
||||
installablePkgs.add(pkgName);
|
||||
}
|
||||
}
|
||||
return installablePkgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of package manifest names that are included anywhere in the package-to-user-type
|
||||
* whitelist, as determined by SystemConfig.
|
||||
*
|
||||
* Packages that are whitelisted, but then blacklisted so that they aren't to be installed on
|
||||
* any user, are still present in this list, since that is a valid scenario (e.g. if an OEM
|
||||
* completely blacklists an AOSP app).
|
||||
*/
|
||||
private Set<String> getWhitelistedSystemPackages() {
|
||||
return mWhitelitsedPackagesForUserTypes.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of package manifest names to the user flags on which it is to be installed.
|
||||
* Also, clears this data from SystemConfig where it was stored inefficiently (and therefore
|
||||
* should be called exactly once, even if the data isn't useful).
|
||||
*
|
||||
* Any system packages not present in this map should not even be on the device at all.
|
||||
* To enforce this:
|
||||
* <ul>
|
||||
* <li>Illegal user types are ignored.</li>
|
||||
* <li>Packages that never whitelisted at all (even if they are explicitly blacklisted) are
|
||||
* ignored.</li>
|
||||
* <li>Packages that are blacklisted whenever they are whitelisted will be stored with the
|
||||
* flag 0 (since this is a valid scenario, e.g. if an OEM completely blacklists an AOSP
|
||||
* app).</li>
|
||||
* </ul>
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static ArrayMap<String, Integer> determineWhitelistedPackagesForUserTypes(
|
||||
SystemConfig sysConfig) {
|
||||
|
||||
final ArrayMap<String, Set<String>> whitelist =
|
||||
sysConfig.getAndClearPackageToUserTypeWhitelist();
|
||||
// result maps packageName -> userTypes on which the package should be installed.
|
||||
final ArrayMap<String, Integer> result = new ArrayMap<>(whitelist.size() + 1);
|
||||
// First, do the whitelisted user types.
|
||||
for (int i = 0; i < whitelist.size(); i++) {
|
||||
final String pkgName = whitelist.keyAt(i);
|
||||
final int flags = getFlagsFromUserTypes(whitelist.valueAt(i));
|
||||
if (flags != 0) {
|
||||
result.put(pkgName, flags);
|
||||
}
|
||||
}
|
||||
// Then, un-whitelist any blacklisted user types.
|
||||
// TODO(b/141370854): Right now, the blacklist is actually just an 'unwhitelist'. Which
|
||||
// direction we go depends on how we design user subtypes, which is still
|
||||
// being designed. For now, unwhitelisting works for current use-cases.
|
||||
final ArrayMap<String, Set<String>> blacklist =
|
||||
sysConfig.getAndClearPackageToUserTypeBlacklist();
|
||||
for (int i = 0; i < blacklist.size(); i++) {
|
||||
final String pkgName = blacklist.keyAt(i);
|
||||
final int nonFlags = getFlagsFromUserTypes(blacklist.valueAt(i));
|
||||
final Integer flags = result.get(pkgName);
|
||||
if (flags != null) {
|
||||
result.put(pkgName, flags & ~nonFlags);
|
||||
}
|
||||
}
|
||||
// Regardless of the whitelists/blacklists, ensure mandatory packages.
|
||||
result.put("android",
|
||||
UserInfo.FLAG_SYSTEM | UserInfo.FLAG_FULL | UserInfo.PROFILE_FLAGS_MASK);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Converts a user types, as used in SystemConfig, to a UserInfo flag. */
|
||||
private static int getFlagsFromUserTypes(Iterable<String> userTypes) {
|
||||
int flags = 0;
|
||||
for (String type : userTypes) {
|
||||
switch (type) {
|
||||
case "GUEST":
|
||||
flags |= UserInfo.FLAG_GUEST;
|
||||
break;
|
||||
case "RESTRICTED":
|
||||
flags |= UserInfo.FLAG_RESTRICTED;
|
||||
break;
|
||||
case "MANAGED_PROFILE":
|
||||
flags |= UserInfo.FLAG_MANAGED_PROFILE;
|
||||
break;
|
||||
case "EPHEMERAL":
|
||||
flags |= UserInfo.FLAG_EPHEMERAL;
|
||||
break;
|
||||
case "DEMO":
|
||||
flags |= UserInfo.FLAG_DEMO;
|
||||
break;
|
||||
case "FULL":
|
||||
flags |= UserInfo.FLAG_FULL;
|
||||
break;
|
||||
case "SYSTEM":
|
||||
flags |= UserInfo.FLAG_SYSTEM;
|
||||
break;
|
||||
case "PROFILE":
|
||||
flags |= UserInfo.PROFILE_FLAGS_MASK;
|
||||
break;
|
||||
default:
|
||||
Slog.w(TAG, "SystemConfig contained an invalid user type: " + type);
|
||||
break;
|
||||
// Other UserInfo flags are forbidden.
|
||||
// In particular, FLAG_INITIALIZED, FLAG_DISABLED, FLAG_QUIET_MODE are inapplicable.
|
||||
// The following are invalid now, but are reconsiderable: FLAG_PRIMARY, FLAG_ADMIN.
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw) {
|
||||
for (int i = 0; i < mWhitelitsedPackagesForUserTypes.size(); i++) {
|
||||
final String pkgName = mWhitelitsedPackagesForUserTypes.keyAt(i);
|
||||
final String whitelistedUserTypes =
|
||||
UserInfo.flagsToString(mWhitelitsedPackagesForUserTypes.valueAt(i));
|
||||
pw.println(" " + pkgName + ": " + whitelistedUserTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Tests for {@link SystemConfig}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest FrameworksServicesTests:SystemConfigTest
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class SystemConfigTest {
|
||||
private static final String LOG_TAG = "SystemConfigTest";
|
||||
|
||||
private SystemConfig mSysConfig;
|
||||
|
||||
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mSysConfig = new SystemConfigTestClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass of SystemConfig without running the constructor.
|
||||
*/
|
||||
private class SystemConfigTestClass extends SystemConfig {
|
||||
SystemConfigTestClass() {
|
||||
super(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that readPermissions works correctly for the tag: install-in-user-type
|
||||
*/
|
||||
@Test
|
||||
public void testInstallInUserType() throws Exception {
|
||||
final String contents1 =
|
||||
"<permissions>\n"
|
||||
+ " <install-in-user-type package=\"com.android.package1\">\n"
|
||||
+ " <install-in user-type=\"FULL\" />\n"
|
||||
+ " <install-in user-type=\"PROFILE\" />\n"
|
||||
+ " </install-in-user-type>\n"
|
||||
+ " <install-in-user-type package=\"com.android.package2\">\n"
|
||||
+ " <install-in user-type=\"FULL\" />\n"
|
||||
+ " <install-in user-type=\"PROFILE\" />\n"
|
||||
+ " <do-not-install-in user-type=\"GUEST\" />\n"
|
||||
+ " </install-in-user-type>\n"
|
||||
+ "</permissions>";
|
||||
|
||||
final String contents2 =
|
||||
"<permissions>\n"
|
||||
+ " <install-in-user-type package=\"com.android.package2\">\n"
|
||||
+ " <install-in user-type=\"SYSTEM\" />\n"
|
||||
+ " <do-not-install-in user-type=\"PROFILE\" />\n"
|
||||
+ " </install-in-user-type>\n"
|
||||
+ "</permissions>";
|
||||
|
||||
final String contents3 =
|
||||
"<permissions>\n"
|
||||
+ " <install-in-user-type package=\"com.android.package2\">\n"
|
||||
+ " <install-in invalid-attribute=\"ADMIN\" />\n" // Ignore invalid attribute
|
||||
+ " </install-in-user-type>\n"
|
||||
+ " <install-in-user-type package=\"com.android.package2\">\n"
|
||||
+ " <install-in user-type=\"RESTRICTED\" />\n" // Valid
|
||||
+ " </install-in-user-type>\n"
|
||||
+ " <install-in-user-type>\n" // Ignored since missing package name
|
||||
+ " <install-in user-type=\"ADMIN\" />\n"
|
||||
+ " </install-in-user-type>\n"
|
||||
+ "</permissions>";
|
||||
|
||||
Map<String, Set<String>> expectedWhite = new ArrayMap<>();
|
||||
expectedWhite.put("com.android.package1",
|
||||
new ArraySet<>(Arrays.asList("FULL", "PROFILE")));
|
||||
expectedWhite.put("com.android.package2",
|
||||
new ArraySet<>(Arrays.asList("FULL", "PROFILE", "RESTRICTED", "SYSTEM")));
|
||||
|
||||
Map<String, Set<String>> expectedBlack = new ArrayMap<>();
|
||||
expectedBlack.put("com.android.package2",
|
||||
new ArraySet<>(Arrays.asList("GUEST", "PROFILE")));
|
||||
|
||||
final File folder1 = createTempSubfolder("folder1");
|
||||
createTempFile(folder1, "permFile1.xml", contents1);
|
||||
|
||||
final File folder2 = createTempSubfolder("folder2");
|
||||
createTempFile(folder2, "permFile2.xml", contents2);
|
||||
|
||||
// Also, make a third file, but with the name folder1/permFile2.xml, to prove no conflicts.
|
||||
createTempFile(folder1, "permFile2.xml", contents3);
|
||||
|
||||
mSysConfig.readPermissions(folder1, /* No permission needed anyway */ 0);
|
||||
mSysConfig.readPermissions(folder2, /* No permission needed anyway */ 0);
|
||||
|
||||
Map<String, Set<String>> actualWhite = mSysConfig.getAndClearPackageToUserTypeWhitelist();
|
||||
Map<String, Set<String>> actualBlack = mSysConfig.getAndClearPackageToUserTypeBlacklist();
|
||||
|
||||
assertEquals("Whitelist was not cleared", 0,
|
||||
mSysConfig.getAndClearPackageToUserTypeWhitelist().size());
|
||||
assertEquals("Blacklist was not cleared", 0,
|
||||
mSysConfig.getAndClearPackageToUserTypeBlacklist().size());
|
||||
|
||||
assertEquals("Incorrect whitelist.", expectedWhite, actualWhite);
|
||||
assertEquals("Incorrect blacklist", expectedBlack, actualBlack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
|
||||
* @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
|
||||
* @return the folder
|
||||
*/
|
||||
private File createTempSubfolder(String folderName)
|
||||
throws IOException {
|
||||
File folder = new File(mTemporaryFolder.getRoot(), folderName);
|
||||
folder.mkdir();
|
||||
return folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
|
||||
* @param folder pre-existing subdirectory of mTemporaryFolder to put the file
|
||||
* @param fileName name of the file (e.g. filename.xml) to create
|
||||
* @param contents contents to write to the file
|
||||
* @return the folder containing the newly created file (not the file itself!)
|
||||
*/
|
||||
private File createTempFile(File folder, String fileName, String contents)
|
||||
throws IOException {
|
||||
File file = new File(folder, fileName);
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
|
||||
bw.write(contents);
|
||||
bw.close();
|
||||
|
||||
// Print to logcat for test debugging.
|
||||
Log.d(LOG_TAG, "Contents of file " + file.getAbsolutePath());
|
||||
Scanner input = new Scanner(file);
|
||||
while (input.hasNextLine()) {
|
||||
Log.d(LOG_TAG, input.nextLine());
|
||||
}
|
||||
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* 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.PROFILE_FLAGS_MASK | 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 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
|
||||
try {
|
||||
String result = mUiDevice.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.PROFILE_FLAGS_MASK);
|
||||
return pkgFlagMap;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user