Merge "Kernel mapping for package scope by user"

This commit is contained in:
Amith Yamasani
2017-02-15 00:53:22 +00:00
committed by Android (Google) Code Review
6 changed files with 275 additions and 15 deletions

View 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)

View 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>

View File

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

View File

@@ -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,

View File

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

View File

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