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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user