Loading core/java/android/os/storage/StorageManagerInternal.java +5 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,11 @@ public abstract class StorageManagerInternal { void onReset(IVold vold); } /** * Return true if fuse is mounted. */ public abstract boolean isFuseMounted(int userId); /** * Create storage directories if it does not exist. * Return true if the directories were setup correctly, otherwise false. Loading core/jni/com_android_internal_os_Zygote.cpp +40 −0 Original line number Diff line number Diff line Loading @@ -184,6 +184,17 @@ static constexpr int PROCESS_PRIORITY_MIN = 19; /** The numeric value for the normal priority a process should have. */ static constexpr int PROCESS_PRIORITY_DEFAULT = 0; /** Exponential back off parameters for storage dir check. */ static constexpr unsigned int STORAGE_DIR_CHECK_RETRY_MULTIPLIER = 2; static constexpr unsigned int STORAGE_DIR_CHECK_INIT_INTERVAL_US = 50; static constexpr unsigned int STORAGE_DIR_CHECK_MAX_INTERVAL_US = 1000; /** * Lower bound time we allow storage dir check to sleep. * If it exceeds 2s, PROC_START_TIMEOUT_MSG will kill the starting app anyway, * so it's fine to assume max retries is 5 mins. */ static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 60 * 5; /** * A helper class containing accounting information for USAPs. */ Loading Loading @@ -1458,6 +1469,31 @@ static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list, } } static void WaitUntilDirReady(const std::string& target, fail_fn_t fail_fn) { unsigned int sleepIntervalUs = STORAGE_DIR_CHECK_INIT_INTERVAL_US; // This is just an approximate value as it doesn't need to be very accurate. unsigned int sleepTotalUs = 0; const char* dir_path = target.c_str(); while (sleepTotalUs < STORAGE_DIR_CHECK_TIMEOUT_US) { if (access(dir_path, F_OK) == 0) { return; } // Failed, so we add exponential backoff and retry usleep(sleepIntervalUs); sleepTotalUs += sleepIntervalUs; sleepIntervalUs = std::min<unsigned int>( sleepIntervalUs * STORAGE_DIR_CHECK_RETRY_MULTIPLIER, STORAGE_DIR_CHECK_MAX_INTERVAL_US); } // Last chance and get the latest errno if it fails. if (access(dir_path, F_OK) == 0) { return; } fail_fn(CREATE_ERROR("Error dir is not ready %s: %s", dir_path, strerror(errno))); } static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid, const char* dir_name, const char* package, fail_fn_t fail_fn) { bool hasSdcardFs = IsSdcardfsUsed(); Loading @@ -1468,6 +1504,10 @@ static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid, source = StringPrintf("/mnt/pass_through/%d/emulated/%d/%s/%s", user_id, user_id, dir_name, package); } // Directory might be not ready, as prepareStorageDirs() is running asynchronously in ProcessList, // so wait until dir is created. WaitUntilDirReady(source, fail_fn); std::string target = StringPrintf("/storage/emulated/%d/%s/%s", user_id, dir_name, package); // As the parent is mounted as tmpfs, we need to create the target dir here. Loading services/core/java/com/android/server/StorageManagerService.java +7 −0 Original line number Diff line number Diff line Loading @@ -4553,6 +4553,13 @@ class StorageManagerService extends IStorageManager.Stub private final List<StorageManagerInternal.ResetListener> mResetListeners = new ArrayList<>(); @Override public boolean isFuseMounted(int userId) { synchronized (mLock) { return mFuseMountedUser.contains(userId); } } /** * Check if fuse is running in target user, if it's running then setup its storage dirs. * Return true if storage dirs are mounted. Loading services/core/java/com/android/server/am/ProcessList.java +13 −5 Original line number Diff line number Diff line Loading @@ -2315,11 +2315,12 @@ public final class ProcessList { StorageManagerInternal storageManagerInternal = LocalServices.getService( StorageManagerInternal.class); if (needsStorageDataIsolation(storageManagerInternal, app)) { // We will run prepareStorageDirs() after we trigger zygote fork, so it won't // slow down app starting speed as those dirs might not be cached. if (pkgDataInfoMap != null && storageManagerInternal.isFuseMounted(userId)) { bindMountAppStorageDirs = true; if (pkgDataInfoMap == null || !storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), app.processName)) { // Cannot prepare Android/app and Android/obb directory or inode == 0, } else { // Fuse is not mounted or inode == 0, // so we won't mount it in zygote, but resume the mount after unlocking device. app.setBindMountPending(true); bindMountAppStorageDirs = false; Loading Loading @@ -2367,6 +2368,13 @@ public final class ProcessList { allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs, new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()}); } // This runs after Process.start() as this method may block app process starting time // if dir is not cached. Running this method after Process.start() can make it // cache the dir asynchronously, so zygote can use it without waiting for it. if (bindMountAppStorageDirs) { storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), app.processName); } checkSlow(startTime, "startProcess: returned from zygote!"); return startResult; } finally { Loading Loading
core/java/android/os/storage/StorageManagerInternal.java +5 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,11 @@ public abstract class StorageManagerInternal { void onReset(IVold vold); } /** * Return true if fuse is mounted. */ public abstract boolean isFuseMounted(int userId); /** * Create storage directories if it does not exist. * Return true if the directories were setup correctly, otherwise false. Loading
core/jni/com_android_internal_os_Zygote.cpp +40 −0 Original line number Diff line number Diff line Loading @@ -184,6 +184,17 @@ static constexpr int PROCESS_PRIORITY_MIN = 19; /** The numeric value for the normal priority a process should have. */ static constexpr int PROCESS_PRIORITY_DEFAULT = 0; /** Exponential back off parameters for storage dir check. */ static constexpr unsigned int STORAGE_DIR_CHECK_RETRY_MULTIPLIER = 2; static constexpr unsigned int STORAGE_DIR_CHECK_INIT_INTERVAL_US = 50; static constexpr unsigned int STORAGE_DIR_CHECK_MAX_INTERVAL_US = 1000; /** * Lower bound time we allow storage dir check to sleep. * If it exceeds 2s, PROC_START_TIMEOUT_MSG will kill the starting app anyway, * so it's fine to assume max retries is 5 mins. */ static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 60 * 5; /** * A helper class containing accounting information for USAPs. */ Loading Loading @@ -1458,6 +1469,31 @@ static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list, } } static void WaitUntilDirReady(const std::string& target, fail_fn_t fail_fn) { unsigned int sleepIntervalUs = STORAGE_DIR_CHECK_INIT_INTERVAL_US; // This is just an approximate value as it doesn't need to be very accurate. unsigned int sleepTotalUs = 0; const char* dir_path = target.c_str(); while (sleepTotalUs < STORAGE_DIR_CHECK_TIMEOUT_US) { if (access(dir_path, F_OK) == 0) { return; } // Failed, so we add exponential backoff and retry usleep(sleepIntervalUs); sleepTotalUs += sleepIntervalUs; sleepIntervalUs = std::min<unsigned int>( sleepIntervalUs * STORAGE_DIR_CHECK_RETRY_MULTIPLIER, STORAGE_DIR_CHECK_MAX_INTERVAL_US); } // Last chance and get the latest errno if it fails. if (access(dir_path, F_OK) == 0) { return; } fail_fn(CREATE_ERROR("Error dir is not ready %s: %s", dir_path, strerror(errno))); } static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid, const char* dir_name, const char* package, fail_fn_t fail_fn) { bool hasSdcardFs = IsSdcardfsUsed(); Loading @@ -1468,6 +1504,10 @@ static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid, source = StringPrintf("/mnt/pass_through/%d/emulated/%d/%s/%s", user_id, user_id, dir_name, package); } // Directory might be not ready, as prepareStorageDirs() is running asynchronously in ProcessList, // so wait until dir is created. WaitUntilDirReady(source, fail_fn); std::string target = StringPrintf("/storage/emulated/%d/%s/%s", user_id, dir_name, package); // As the parent is mounted as tmpfs, we need to create the target dir here. Loading
services/core/java/com/android/server/StorageManagerService.java +7 −0 Original line number Diff line number Diff line Loading @@ -4553,6 +4553,13 @@ class StorageManagerService extends IStorageManager.Stub private final List<StorageManagerInternal.ResetListener> mResetListeners = new ArrayList<>(); @Override public boolean isFuseMounted(int userId) { synchronized (mLock) { return mFuseMountedUser.contains(userId); } } /** * Check if fuse is running in target user, if it's running then setup its storage dirs. * Return true if storage dirs are mounted. Loading
services/core/java/com/android/server/am/ProcessList.java +13 −5 Original line number Diff line number Diff line Loading @@ -2315,11 +2315,12 @@ public final class ProcessList { StorageManagerInternal storageManagerInternal = LocalServices.getService( StorageManagerInternal.class); if (needsStorageDataIsolation(storageManagerInternal, app)) { // We will run prepareStorageDirs() after we trigger zygote fork, so it won't // slow down app starting speed as those dirs might not be cached. if (pkgDataInfoMap != null && storageManagerInternal.isFuseMounted(userId)) { bindMountAppStorageDirs = true; if (pkgDataInfoMap == null || !storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), app.processName)) { // Cannot prepare Android/app and Android/obb directory or inode == 0, } else { // Fuse is not mounted or inode == 0, // so we won't mount it in zygote, but resume the mount after unlocking device. app.setBindMountPending(true); bindMountAppStorageDirs = false; Loading Loading @@ -2367,6 +2368,13 @@ public final class ProcessList { allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs, new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()}); } // This runs after Process.start() as this method may block app process starting time // if dir is not cached. Running this method after Process.start() can make it // cache the dir asynchronously, so zygote can use it without waiting for it. if (bindMountAppStorageDirs) { storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), app.processName); } checkSlow(startTime, "startProcess: returned from zygote!"); return startResult; } finally { Loading