Merge "Check and create data and obb dirs after starting zygote process." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3b4857549e
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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();
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -2315,11 +2315,12 @@ public final class ProcessList {
|
||||
StorageManagerInternal storageManagerInternal = LocalServices.getService(
|
||||
StorageManagerInternal.class);
|
||||
if (needsStorageDataIsolation(storageManagerInternal, app)) {
|
||||
bindMountAppStorageDirs = true;
|
||||
if (pkgDataInfoMap == null ||
|
||||
!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
|
||||
app.processName)) {
|
||||
// Cannot prepare Android/app and Android/obb directory or inode == 0,
|
||||
// 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;
|
||||
} 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;
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user