Merge "Tests for UserDataPreparer"

This commit is contained in:
TreeHugger Robot
2017-02-06 22:43:15 +00:00
committed by Android (Google) Code Review
4 changed files with 492 additions and 146 deletions

View File

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

View File

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

View File

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

View File

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