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;
}
@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();
Parcel _reply = Parcel.obtain();
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;
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();
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;
}
}
/** {@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 {
return mMountService.isUserKeyUnlocked(userId);
}
@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 {
final int uid = Binder.getCallingUid();
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 {
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;
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
}
/**
+ * 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;
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);
}
}
}
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);
}
}
* 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();
/** 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) {
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;
}
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;
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);
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) {
}
/**
- * 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) {
*/
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);
}