Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c0ecfdb authored by Fyodor Kupolov's avatar Fyodor Kupolov
Browse files

Introduced UserDataPreparer class

Extracted the logic for preparing and destroying user data into a new
class. Unit tests will be added in a follow up cl.

Test: manual + UserManagerTest
Bug: 34736064
Change-Id: I4df7189c4ad8703cb34f54da21873b9ec83589c5
parent 766f4627
Loading
Loading
Loading
Loading
+4 −98
Original line number Diff line number Diff line
@@ -831,6 +831,7 @@ public class PackageManagerService extends IPackageManager.Stub {
    private List<String> mKeepUninstalledPackages;
    private UserManagerInternal mUserManagerInternal;
    private final UserDataPreparer mUserDataPreparer;
    private File mCacheDir;
@@ -2262,8 +2263,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");
            sUserManager = new UserManagerService(context, this, mPackages);
            mUserDataPreparer = new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore);
            sUserManager = new UserManagerService(context, this, mUserDataPreparer, mPackages);
            // Propagate permission configuration in to package manager.
            ArrayMap<String, SystemConfig.PermissionEntry> permConfig
@@ -21255,101 +21256,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
        }
    }
    /**
     * 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 && !mOnlyCore) {
                UserManagerService.enforceSerialNumber(
                        Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
                    UserManagerService.enforceSerialNumber(
                            Environment.getDataSystemDeDirectory(userId), userSerial);
                }
            }
            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && !mOnlyCore) {
                UserManagerService.enforceSerialNumber(
                        Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
                    UserManagerService.enforceSerialNumber(
                            Environment.getDataSystemCeDirectory(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,
                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
            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));
                    FileUtils.deleteContentsAndDir(Environment.getDataMiscDeDirectory(userId));
                }
                if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
                    FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
                    FileUtils.deleteContentsAndDir(Environment.getDataMiscCeDirectory(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
@@ -21397,7 +21303,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
            if (destroyUser) {
                synchronized (mInstallLock) {
                    destroyUserDataLI(volumeUuid, userId,
                    mUserDataPreparer.destroyUserDataLI(volumeUuid, userId,
                            StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
                }
            }
+139 −0
Original line number Diff line number Diff line
/*
 * 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.os.Environment;
import android.os.FileUtils;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.Log;

import java.util.Objects;

import static com.android.server.pm.PackageManagerService.logCriticalInfo;

/**
 * Helper class for preparing and destroying user storage
 */
class UserDataPreparer {
    private final Object mInstallLock;
    private final Context mContext;
    private final boolean mOnlyCore;
    private final Installer mInstaller;

    UserDataPreparer(Installer installer, Object installLock, Context context, boolean onlyCore) {
        mInstallLock = installLock;
        mContext = context;
        mOnlyCore = onlyCore;
        mInstaller = installer;
    }

    /**
     * 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 && !mOnlyCore) {
                UserManagerService.enforceSerialNumber(
                        Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
                    UserManagerService.enforceSerialNumber(
                            Environment.getDataSystemDeDirectory(userId), userSerial);
                }
            }
            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && !mOnlyCore) {
                UserManagerService.enforceSerialNumber(
                        Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
                    UserManagerService.enforceSerialNumber(
                            Environment.getDataSystemCeDirectory(userId), userSerial);
                }
            }

            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,
                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);

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

    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));
                    FileUtils.deleteContentsAndDir(Environment.getDataMiscDeDirectory(userId));
                }
                if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
                    FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
                    FileUtils.deleteContentsAndDir(Environment.getDataMiscCeDirectory(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);
        }
    }

}
+11 −8
Original line number Diff line number Diff line
@@ -227,6 +227,7 @@ public class UserManagerService extends IUserManager.Stub {
    private final Context mContext;
    private final PackageManagerService mPm;
    private final Object mPackagesLock;
    private final UserDataPreparer mUserDataPreparer;
    // Short-term lock for internal state, when interaction/sync with PM is not required
    private final Object mUsersLock = new Object();
    private final Object mRestrictionsLock = new Object();
@@ -433,7 +434,7 @@ public class UserManagerService extends IUserManager.Stub {
    // TODO b/28848102 Add support for test dependencies injection
    @VisibleForTesting
    UserManagerService(Context context) {
        this(context, null, new Object(), context.getCacheDir());
        this(context, null, null, new Object(), context.getCacheDir());
    }

    /**
@@ -441,16 +442,18 @@ public class UserManagerService extends IUserManager.Stub {
     * associated with the package manager, and the given lock is the
     * package manager's own lock.
     */
    UserManagerService(Context context, PackageManagerService pm, Object packagesLock) {
        this(context, pm, packagesLock, Environment.getDataDirectory());
    UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer,
            Object packagesLock) {
        this(context, pm, userDataPreparer, packagesLock, Environment.getDataDirectory());
    }

    private UserManagerService(Context context, PackageManagerService pm,
            Object packagesLock, File dataDir) {
            UserDataPreparer userDataPreparer, Object packagesLock, File dataDir) {
        mContext = context;
        mPm = pm;
        mPackagesLock = packagesLock;
        mHandler = new MainHandler();
        mUserDataPreparer = userDataPreparer;
        synchronized (mPackagesLock) {
            mUsersDir = new File(dataDir, USER_INFO_DIR);
            mUsersDir.mkdirs();
@@ -2494,7 +2497,7 @@ public class UserManagerService extends IUserManager.Stub {
            }
            final StorageManager storage = mContext.getSystemService(StorageManager.class);
            storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
            mPm.prepareUserData(userId, userInfo.serialNumber,
            mUserDataPreparer.prepareUserData(userId, userInfo.serialNumber,
                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
            mPm.createNewUser(userId, disallowedPackages);
            userInfo.partial = false;
@@ -2788,7 +2791,7 @@ public class UserManagerService extends IUserManager.Stub {
        mPm.cleanUpUser(this, userHandle);

        // Clean up all data before removing metadata
        mPm.destroyUserData(userHandle,
        mUserDataPreparer.destroyUserData(userHandle,
                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);

        // Remove this user from the list
@@ -3129,7 +3132,7 @@ public class UserManagerService extends IUserManager.Stub {
        final int userSerial = userInfo.serialNumber;
        // Migrate only if build fingerprints mismatch
        boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
        mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
        mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE, migrateAppsData);

        if (userId != UserHandle.USER_SYSTEM) {
@@ -3151,7 +3154,7 @@ public class UserManagerService extends IUserManager.Stub {
        final int userSerial = userInfo.serialNumber;
        // Migrate only if build fingerprints mismatch
        boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
        mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
        mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
    }