Merge "Consistent creation/destruction of user data." into nyc-dev

am: ad14b88

* commit 'ad14b884f4110e03ec7b5ba7b913be25d19aa95c':
  Consistent creation/destruction of user data.

Change-Id: I5d06aab6a0112849ba54b965c18f345f6add9f30
This commit is contained in:
Jeff Sharkey
2016-04-18 19:52:06 +00:00
committed by android-build-merger
7 changed files with 170 additions and 67 deletions

View File

@@ -437,6 +437,14 @@ public class FileUtils {
return filePath.startsWith(dirPath);
}
public static boolean deleteContentsAndDir(File dir) {
if (deleteContents(dir)) {
return dir.delete();
} else {
return false;
}
}
public static boolean deleteContents(File dir) {
File[] files = dir.listFiles();
boolean success = true;

View File

@@ -1324,6 +1324,24 @@ public interface IMountService extends IInterface {
}
}
@Override
public void destroyUserStorage(String volumeUuid, int userId, int flags)
throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(volumeUuid);
_data.writeInt(userId);
_data.writeInt(flags);
mRemote.transact(Stub.TRANSACTION_destroyUserStorage, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException {
Parcel _data = Parcel.obtain();
@@ -1465,6 +1483,7 @@ public interface IMountService extends IInterface {
static final int TRANSACTION_isUserKeyUnlocked = IBinder.FIRST_CALL_TRANSACTION + 65;
static final int TRANSACTION_prepareUserStorage = IBinder.FIRST_CALL_TRANSACTION + 66;
static final int TRANSACTION_destroyUserStorage = IBinder.FIRST_CALL_TRANSACTION + 67;
static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 68;
@@ -2096,6 +2115,15 @@ public interface IMountService extends IInterface {
reply.writeNoException();
return true;
}
case TRANSACTION_destroyUserStorage: {
data.enforceInterface(DESCRIPTOR);
String volumeUuid = data.readString();
int userId = data.readInt();
int _flags = data.readInt();
destroyUserStorage(volumeUuid, userId, _flags);
reply.writeNoException();
return true;
}
case TRANSACTION_mountAppFuse: {
data.enforceInterface(DESCRIPTOR);
String name = data.readString();
@@ -2434,6 +2462,7 @@ public interface IMountService extends IInterface {
public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
int flags) throws RemoteException;
public void destroyUserStorage(String volumeUuid, int userId, int flags) throws RemoteException;
public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
}

View File

@@ -1052,6 +1052,15 @@ public class StorageManager {
}
}
/** {@hide} */
public void destroyUserStorage(String volumeUuid, int userId, int flags) {
try {
mMountService.destroyUserStorage(volumeUuid, userId, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** {@hide} */
public boolean isUserKeyUnlocked(int userId) {
try {

View File

@@ -2874,6 +2874,19 @@ class MountService extends IMountService.Stub
}
}
@Override
public void destroyUserStorage(String volumeUuid, int userId, int flags) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
try {
mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid),
userId, flags);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
@Override
public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
try {

View File

@@ -174,12 +174,13 @@ public final class Installer extends SystemService {
mInstaller.execute("destroy_app_profiles", pkgName);
}
public void createUserConfig(int userid) throws InstallerException {
mInstaller.execute("mkuserconfig", userid);
public void createUserData(String uuid, int userId, int userSerial, int flags)
throws InstallerException {
mInstaller.execute("create_user_data", uuid, userId, userSerial, flags);
}
public void removeUserDataDirs(String uuid, int userid) throws InstallerException {
mInstaller.execute("rmuser", uuid, userid);
public void destroyUserData(String uuid, int userId, int flags) throws InstallerException {
mInstaller.execute("destroy_user_data", uuid, userId, flags);
}
public void markBootComplete(String instructionSet) throws InstallerException {

View File

@@ -109,7 +109,6 @@ import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -17421,6 +17420,10 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
return true;
}
});
// Now that we're mostly running, clean up stale users and apps
reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
}
@Override
@@ -18652,15 +18655,101 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
sendResourcesChangedBroadcast(false, false, unloaded, null);
}
/**
* Prepare storage areas for given user on all mounted devices.
*/
void prepareUserData(int userId, int userSerial, int flags) {
synchronized (mInstallLock) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
}
}
}
private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
boolean allowRecover) {
// Prepare storage and verify that serial numbers are consistent; if
// there's a mismatch we need to destroy to avoid leaking data
final StorageManager storage = mContext.getSystemService(StorageManager.class);
try {
storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
UserManagerService.enforceSerialNumber(
Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
UserManagerService.enforceSerialNumber(
Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
}
synchronized (mInstallLock) {
mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
}
} catch (Exception e) {
logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
+ " because we failed to prepare: " + e);
destroyUserDataLI(volumeUuid, userId, flags);
if (allowRecover) {
// Try one last time; if we fail again we're really in trouble
prepareUserDataLI(volumeUuid, userId, userSerial, flags, false);
}
}
}
/**
* Destroy storage areas for given user on all mounted devices.
*/
void destroyUserData(int userId, int flags) {
synchronized (mInstallLock) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
destroyUserDataLI(volumeUuid, userId, flags);
}
}
}
private void destroyUserDataLI(String volumeUuid, int userId, int flags) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
try {
// Clean up app data, profile data, and media data
mInstaller.destroyUserData(volumeUuid, userId, flags);
// 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));
}
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
}
}
// Data with special labels is now gone, so finish the job
storage.destroyUserStorage(volumeUuid, userId, flags);
} catch (Exception e) {
logCriticalInfo(Log.WARN,
"Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
}
}
/**
* 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) {
// TODO: also reconcile DE directories
final File[] files = FileUtils
.listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid));
final List<File> files = new ArrayList<>();
Collections.addAll(files, FileUtils
.listFilesOrEmpty(Environment.getDataUserDeDirectory(volumeUuid)));
Collections.addAll(files, FileUtils
.listFilesOrEmpty(Environment.getDataUserCeDirectory(volumeUuid)));
for (File file : files) {
if (!file.isDirectory()) continue;
@@ -18691,11 +18780,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
if (destroyUser) {
synchronized (mInstallLock) {
try {
mInstaller.removeUserDataDirs(volumeUuid, userId);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to clean up user dirs", e);
}
destroyUserDataLI(volumeUuid, userId,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
}
}
}
@@ -19490,21 +19576,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
mSettings.removeUserLPw(userHandle);
mPendingBroadcasts.remove(userHandle);
mEphemeralApplicationRegistry.onUserRemovedLPw(userHandle);
}
synchronized (mInstallLock) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
try {
mInstaller.removeUserDataDirs(volumeUuid, userHandle);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to remove user data", e);
}
}
synchronized (mPackages) {
removeUnusedPackagesLILPw(userManager, userHandle);
}
removeUnusedPackagesLPw(userManager, userHandle);
}
}
@@ -19513,7 +19585,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
* that are no longer in use by any other user.
* @param userHandle the user being removed
*/
private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) {
final boolean DEBUG_CLEAN_APKS = false;
int [] users = userManager.getUserIds();
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
@@ -19563,11 +19635,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
/** Called by UserManagerService */
void createNewUser(int userHandle) {
synchronized (mInstallLock) {
try {
mInstaller.createUserConfig(userHandle);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to create user config", e);
}
mSettings.createNewUserLI(this, mInstaller, userHandle);
}
synchronized (mPackages) {

View File

@@ -65,7 +65,6 @@ import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -2060,7 +2059,7 @@ public class UserManagerService extends IUserManager.Stub {
}
final StorageManager storage = mContext.getSystemService(StorageManager.class);
storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
prepareUserStorage(userId, userInfo.serialNumber,
mPm.prepareUserData(userId, userInfo.serialNumber,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
mPm.createNewUser(userId);
userInfo.partial = false;
@@ -2300,9 +2299,9 @@ public class UserManagerService extends IUserManager.Stub {
Slog.i(LOG_TAG,
"Destroying key for user " + userHandle + " failed, continuing anyway", e);
}
// Cleanup package manager settings
mPm.cleanUpUser(this, userHandle);
// Remove this user from the list
synchronized (mUsersLock) {
mUsers.remove(userHandle);
@@ -2322,24 +2321,12 @@ public class UserManagerService extends IUserManager.Stub {
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
userFile.delete();
updateUserIds();
File userDir = Environment.getUserSystemDirectory(userHandle);
File renamedUserDir = Environment.getUserSystemDirectory(UserHandle.USER_NULL - userHandle);
if (userDir.renameTo(renamedUserDir)) {
removeDirectoryRecursive(renamedUserDir);
} else {
removeDirectoryRecursive(userDir);
}
}
private void removeDirectoryRecursive(File parent) {
if (parent.isDirectory()) {
String[] files = parent.list();
for (String filename : files) {
File child = new File(parent, filename);
removeDirectoryRecursive(child);
}
}
parent.delete();
// Now that we've purged all the metadata above, destroy the actual data
// on disk; if we battery pull in here we'll finish cleaning up when
// reconciling after reboot.
mPm.destroyUserData(userHandle,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
}
private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
@@ -2633,24 +2620,13 @@ public class UserManagerService extends IUserManager.Stub {
}
}
/**
* Prepare storage areas for given user on all mounted devices.
*/
private void prepareUserStorage(int userId, int userSerial, int flags) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
}
}
/**
* Called right before a user is started. This gives us a chance to prepare
* app storage and apply any user restrictions.
*/
public void onBeforeStartUser(int userId) {
final int userSerial = getUserSerialNumber(userId);
prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE);
if (userId != UserHandle.USER_SYSTEM) {
@@ -2666,7 +2642,7 @@ public class UserManagerService extends IUserManager.Stub {
*/
public void onBeforeUnlockUser(@UserIdInt int userId) {
final int userSerial = getUserSerialNumber(userId);
prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE);
}