Merge "Kernel mapping for package scope by user"
This commit is contained in:
committed by
Android (Google) Code Review
commit
5e2714ea7a
20
core/tests/packagemanagertests/Android.mk
Normal file
20
core/tests/packagemanagertests/Android.mk
Normal file
@@ -0,0 +1,20 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# We only want this apk build for tests.
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
# Include all test java files.
|
||||
LOCAL_SRC_FILES := \
|
||||
$(call all-java-files-under, src)
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
android-support-test \
|
||||
frameworks-base-testutils
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||
LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests
|
||||
|
||||
LOCAL_CERTIFICATE := platform
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
31
core/tests/packagemanagertests/AndroidManifest.xml
Normal file
31
core/tests/packagemanagertests/AndroidManifest.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:installLocation="internalOnly"
|
||||
package="com.android.frameworks.coretests.packagemanager"
|
||||
android:sharedUserId="android.uid.system">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:name="android.support.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.frameworks.coretests.packagemanager"
|
||||
android:label="Frameworks PackageManager Core Tests" />
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.content.pm;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.FileUtils;
|
||||
import android.os.Process;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserManager;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.Log;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This test needs to be run without any secondary users on the device,
|
||||
* and selinux needs to be disabled with "adb shell setenforce 0".
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class KernelPackageMappingTests {
|
||||
|
||||
private static final String TAG = "KernelPackageMapping";
|
||||
private static final String SDCARDFS_PATH = "/config/sdcardfs";
|
||||
|
||||
private UserInfo mSecondaryUser;
|
||||
|
||||
private static File getKernelPackageDir(String packageName) {
|
||||
return new File(new File(SDCARDFS_PATH), packageName);
|
||||
}
|
||||
|
||||
private static File getKernelPackageFile(String packageName, String filename) {
|
||||
return new File(getKernelPackageDir(packageName), filename);
|
||||
}
|
||||
|
||||
private UserManager getUserManager() {
|
||||
UserManager um = (UserManager) InstrumentationRegistry.getContext().getSystemService(
|
||||
Context.USER_SERVICE);
|
||||
return um;
|
||||
}
|
||||
|
||||
private IPackageManager getIPackageManager() {
|
||||
IPackageManager ipm = IPackageManager.Stub.asInterface(
|
||||
ServiceManager.getService("package"));
|
||||
return ipm;
|
||||
}
|
||||
|
||||
private static String getContent(File file) {
|
||||
try {
|
||||
return FileUtils.readTextFile(file, 0, null).trim();
|
||||
} catch (IOException ioe) {
|
||||
Log.w(TAG, "Couldn't read file " + file.getAbsolutePath() + "\n" + ioe);
|
||||
return "<error>";
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstalledPrimary() throws Exception {
|
||||
assertEquals("1000", getContent(getKernelPackageFile("com.android.settings", "appid")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstalledAll() throws Exception {
|
||||
assertEquals("", getContent(getKernelPackageFile("com.android.settings",
|
||||
"excluded_userids")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotInstalledSecondary() throws Exception {
|
||||
mSecondaryUser = getUserManager().createUser("Secondary", 0);
|
||||
assertEquals(Integer.toString(mSecondaryUser.id),
|
||||
getContent(
|
||||
getKernelPackageFile("com.android.frameworks.coretests.packagemanager",
|
||||
"excluded_userids")));
|
||||
}
|
||||
|
||||
@After
|
||||
public void shutDown() throws Exception {
|
||||
if (mSecondaryUser != null) {
|
||||
getUserManager().removeUser(mSecondaryUser.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2157,6 +2157,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
pkgSetting.setInstalled(install, UserHandle.USER_SYSTEM);
|
||||
}
|
||||
}
|
||||
scheduleWritePackageRestrictionsLocked(UserHandle.USER_SYSTEM);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13313,6 +13314,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
pkgSetting.setHidden(false, userId);
|
||||
pkgSetting.setInstallReason(installReason, userId);
|
||||
mSettings.writePackageRestrictionsLPr(userId);
|
||||
mSettings.writeKernelMappingLPr(pkgSetting);
|
||||
installed = true;
|
||||
}
|
||||
}
|
||||
@@ -16410,6 +16412,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
//note that the new package setting would have already been
|
||||
//added to mPackages. It hasn't been persisted yet.
|
||||
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
|
||||
// TODO: Remove this write? It's also written at the end of this method
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
|
||||
mSettings.writeLPr();
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
@@ -16483,6 +16486,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
} else if (!previousUserIds.contains(userId)) {
|
||||
ps.setInstallReason(installReason, userId);
|
||||
}
|
||||
mSettings.writeKernelMappingLPr(ps);
|
||||
}
|
||||
res.name = pkgName;
|
||||
res.uid = newPackage.applicationInfo.uid;
|
||||
@@ -17667,6 +17671,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
|
||||
// writer
|
||||
synchronized (mPackages) {
|
||||
boolean installedStateChanged = false;
|
||||
if (deletedPs != null) {
|
||||
if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
|
||||
clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
|
||||
@@ -17714,6 +17719,9 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
if (DEBUG_REMOVE) {
|
||||
Slog.d(TAG, " user " + userId + " => " + installed);
|
||||
}
|
||||
if (installed != ps.getInstalled(userId)) {
|
||||
installedStateChanged = true;
|
||||
}
|
||||
ps.setInstalled(installed, userId);
|
||||
}
|
||||
}
|
||||
@@ -17723,6 +17731,9 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
// Save settings now
|
||||
mSettings.writeLPr();
|
||||
}
|
||||
if (installedStateChanged) {
|
||||
mSettings.writeKernelMappingLPr(ps);
|
||||
}
|
||||
}
|
||||
if (removedAppId != -1) {
|
||||
// A user ID was deleted here. Go through all users and remove it
|
||||
@@ -17865,6 +17876,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
|
||||
|
||||
if (applyUserRestrictions) {
|
||||
boolean installedStateChanged = false;
|
||||
if (DEBUG_REMOVE) {
|
||||
Slog.d(TAG, "Propagating install state across reinstall");
|
||||
}
|
||||
@@ -17873,6 +17885,9 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
if (DEBUG_REMOVE) {
|
||||
Slog.d(TAG, " user " + userId + " => " + installed);
|
||||
}
|
||||
if (installed != ps.getInstalled(userId)) {
|
||||
installedStateChanged = true;
|
||||
}
|
||||
ps.setInstalled(installed, userId);
|
||||
|
||||
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
|
||||
@@ -17880,6 +17895,9 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
// Regardless of writeSettings we need to ensure that this restriction
|
||||
// state propagation is persisted
|
||||
mSettings.writeAllUsersPackageRestrictionsLPr();
|
||||
if (installedStateChanged) {
|
||||
mSettings.writeKernelMappingLPr(ps);
|
||||
}
|
||||
}
|
||||
// can downgrade to reader here
|
||||
if (writeSettings) {
|
||||
@@ -18083,6 +18101,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
// broadcasts will be sent correctly.
|
||||
if (DEBUG_REMOVE) Slog.d(TAG, "Not installed by other users, full delete");
|
||||
ps.setInstalled(true, user.getIdentifier());
|
||||
mSettings.writeKernelMappingLPr(ps);
|
||||
}
|
||||
} else {
|
||||
// This is a system app, so we assume that the
|
||||
@@ -18192,6 +18211,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
ps.readUserState(nextUserId).domainVerificationStatus, 0,
|
||||
PackageManager.INSTALL_REASON_UNKNOWN);
|
||||
}
|
||||
mSettings.writeKernelMappingLPr(ps);
|
||||
}
|
||||
|
||||
private boolean clearPackageStateForUserLIF(PackageSetting ps, int userId,
|
||||
|
||||
@@ -40,6 +40,9 @@ import java.util.Set;
|
||||
* Settings base class for pending and resolved classes.
|
||||
*/
|
||||
abstract class PackageSettingBase extends SettingBase {
|
||||
|
||||
private static final int[] EMPTY_INT_ARRAY = new int[0];
|
||||
|
||||
/**
|
||||
* Indicates the state of installation. Used by PackageManager to figure out
|
||||
* incomplete installations. Say a package is being installed (the state is
|
||||
@@ -502,6 +505,25 @@ abstract class PackageSettingBase extends SettingBase {
|
||||
userState.delete(userId);
|
||||
}
|
||||
|
||||
public int[] getNotInstalledUserIds() {
|
||||
int count = 0;
|
||||
int userStateCount = userState.size();
|
||||
for (int i = 0; i < userStateCount; i++) {
|
||||
if (userState.valueAt(i).installed == false) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count == 0) return EMPTY_INT_ARRAY;
|
||||
int[] excludedUserIds = new int[count];
|
||||
int idx = 0;
|
||||
for (int i = 0; i < userStateCount; i++) {
|
||||
if (userState.valueAt(i).installed == false) {
|
||||
excludedUserIds[idx++] = userState.keyAt(i);
|
||||
}
|
||||
}
|
||||
return excludedUserIds;
|
||||
}
|
||||
|
||||
IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
|
||||
return verificationInfo;
|
||||
}
|
||||
|
||||
@@ -252,6 +252,7 @@ final class Settings {
|
||||
private final File mPackageListFilename;
|
||||
private final File mStoppedPackagesFilename;
|
||||
private final File mBackupStoppedPackagesFilename;
|
||||
/** The top level directory in configfs for sdcardfs to push the package->uid,userId mappings */
|
||||
private final File mKernelMappingFilename;
|
||||
|
||||
/** Map from package name to settings */
|
||||
@@ -260,8 +261,8 @@ final class Settings {
|
||||
/** List of packages that installed other packages */
|
||||
final ArraySet<String> mInstallerPackages = new ArraySet<>();
|
||||
|
||||
/** Map from package name to appId */
|
||||
private final ArrayMap<String, Integer> mKernelMapping = new ArrayMap<>();
|
||||
/** Map from package name to appId and excluded userids */
|
||||
private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>();
|
||||
|
||||
// List of replaced system applications
|
||||
private final ArrayMap<String, PackageSetting> mDisabledSysPackages =
|
||||
@@ -271,6 +272,11 @@ final class Settings {
|
||||
private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications =
|
||||
new ArrayMap<String, IntentFilterVerificationInfo>();
|
||||
|
||||
private static final class KernelPackageState {
|
||||
int appId;
|
||||
int[] excludedUserIds;
|
||||
}
|
||||
|
||||
// Bookkeeping for restored user permission grants
|
||||
final class RestoredPermissionGrant {
|
||||
String permissionName;
|
||||
@@ -2512,6 +2518,15 @@ final class Settings {
|
||||
//Debug.stopMethodTracing();
|
||||
}
|
||||
|
||||
private void writeKernelRemoveUserLPr(int userId) {
|
||||
if (mKernelMappingFilename == null) return;
|
||||
|
||||
File removeUserIdFile = new File(mKernelMappingFilename, "remove_userid");
|
||||
if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + userId + " to " + removeUserIdFile
|
||||
.getAbsolutePath());
|
||||
writeIntToFile(removeUserIdFile, userId);
|
||||
}
|
||||
|
||||
void writeKernelMappingLPr() {
|
||||
if (mKernelMappingFilename == null) return;
|
||||
|
||||
@@ -2538,27 +2553,63 @@ final class Settings {
|
||||
}
|
||||
|
||||
void writeKernelMappingLPr(PackageSetting ps) {
|
||||
if (mKernelMappingFilename == null) return;
|
||||
if (mKernelMappingFilename == null || ps == null || ps.name == null) return;
|
||||
|
||||
final Integer cur = mKernelMapping.get(ps.name);
|
||||
if (cur != null && cur.intValue() == ps.appId) {
|
||||
// Ignore when mapping already matches
|
||||
return;
|
||||
KernelPackageState cur = mKernelMapping.get(ps.name);
|
||||
final boolean firstTime = cur == null;
|
||||
int[] excludedUserIds = ps.getNotInstalledUserIds();
|
||||
final boolean userIdsChanged = firstTime
|
||||
|| !Arrays.equals(excludedUserIds, cur.excludedUserIds);
|
||||
|
||||
// Package directory
|
||||
final File dir = new File(mKernelMappingFilename, ps.name);
|
||||
|
||||
if (firstTime) {
|
||||
dir.mkdir();
|
||||
// Create a new mapping state
|
||||
cur = new KernelPackageState();
|
||||
mKernelMapping.put(ps.name, cur);
|
||||
}
|
||||
|
||||
if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId);
|
||||
// If mapping is incorrect or non-existent, write the appid file
|
||||
if (cur.appId != ps.appId) {
|
||||
final File appIdFile = new File(dir, "appid");
|
||||
writeIntToFile(appIdFile, ps.appId);
|
||||
if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + ps.name + " to " + ps.appId);
|
||||
}
|
||||
|
||||
final File dir = new File(mKernelMappingFilename, ps.name);
|
||||
dir.mkdir();
|
||||
if (userIdsChanged) {
|
||||
// Build the exclusion list -- the ids to add to the exclusion list
|
||||
for (int i = 0; i < excludedUserIds.length; i++) {
|
||||
if (cur.excludedUserIds == null || !ArrayUtils.contains(cur.excludedUserIds,
|
||||
excludedUserIds[i])) {
|
||||
writeIntToFile(new File(dir, "excluded_userids"), excludedUserIds[i]);
|
||||
if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + excludedUserIds[i] + " to "
|
||||
+ ps.name + "/excluded_userids");
|
||||
}
|
||||
}
|
||||
// Build the inclusion list -- the ids to remove from the exclusion list
|
||||
if (cur.excludedUserIds != null) {
|
||||
for (int i = 0; i < cur.excludedUserIds.length; i++) {
|
||||
if (!ArrayUtils.contains(excludedUserIds, cur.excludedUserIds[i])) {
|
||||
writeIntToFile(new File(dir, "clear_userid"),
|
||||
cur.excludedUserIds[i]);
|
||||
if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + cur.excludedUserIds[i] + " to "
|
||||
+ ps.name + "/clear_userid");
|
||||
|
||||
final File file = new File(dir, "appid");
|
||||
}
|
||||
}
|
||||
}
|
||||
cur.excludedUserIds = excludedUserIds;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeIntToFile(File file, int value) {
|
||||
try {
|
||||
// Note that the use of US_ASCII here is safe, we're only writing a decimal
|
||||
// number to the file.
|
||||
FileUtils.bytesToFile(file.getAbsolutePath(),
|
||||
Integer.toString(ps.appId).getBytes(StandardCharsets.US_ASCII));
|
||||
mKernelMapping.put(ps.name, ps.appId);
|
||||
Integer.toString(value).getBytes(StandardCharsets.US_ASCII));
|
||||
} catch (IOException ignored) {
|
||||
Slog.w(TAG, "Couldn't write " + value + " to " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4081,6 +4132,9 @@ final class Settings {
|
||||
!ArrayUtils.contains(disallowedPackages, ps.name);
|
||||
// Only system apps are initially installed.
|
||||
ps.setInstalled(shouldInstall, userHandle);
|
||||
if (!shouldInstall) {
|
||||
writeKernelMappingLPr(ps);
|
||||
}
|
||||
// Need to create a data directory for all apps under this user. Accumulate all
|
||||
// required args and call the installer after mPackages lock has been released
|
||||
volumeUuids[i] = ps.volumeUuid;
|
||||
@@ -4123,6 +4177,10 @@ final class Settings {
|
||||
mRuntimePermissionsPersistence.onUserRemovedLPw(userId);
|
||||
|
||||
writePackageListLPr();
|
||||
|
||||
// Inform kernel that the user was removed, so that packages are marked uninstalled
|
||||
// for sdcardfs
|
||||
writeKernelRemoveUserLPr(userId);
|
||||
}
|
||||
|
||||
void removeCrossProfileIntentFiltersLPw(int userId) {
|
||||
|
||||
Reference in New Issue
Block a user