Merge "Tests for UserDataPreparer"
This commit is contained in:
committed by
Android (Google) Code Review
commit
68281d4327
@@ -833,7 +833,6 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
private List<String> mKeepUninstalledPackages;
|
||||
|
||||
private UserManagerInternal mUserManagerInternal;
|
||||
private final UserDataPreparer mUserDataPreparer;
|
||||
|
||||
private File mCacheDir;
|
||||
|
||||
@@ -1937,7 +1936,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
|
||||
// Clean up any users or apps that were removed or recreated
|
||||
// while this volume was missing
|
||||
reconcileUsers(volumeUuid);
|
||||
sUserManager.reconcileUsers(volumeUuid);
|
||||
reconcileApps(volumeUuid);
|
||||
|
||||
// Clean up any install sessions that expired or were
|
||||
@@ -2270,8 +2269,8 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
mEphemeralInstallDir = new File(dataDir, "app-ephemeral");
|
||||
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
|
||||
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
|
||||
mUserDataPreparer = new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore);
|
||||
sUserManager = new UserManagerService(context, this, mUserDataPreparer, mPackages);
|
||||
sUserManager = new UserManagerService(context, this,
|
||||
new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
|
||||
|
||||
// Propagate permission configuration in to package manager.
|
||||
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
|
||||
@@ -19971,7 +19970,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
|
||||
});
|
||||
|
||||
// Now that we're mostly running, clean up stale users and apps
|
||||
reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
|
||||
sUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
|
||||
reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
|
||||
}
|
||||
|
||||
@@ -21309,60 +21308,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine all users present on given mounted volume, and destroy data
|
||||
* belonging to users that are no longer valid, or whose user ID has been
|
||||
* recycled.
|
||||
*/
|
||||
private void reconcileUsers(String volumeUuid) {
|
||||
final List<File> files = new ArrayList<>();
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataSystemDeDirectory()));
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataSystemCeDirectory()));
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataMiscCeDirectory()));
|
||||
for (File file : files) {
|
||||
if (!file.isDirectory()) continue;
|
||||
|
||||
final int userId;
|
||||
final UserInfo info;
|
||||
try {
|
||||
userId = Integer.parseInt(file.getName());
|
||||
info = sUserManager.getUserInfo(userId);
|
||||
} catch (NumberFormatException e) {
|
||||
Slog.w(TAG, "Invalid user directory " + file);
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean destroyUser = false;
|
||||
if (info == null) {
|
||||
logCriticalInfo(Log.WARN, "Destroying user directory " + file
|
||||
+ " because no matching user was found");
|
||||
destroyUser = true;
|
||||
} else if (!mOnlyCore) {
|
||||
try {
|
||||
UserManagerService.enforceSerialNumber(file, info.serialNumber);
|
||||
} catch (IOException e) {
|
||||
logCriticalInfo(Log.WARN, "Destroying user directory " + file
|
||||
+ " because we failed to enforce serial number: " + e);
|
||||
destroyUser = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyUser) {
|
||||
synchronized (mInstallLock) {
|
||||
mUserDataPreparer.destroyUserDataLI(volumeUuid, userId,
|
||||
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertPackageKnown(String volumeUuid, String packageName)
|
||||
throws PackageManagerException {
|
||||
synchronized (mPackages) {
|
||||
|
||||
@@ -17,13 +17,28 @@
|
||||
package com.android.server.pm;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.Environment;
|
||||
import android.os.FileUtils;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.storage.VolumeInfo;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.android.server.pm.PackageManagerService.logCriticalInfo;
|
||||
|
||||
@@ -31,6 +46,9 @@ import static com.android.server.pm.PackageManagerService.logCriticalInfo;
|
||||
* Helper class for preparing and destroying user storage
|
||||
*/
|
||||
class UserDataPreparer {
|
||||
private static final String TAG = "UserDataPreparer";
|
||||
private static final String XATTR_SERIAL = "user.serial";
|
||||
|
||||
private final Object mInstallLock;
|
||||
private final Context mContext;
|
||||
private final boolean mOnlyCore;
|
||||
@@ -65,19 +83,15 @@ class UserDataPreparer {
|
||||
storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
|
||||
|
||||
if ((flags & StorageManager.FLAG_STORAGE_DE) != 0 && !mOnlyCore) {
|
||||
UserManagerService.enforceSerialNumber(
|
||||
Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
|
||||
enforceSerialNumber(getDataUserDeDirectory(volumeUuid, userId), userSerial);
|
||||
if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
|
||||
UserManagerService.enforceSerialNumber(
|
||||
Environment.getDataSystemDeDirectory(userId), userSerial);
|
||||
enforceSerialNumber(getDataSystemDeDirectory(userId), userSerial);
|
||||
}
|
||||
}
|
||||
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && !mOnlyCore) {
|
||||
UserManagerService.enforceSerialNumber(
|
||||
Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
|
||||
enforceSerialNumber(getDataUserCeDirectory(volumeUuid, userId), userSerial);
|
||||
if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
|
||||
UserManagerService.enforceSerialNumber(
|
||||
Environment.getDataSystemCeDirectory(userId), userSerial);
|
||||
enforceSerialNumber(getDataSystemCeDirectory(userId), userSerial);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,13 +131,13 @@ class UserDataPreparer {
|
||||
// Clean up system data
|
||||
if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
|
||||
if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
|
||||
FileUtils.deleteContentsAndDir(Environment.getUserSystemDirectory(userId));
|
||||
FileUtils.deleteContentsAndDir(Environment.getDataSystemDeDirectory(userId));
|
||||
FileUtils.deleteContentsAndDir(Environment.getDataMiscDeDirectory(userId));
|
||||
FileUtils.deleteContentsAndDir(getUserSystemDirectory(userId));
|
||||
FileUtils.deleteContentsAndDir(getDataSystemDeDirectory(userId));
|
||||
FileUtils.deleteContentsAndDir(getDataMiscDeDirectory(userId));
|
||||
}
|
||||
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
|
||||
FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
|
||||
FileUtils.deleteContentsAndDir(Environment.getDataMiscCeDirectory(userId));
|
||||
FileUtils.deleteContentsAndDir(getDataSystemCeDirectory(userId));
|
||||
FileUtils.deleteContentsAndDir(getDataMiscCeDirectory(userId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,4 +150,183 @@ class UserDataPreparer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine all users present on given mounted volume, and destroy data
|
||||
* belonging to users that are no longer valid, or whose user ID has been
|
||||
* recycled.
|
||||
*/
|
||||
void reconcileUsers(String volumeUuid, List<UserInfo> validUsersList) {
|
||||
final List<File> files = new ArrayList<>();
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataSystemDeDirectory()));
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataSystemCeDirectory()));
|
||||
Collections.addAll(files, FileUtils
|
||||
.listFilesOrEmpty(Environment.getDataMiscCeDirectory()));
|
||||
reconcileUsers(volumeUuid, validUsersList, files);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void reconcileUsers(String volumeUuid, List<UserInfo> validUsersList, List<File> files) {
|
||||
final int userCount = validUsersList.size();
|
||||
SparseArray<UserInfo> users = new SparseArray<>(userCount);
|
||||
for (int i = 0; i < userCount; i++) {
|
||||
UserInfo user = validUsersList.get(i);
|
||||
users.put(user.id, user);
|
||||
}
|
||||
for (File file : files) {
|
||||
if (!file.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int userId;
|
||||
final UserInfo info;
|
||||
try {
|
||||
userId = Integer.parseInt(file.getName());
|
||||
info = users.get(userId);
|
||||
} catch (NumberFormatException e) {
|
||||
Slog.w(TAG, "Invalid user directory " + file);
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean destroyUser = false;
|
||||
if (info == null) {
|
||||
logCriticalInfo(Log.WARN, "Destroying user directory " + file
|
||||
+ " because no matching user was found");
|
||||
destroyUser = true;
|
||||
} else if (!mOnlyCore) {
|
||||
try {
|
||||
enforceSerialNumber(file, info.serialNumber);
|
||||
} catch (IOException e) {
|
||||
logCriticalInfo(Log.WARN, "Destroying user directory " + file
|
||||
+ " because we failed to enforce serial number: " + e);
|
||||
destroyUser = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (destroyUser) {
|
||||
synchronized (mInstallLock) {
|
||||
destroyUserDataLI(volumeUuid, userId,
|
||||
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected File getDataMiscCeDirectory(int userId) {
|
||||
return Environment.getDataMiscCeDirectory(userId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected File getDataSystemCeDirectory(int userId) {
|
||||
return Environment.getDataSystemCeDirectory(userId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected File getDataMiscDeDirectory(int userId) {
|
||||
return Environment.getDataMiscDeDirectory(userId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected File getUserSystemDirectory(int userId) {
|
||||
return Environment.getUserSystemDirectory(userId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected File getDataUserCeDirectory(String volumeUuid, int userId) {
|
||||
return Environment.getDataUserCeDirectory(volumeUuid, userId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected File getDataSystemDeDirectory(int userId) {
|
||||
return Environment.getDataSystemDeDirectory(userId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected File getDataUserDeDirectory(String volumeUuid, int userId) {
|
||||
return Environment.getDataUserDeDirectory(volumeUuid, userId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean isFileEncryptedEmulatedOnly() {
|
||||
return StorageManager.isFileEncryptedEmulatedOnly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforce that serial number stored in user directory inode matches the
|
||||
* given expected value. Gracefully sets the serial number if currently
|
||||
* undefined.
|
||||
*
|
||||
* @throws IOException when problem extracting serial number, or serial
|
||||
* number is mismatched.
|
||||
*/
|
||||
void enforceSerialNumber(File file, int serialNumber) throws IOException {
|
||||
if (isFileEncryptedEmulatedOnly()) {
|
||||
// When we're emulating FBE, the directory may have been chmod
|
||||
// 000'ed, meaning we can't read the serial number to enforce it;
|
||||
// instead of destroying the user, just log a warning.
|
||||
Slog.w(TAG, "Device is emulating FBE; assuming current serial number is valid");
|
||||
return;
|
||||
}
|
||||
|
||||
final int foundSerial = getSerialNumber(file);
|
||||
Slog.v(TAG, "Found " + file + " with serial number " + foundSerial);
|
||||
|
||||
if (foundSerial == -1) {
|
||||
Slog.d(TAG, "Serial number missing on " + file + "; assuming current is valid");
|
||||
try {
|
||||
setSerialNumber(file, serialNumber);
|
||||
} catch (IOException e) {
|
||||
Slog.w(TAG, "Failed to set serial number on " + file, e);
|
||||
}
|
||||
|
||||
} else if (foundSerial != serialNumber) {
|
||||
throw new IOException("Found serial number " + foundSerial
|
||||
+ " doesn't match expected " + serialNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set serial number stored in user directory inode.
|
||||
*
|
||||
* @throws IOException if serial number was already set
|
||||
*/
|
||||
private static void setSerialNumber(File file, int serialNumber) throws IOException {
|
||||
try {
|
||||
final byte[] buf = Integer.toString(serialNumber).getBytes(StandardCharsets.UTF_8);
|
||||
Os.setxattr(file.getAbsolutePath(), XATTR_SERIAL, buf, OsConstants.XATTR_CREATE);
|
||||
} catch (ErrnoException e) {
|
||||
throw e.rethrowAsIOException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return serial number stored in user directory inode.
|
||||
*
|
||||
* @return parsed serial number, or -1 if not set
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static int getSerialNumber(File file) throws IOException {
|
||||
try {
|
||||
final byte[] buf = Os.getxattr(file.getAbsolutePath(), XATTR_SERIAL);
|
||||
final String serial = new String(buf);
|
||||
try {
|
||||
return Integer.parseInt(serial);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IOException("Bad serial number: " + serial);
|
||||
}
|
||||
} catch (ErrnoException e) {
|
||||
if (e.errno == OsConstants.ENODATA) {
|
||||
return -1;
|
||||
} else {
|
||||
throw e.rethrowAsIOException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -218,8 +218,6 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
static final int WRITE_USER_MSG = 1;
|
||||
static final int WRITE_USER_DELAY = 2*1000; // 2 seconds
|
||||
|
||||
private static final String XATTR_SERIAL = "user.serial";
|
||||
|
||||
// Tron counters
|
||||
private static final String TRON_GUEST_CREATED = "users_guest_created";
|
||||
private static final String TRON_USER_CREATED = "users_user_created";
|
||||
@@ -3158,6 +3156,15 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine all users present on given mounted volume, and destroy data
|
||||
* belonging to users that are no longer valid, or whose user ID has been
|
||||
* recycled.
|
||||
*/
|
||||
void reconcileUsers(String volumeUuid) {
|
||||
mUserDataPreparer.reconcileUsers(volumeUuid, getUsers(true /* excludeDying */));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a note of the last started time of a user and do some cleanup.
|
||||
* This is called with ActivityManagerService lock held.
|
||||
@@ -3219,78 +3226,6 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
return RESTRICTIONS_FILE_PREFIX + packageName + XML_SUFFIX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforce that serial number stored in user directory inode matches the
|
||||
* given expected value. Gracefully sets the serial number if currently
|
||||
* undefined.
|
||||
*
|
||||
* @throws IOException when problem extracting serial number, or serial
|
||||
* number is mismatched.
|
||||
*/
|
||||
public static void enforceSerialNumber(File file, int serialNumber) throws IOException {
|
||||
if (StorageManager.isFileEncryptedEmulatedOnly()) {
|
||||
// When we're emulating FBE, the directory may have been chmod
|
||||
// 000'ed, meaning we can't read the serial number to enforce it;
|
||||
// instead of destroying the user, just log a warning.
|
||||
Slog.w(LOG_TAG, "Device is emulating FBE; assuming current serial number is valid");
|
||||
return;
|
||||
}
|
||||
|
||||
final int foundSerial = getSerialNumber(file);
|
||||
Slog.v(LOG_TAG, "Found " + file + " with serial number " + foundSerial);
|
||||
|
||||
if (foundSerial == -1) {
|
||||
Slog.d(LOG_TAG, "Serial number missing on " + file + "; assuming current is valid");
|
||||
try {
|
||||
setSerialNumber(file, serialNumber);
|
||||
} catch (IOException e) {
|
||||
Slog.w(LOG_TAG, "Failed to set serial number on " + file, e);
|
||||
}
|
||||
|
||||
} else if (foundSerial != serialNumber) {
|
||||
throw new IOException("Found serial number " + foundSerial
|
||||
+ " doesn't match expected " + serialNumber);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set serial number stored in user directory inode.
|
||||
*
|
||||
* @throws IOException if serial number was already set
|
||||
*/
|
||||
private static void setSerialNumber(File file, int serialNumber)
|
||||
throws IOException {
|
||||
try {
|
||||
final byte[] buf = Integer.toString(serialNumber).getBytes(StandardCharsets.UTF_8);
|
||||
Os.setxattr(file.getAbsolutePath(), XATTR_SERIAL, buf, OsConstants.XATTR_CREATE);
|
||||
} catch (ErrnoException e) {
|
||||
throw e.rethrowAsIOException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return serial number stored in user directory inode.
|
||||
*
|
||||
* @return parsed serial number, or -1 if not set
|
||||
*/
|
||||
private static int getSerialNumber(File file) throws IOException {
|
||||
try {
|
||||
final byte[] buf = Os.getxattr(file.getAbsolutePath(), XATTR_SERIAL);
|
||||
final String serial = new String(buf);
|
||||
try {
|
||||
return Integer.parseInt(serial);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IOException("Bad serial number: " + serial);
|
||||
}
|
||||
} catch (ErrnoException e) {
|
||||
if (e.errno == OsConstants.ENODATA) {
|
||||
return -1;
|
||||
} else {
|
||||
throw e.rethrowAsIOException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeedAccountData(int userId, String accountName, String accountType,
|
||||
PersistableBundle accountOptions, boolean persist) {
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* 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 com.android.server.pm;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.FileUtils;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.storage.VolumeInfo;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Matchers.isNull;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* <p>Run with:<pre>
|
||||
* m FrameworksServicesTests &&
|
||||
* adb install \
|
||||
* -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
|
||||
* adb shell am instrument -e class com.android.server.pm.UserDataPreparerTest \
|
||||
* -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
|
||||
* </pre>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
public class UserDataPreparerTest {
|
||||
|
||||
private static final int TEST_USER_SERIAL = 1000;
|
||||
private static final int TEST_USER_ID = 10;
|
||||
|
||||
private TestUserDataPreparer mUserDataPreparer;
|
||||
|
||||
@Mock
|
||||
private StorageManager mStorageManagerMock;
|
||||
|
||||
@Mock
|
||||
private Context mContextMock;
|
||||
|
||||
@Mock
|
||||
private Installer mInstaller;
|
||||
|
||||
private Object mInstallLock;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Context ctx = InstrumentationRegistry.getContext();
|
||||
FileUtils.deleteContents(ctx.getCacheDir());
|
||||
mInstallLock = new Object();
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mUserDataPreparer = new TestUserDataPreparer(mInstaller, mInstallLock, mContextMock, false,
|
||||
ctx.getCacheDir());
|
||||
when(mContextMock.getSystemServiceName(StorageManager.class))
|
||||
.thenReturn(Context.STORAGE_SERVICE);
|
||||
when(mContextMock.getSystemService(eq(Context.STORAGE_SERVICE)))
|
||||
.thenReturn(mStorageManagerMock);
|
||||
VolumeInfo testVolume = new VolumeInfo("testuuid", VolumeInfo.TYPE_PRIVATE, null, null);
|
||||
when(mStorageManagerMock.getWritablePrivateVolumes()).thenReturn(Arrays.asList(testVolume));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrepareUserData_De() throws Exception {
|
||||
File userDeDir = mUserDataPreparer.getDataUserDeDirectory(null, TEST_USER_ID);
|
||||
userDeDir.mkdirs();
|
||||
File systemDeDir = mUserDataPreparer.getDataSystemDeDirectory(TEST_USER_ID);
|
||||
systemDeDir.mkdirs();
|
||||
mUserDataPreparer
|
||||
.prepareUserData(TEST_USER_ID, TEST_USER_SERIAL, StorageManager.FLAG_STORAGE_DE);
|
||||
verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
|
||||
eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE));
|
||||
verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
|
||||
eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE));
|
||||
int serialNumber = UserDataPreparer.getSerialNumber(userDeDir);
|
||||
assertEquals(TEST_USER_SERIAL, serialNumber);
|
||||
serialNumber = UserDataPreparer.getSerialNumber(systemDeDir);
|
||||
assertEquals(TEST_USER_SERIAL, serialNumber);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrepareUserData_Ce() throws Exception {
|
||||
File userCeDir = mUserDataPreparer.getDataUserCeDirectory(null, TEST_USER_ID);
|
||||
userCeDir.mkdirs();
|
||||
File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID);
|
||||
systemCeDir.mkdirs();
|
||||
mUserDataPreparer
|
||||
.prepareUserData(TEST_USER_ID, TEST_USER_SERIAL, StorageManager.FLAG_STORAGE_CE);
|
||||
verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
|
||||
eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE));
|
||||
verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
|
||||
eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE));
|
||||
int serialNumber = UserDataPreparer.getSerialNumber(userCeDir);
|
||||
assertEquals(TEST_USER_SERIAL, serialNumber);
|
||||
serialNumber = UserDataPreparer.getSerialNumber(systemCeDir);
|
||||
assertEquals(TEST_USER_SERIAL, serialNumber);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDestroyUserData() throws Exception {
|
||||
// Add file in CE
|
||||
File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID);
|
||||
systemCeDir.mkdirs();
|
||||
File ceFile = new File(systemCeDir, "file");
|
||||
writeFile(ceFile, "-----" );
|
||||
testDestroyUserData_De();
|
||||
// CE directory should be preserved
|
||||
assertEquals(Collections.singletonList(ceFile), Arrays.asList(FileUtils.listFilesOrEmpty(
|
||||
systemCeDir)));
|
||||
|
||||
testDestroyUserData_Ce();
|
||||
|
||||
// Verify that testDir is empty
|
||||
assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(
|
||||
mUserDataPreparer.testDir)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDestroyUserData_De() throws Exception {
|
||||
File systemDir = mUserDataPreparer.getUserSystemDirectory(TEST_USER_ID);
|
||||
systemDir.mkdirs();
|
||||
writeFile(new File(systemDir, "file"), "-----" );
|
||||
File systemDeDir = mUserDataPreparer.getDataSystemDeDirectory(TEST_USER_ID);
|
||||
systemDeDir.mkdirs();
|
||||
writeFile(new File(systemDeDir, "file"), "-----" );
|
||||
File miscDeDir = mUserDataPreparer.getDataMiscDeDirectory(TEST_USER_ID);
|
||||
miscDeDir.mkdirs();
|
||||
writeFile(new File(miscDeDir, "file"), "-----" );
|
||||
|
||||
mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_DE);
|
||||
|
||||
verify(mInstaller).destroyUserData(isNull(String.class), eq(TEST_USER_ID),
|
||||
eq(StorageManager.FLAG_STORAGE_DE));
|
||||
verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID),
|
||||
eq(StorageManager.FLAG_STORAGE_DE));
|
||||
|
||||
assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(systemDir)));
|
||||
assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(
|
||||
systemDeDir)));
|
||||
assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(
|
||||
miscDeDir)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDestroyUserData_Ce() throws Exception {
|
||||
File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID);
|
||||
systemCeDir.mkdirs();
|
||||
writeFile(new File(systemCeDir, "file"), "-----" );
|
||||
File miscCeDir = mUserDataPreparer.getDataMiscCeDirectory(TEST_USER_ID);
|
||||
miscCeDir.mkdirs();
|
||||
writeFile(new File(miscCeDir, "file"), "-----" );
|
||||
|
||||
mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_CE);
|
||||
|
||||
verify(mInstaller).destroyUserData(isNull(String.class), eq(TEST_USER_ID),
|
||||
eq(StorageManager.FLAG_STORAGE_CE));
|
||||
verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID),
|
||||
eq(StorageManager.FLAG_STORAGE_CE));
|
||||
|
||||
assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(
|
||||
systemCeDir)));
|
||||
assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(
|
||||
miscCeDir)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReconcileUsers() throws Exception {
|
||||
UserInfo u1 = new UserInfo(1, "u1", 0);
|
||||
UserInfo u2 = new UserInfo(2, "u2", 0);
|
||||
File testDir = mUserDataPreparer.testDir;
|
||||
File dir1 = new File(testDir, "1");
|
||||
dir1.mkdirs();
|
||||
File dir2 = new File(testDir, "2");
|
||||
dir2.mkdirs();
|
||||
File dir3 = new File(testDir, "3");
|
||||
dir3.mkdirs();
|
||||
|
||||
mUserDataPreparer
|
||||
.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL, Arrays.asList(u1, u2),
|
||||
Arrays.asList(dir1, dir2, dir3));
|
||||
// Verify that user 3 data is removed
|
||||
verify(mInstaller).destroyUserData(isNull(String.class), eq(3),
|
||||
eq(StorageManager.FLAG_STORAGE_DE|StorageManager.FLAG_STORAGE_CE));
|
||||
}
|
||||
|
||||
private static void writeFile(File file, String content) throws IOException {
|
||||
try (FileOutputStream os = new FileOutputStream(file)) {
|
||||
os.write(content.getBytes(Charset.defaultCharset()));
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestUserDataPreparer extends UserDataPreparer {
|
||||
File testDir;
|
||||
|
||||
TestUserDataPreparer(Installer installer, Object installLock, Context context,
|
||||
boolean onlyCore, File testDir) {
|
||||
super(installer, installLock, context, onlyCore);
|
||||
this.testDir = testDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getDataMiscCeDirectory(int userId) {
|
||||
return new File(testDir, "misc_ce_" + userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getDataSystemCeDirectory(int userId) {
|
||||
return new File(testDir, "system_ce_" + userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getDataMiscDeDirectory(int userId) {
|
||||
return new File(testDir, "misc_de_" + userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getUserSystemDirectory(int userId) {
|
||||
return new File(testDir, "user_system_" + userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getDataUserCeDirectory(String volumeUuid, int userId) {
|
||||
return new File(testDir, "user_ce_" + userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getDataSystemDeDirectory(int userId) {
|
||||
return new File(testDir, "system_de_" + userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getDataUserDeDirectory(String volumeUuid, int userId) {
|
||||
return new File(testDir, "user_de_" + userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isFileEncryptedEmulatedOnly() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user