Loading services/core/java/com/android/server/pm/PackageManagerService.java +4 −59 Original line number Diff line number Diff line Loading @@ -833,7 +833,6 @@ public class PackageManagerService extends IPackageManager.Stub { private List<String> mKeepUninstalledPackages; private UserManagerInternal mUserManagerInternal; private final UserDataPreparer mUserDataPreparer; private File mCacheDir; Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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); } Loading Loading @@ -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) { services/core/java/com/android/server/pm/UserDataPreparer.java +206 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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)); } } Loading @@ -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(); } } } } services/core/java/com/android/server/pm/UserManagerService.java +9 −74 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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. Loading Loading @@ -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) { Loading services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java 0 → 100644 +273 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/pm/PackageManagerService.java +4 −59 Original line number Diff line number Diff line Loading @@ -833,7 +833,6 @@ public class PackageManagerService extends IPackageManager.Stub { private List<String> mKeepUninstalledPackages; private UserManagerInternal mUserManagerInternal; private final UserDataPreparer mUserDataPreparer; private File mCacheDir; Loading Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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); } Loading Loading @@ -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) {
services/core/java/com/android/server/pm/UserDataPreparer.java +206 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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)); } } Loading @@ -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(); } } } }
services/core/java/com/android/server/pm/UserManagerService.java +9 −74 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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. Loading Loading @@ -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) { Loading
services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java 0 → 100644 +273 −0 File added.Preview size limit exceeded, changes collapsed. Show changes