Merge "More robust createAppData() batching."
This commit is contained in:
@@ -23,6 +23,8 @@ import android.annotation.UserIdInt;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageStats;
|
||||
import android.os.Build;
|
||||
import android.os.CreateAppDataArgs;
|
||||
import android.os.CreateAppDataResult;
|
||||
import android.os.IBinder;
|
||||
import android.os.IBinder.DeathRecipient;
|
||||
import android.os.IInstalld;
|
||||
@@ -39,7 +41,10 @@ import dalvik.system.BlockGuard;
|
||||
import dalvik.system.VMRuntime;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class Installer extends SystemService {
|
||||
private static final String TAG = "Installer";
|
||||
@@ -176,37 +181,140 @@ public class Installer extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private static CreateAppDataArgs buildCreateAppDataArgs(String uuid, String packageName,
|
||||
int userId, int flags, int appId, String seInfo, int targetSdkVersion) {
|
||||
final CreateAppDataArgs args = new CreateAppDataArgs();
|
||||
args.uuid = uuid;
|
||||
args.packageName = packageName;
|
||||
args.userId = userId;
|
||||
args.flags = flags;
|
||||
args.appId = appId;
|
||||
args.seInfo = seInfo;
|
||||
args.targetSdkVersion = targetSdkVersion;
|
||||
return args;
|
||||
}
|
||||
|
||||
private static CreateAppDataResult buildPlaceholderCreateAppDataResult() {
|
||||
final CreateAppDataResult result = new CreateAppDataResult();
|
||||
result.ceDataInode = -1;
|
||||
result.exceptionCode = 0;
|
||||
result.exceptionMessage = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated callers are encouraged to migrate to using {@link Batch} to
|
||||
* more efficiently handle operations in bulk.
|
||||
*/
|
||||
@Deprecated
|
||||
public long createAppData(String uuid, String packageName, int userId, int flags, int appId,
|
||||
String seInfo, int targetSdkVersion) throws InstallerException {
|
||||
if (!checkBeforeRemote()) return -1;
|
||||
final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags,
|
||||
appId, seInfo, targetSdkVersion);
|
||||
final CreateAppDataResult result = createAppData(args);
|
||||
if (result.exceptionCode == 0) {
|
||||
return result.ceDataInode;
|
||||
} else {
|
||||
throw new InstallerException(result.exceptionMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args)
|
||||
throws InstallerException {
|
||||
if (!checkBeforeRemote()) {
|
||||
return buildPlaceholderCreateAppDataResult();
|
||||
}
|
||||
try {
|
||||
return mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
|
||||
targetSdkVersion);
|
||||
return mInstalld.createAppData(args);
|
||||
} catch (Exception e) {
|
||||
throw InstallerException.from(e);
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull CreateAppDataResult[] createAppDataBatched(@NonNull CreateAppDataArgs[] args)
|
||||
throws InstallerException {
|
||||
if (!checkBeforeRemote()) {
|
||||
final CreateAppDataResult[] results = new CreateAppDataResult[args.length];
|
||||
Arrays.fill(results, buildPlaceholderCreateAppDataResult());
|
||||
return results;
|
||||
}
|
||||
try {
|
||||
return mInstalld.createAppDataBatched(args);
|
||||
} catch (Exception e) {
|
||||
throw InstallerException.from(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batched version of createAppData for use with multiple packages.
|
||||
* Class that collects multiple {@code installd} operations together in an
|
||||
* attempt to more efficiently execute them in bulk.
|
||||
* <p>
|
||||
* Instead of returning results immediately, {@link CompletableFuture}
|
||||
* instances are returned which can be used to chain follow-up work for each
|
||||
* request.
|
||||
* <p>
|
||||
* The creator of this object <em>must</em> invoke {@link #execute()}
|
||||
* exactly once to begin execution of all pending operations. Once execution
|
||||
* has been kicked off, no additional events can be enqueued into this
|
||||
* instance, but multiple instances can safely exist in parallel.
|
||||
*/
|
||||
public void createAppDataBatched(String[] uuids, String[] packageNames, int userId, int flags,
|
||||
int[] appIds, String[] seInfos, int[] targetSdkVersions) throws InstallerException {
|
||||
if (!checkBeforeRemote()) return;
|
||||
final int batchSize = 256;
|
||||
for (int i = 0; i < uuids.length; i += batchSize) {
|
||||
int to = i + batchSize;
|
||||
if (to > uuids.length) {
|
||||
to = uuids.length;
|
||||
}
|
||||
public static class Batch {
|
||||
private static final int CREATE_APP_DATA_BATCH_SIZE = 256;
|
||||
|
||||
try {
|
||||
mInstalld.createAppDataBatched(Arrays.copyOfRange(uuids, i, to),
|
||||
Arrays.copyOfRange(packageNames, i, to), userId, flags,
|
||||
Arrays.copyOfRange(appIds, i, to), Arrays.copyOfRange(seInfos, i, to),
|
||||
Arrays.copyOfRange(targetSdkVersions, i, to));
|
||||
} catch (Exception e) {
|
||||
throw InstallerException.from(e);
|
||||
private boolean mExecuted;
|
||||
|
||||
private final List<CreateAppDataArgs> mArgs = new ArrayList<>();
|
||||
private final List<CompletableFuture<Long>> mFutures = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Enqueue the given {@code installd} operation to be executed in the
|
||||
* future when {@link #execute(Installer)} is invoked.
|
||||
* <p>
|
||||
* Callers of this method are not required to hold a monitor lock on an
|
||||
* {@link Installer} object.
|
||||
*/
|
||||
public synchronized @NonNull CompletableFuture<Long> createAppData(String uuid,
|
||||
String packageName, int userId, int flags, int appId, String seInfo,
|
||||
int targetSdkVersion) {
|
||||
if (mExecuted) throw new IllegalStateException();
|
||||
|
||||
final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags,
|
||||
appId, seInfo, targetSdkVersion);
|
||||
final CompletableFuture<Long> future = new CompletableFuture<>();
|
||||
mArgs.add(args);
|
||||
mFutures.add(future);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute all pending {@code installd} operations that have been
|
||||
* collected by this batch in a blocking fashion.
|
||||
* <p>
|
||||
* Callers of this method <em>must</em> hold a monitor lock on the given
|
||||
* {@link Installer} object.
|
||||
*/
|
||||
public synchronized void execute(@NonNull Installer installer) throws InstallerException {
|
||||
if (mExecuted) throw new IllegalStateException();
|
||||
mExecuted = true;
|
||||
|
||||
final int size = mArgs.size();
|
||||
for (int i = 0; i < size; i += CREATE_APP_DATA_BATCH_SIZE) {
|
||||
final CreateAppDataArgs[] args = new CreateAppDataArgs[Math.min(size - i,
|
||||
CREATE_APP_DATA_BATCH_SIZE)];
|
||||
for (int j = 0; j < args.length; j++) {
|
||||
args[j] = mArgs.get(i + j);
|
||||
}
|
||||
final CreateAppDataResult[] results = installer.createAppDataBatched(args);
|
||||
for (int j = 0; j < args.length; j++) {
|
||||
final CreateAppDataResult result = results[j];
|
||||
final CompletableFuture<Long> future = mFutures.get(i + j);
|
||||
if (result.exceptionCode == 0) {
|
||||
future.complete(result.ceDataInode);
|
||||
} else {
|
||||
future.completeExceptionally(
|
||||
new InstallerException(result.exceptionMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +418,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -3481,6 +3482,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
return;
|
||||
}
|
||||
int count = 0;
|
||||
final Installer.Batch batch = new Installer.Batch();
|
||||
for (String pkgName : deferPackages) {
|
||||
AndroidPackage pkg = null;
|
||||
synchronized (mLock) {
|
||||
@@ -3490,13 +3492,14 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
if (pkg != null) {
|
||||
synchronized (mInstallLock) {
|
||||
prepareAppDataAndMigrateLIF(pkg, UserHandle.USER_SYSTEM, storageFlags,
|
||||
true /* maybeMigrateAppData */);
|
||||
}
|
||||
prepareAppDataAndMigrate(batch, pkg, UserHandle.USER_SYSTEM, storageFlags,
|
||||
true /* maybeMigrateAppData */);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
synchronized (mInstallLock) {
|
||||
executeBatchLI(batch);
|
||||
}
|
||||
traceLog.traceEnd();
|
||||
Slog.i(TAG, "Deferred reconcileAppsData finished " + count + " packages");
|
||||
}, "prepareAppData");
|
||||
@@ -22743,6 +22746,14 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private void executeBatchLI(@NonNull Installer.Batch batch) {
|
||||
try {
|
||||
batch.execute(mInstaller);
|
||||
} catch (InstallerException e) {
|
||||
Slog.w(TAG, "Failed to execute pending operations", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine all apps present on given mounted volume, and destroy apps that
|
||||
* aren't expected, either due to uninstallation or reinstallation on
|
||||
@@ -22882,6 +22893,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
// Ensure that data directories are ready to roll for all packages
|
||||
// installed for this volume and user
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "prepareAppDataAndMigrate");
|
||||
Installer.Batch batch = new Installer.Batch();
|
||||
final List<PackageSetting> packages;
|
||||
synchronized (mLock) {
|
||||
packages = mSettings.getVolumePackagesLPr(volumeUuid);
|
||||
@@ -22902,10 +22915,12 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
|
||||
if (ps.getInstalled(userId)) {
|
||||
prepareAppDataAndMigrateLIF(ps.pkg, userId, flags, migrateAppData);
|
||||
prepareAppDataAndMigrate(batch, ps.pkg, userId, flags, migrateAppData);
|
||||
preparedCount++;
|
||||
}
|
||||
}
|
||||
executeBatchLI(batch);
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
|
||||
Slog.v(TAG, "reconcileAppsData finished " + preparedCount + " packages");
|
||||
return result;
|
||||
@@ -22930,6 +22945,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
mSettings.writeKernelMappingLPr(ps);
|
||||
}
|
||||
|
||||
Installer.Batch batch = new Installer.Batch();
|
||||
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
|
||||
StorageManagerInternal smInternal = mInjector.getStorageManagerInternal();
|
||||
for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
|
||||
@@ -22944,16 +22960,20 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
if (ps.getInstalled(user.id)) {
|
||||
// TODO: when user data is locked, mark that we're still dirty
|
||||
prepareAppDataLIF(pkg, user.id, flags);
|
||||
|
||||
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
|
||||
// Prepare app data on external storage; currently this is used to
|
||||
// setup any OBB dirs that were created by the installer correctly.
|
||||
int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
|
||||
smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid);
|
||||
}
|
||||
prepareAppData(batch, pkg, user.id, flags).thenRun(() -> {
|
||||
// Note: this code block is executed with the Installer lock
|
||||
// already held, since it's invoked as a side-effect of
|
||||
// executeBatchLI()
|
||||
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
|
||||
// Prepare app data on external storage; currently this is used to
|
||||
// setup any OBB dirs that were created by the installer correctly.
|
||||
int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
|
||||
smInternal.prepareAppDataAfterInstall(pkg.getPackageName(), uid);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
executeBatchLI(batch);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -22964,26 +22984,33 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
* will try recovering system apps by wiping data; third-party app data is
|
||||
* left intact.
|
||||
*/
|
||||
private void prepareAppDataLIF(AndroidPackage pkg, int userId, int flags) {
|
||||
private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch,
|
||||
@Nullable AndroidPackage pkg, int userId, int flags) {
|
||||
if (pkg == null) {
|
||||
Slog.wtf(TAG, "Package was null!", new Throwable());
|
||||
return;
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
prepareAppDataLeafLIF(pkg, userId, flags);
|
||||
return prepareAppDataLeaf(batch, pkg, userId, flags);
|
||||
}
|
||||
|
||||
private void prepareAppDataAndMigrateLIF(AndroidPackage pkg, int userId, int flags,
|
||||
boolean maybeMigrateAppData) {
|
||||
prepareAppDataLIF(pkg, userId, flags);
|
||||
|
||||
if (maybeMigrateAppData && maybeMigrateAppDataLIF(pkg, userId)) {
|
||||
// We may have just shuffled around app data directories, so
|
||||
// prepare them one more time
|
||||
prepareAppDataLIF(pkg, userId, flags);
|
||||
}
|
||||
private @NonNull CompletableFuture<?> prepareAppDataAndMigrate(@NonNull Installer.Batch batch,
|
||||
@NonNull AndroidPackage pkg, int userId, int flags, boolean maybeMigrateAppData) {
|
||||
return prepareAppData(batch, pkg, userId, flags).thenRun(() -> {
|
||||
// Note: this code block is executed with the Installer lock
|
||||
// already held, since it's invoked as a side-effect of
|
||||
// executeBatchLI()
|
||||
if (maybeMigrateAppData && maybeMigrateAppDataLIF(pkg, userId)) {
|
||||
// We may have just shuffled around app data directories, so
|
||||
// prepare them one more time
|
||||
final Installer.Batch batchInner = new Installer.Batch();
|
||||
prepareAppData(batchInner, pkg, userId, flags);
|
||||
executeBatchLI(batchInner);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void prepareAppDataLeafLIF(AndroidPackage pkg, int userId, int flags) {
|
||||
private @NonNull CompletableFuture<?> prepareAppDataLeaf(@NonNull Installer.Batch batch,
|
||||
@NonNull AndroidPackage pkg, int userId, int flags) {
|
||||
if (DEBUG_APP_DATA) {
|
||||
Slog.v(TAG, "prepareAppData for " + pkg.getPackageName() + " u" + userId + " 0x"
|
||||
+ Integer.toHexString(flags));
|
||||
@@ -23003,57 +23030,67 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
Preconditions.checkNotNull(pkgSeInfo);
|
||||
|
||||
final String seInfo = pkgSeInfo + (pkg.getSeInfoUser() != null ? pkg.getSeInfoUser() : "");
|
||||
long ceDataInode = -1;
|
||||
try {
|
||||
ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
|
||||
appId, seInfo, pkg.getTargetSdkVersion());
|
||||
} catch (InstallerException e) {
|
||||
if (pkg.isSystem()) {
|
||||
logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
|
||||
+ ", but trying to recover: " + e);
|
||||
destroyAppDataLeafLIF(pkg, userId, flags);
|
||||
try {
|
||||
ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
|
||||
appId, seInfo, pkg.getTargetSdkVersion());
|
||||
logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
|
||||
} catch (InstallerException e2) {
|
||||
logCriticalInfo(Log.DEBUG, "Recovery failed!");
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
|
||||
}
|
||||
}
|
||||
// Prepare the application profiles only for upgrades and first boot (so that we don't
|
||||
// repeat the same operation at each boot).
|
||||
// We only have to cover the upgrade and first boot here because for app installs we
|
||||
// prepare the profiles before invoking dexopt (in installPackageLI).
|
||||
//
|
||||
// We also have to cover non system users because we do not call the usual install package
|
||||
// methods for them.
|
||||
//
|
||||
// NOTE: in order to speed up first boot time we only create the current profile and do not
|
||||
// update the content of the reference profile. A system image should already be configured
|
||||
// with the right profile keys and the profiles for the speed-profile prebuilds should
|
||||
// already be copied. That's done in #performDexOptUpgrade.
|
||||
//
|
||||
// TODO(calin, mathieuc): We should use .dm files for prebuilds profiles instead of
|
||||
// manually copying them in #performDexOptUpgrade. When we do that we should have a more
|
||||
// granular check here and only update the existing profiles.
|
||||
if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
|
||||
mArtManagerService.prepareAppProfiles(pkg, userId,
|
||||
/* updateReferenceProfileContent= */ false);
|
||||
}
|
||||
final int targetSdkVersion = pkg.getTargetSdkVersion();
|
||||
|
||||
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
|
||||
// TODO: mark this structure as dirty so we persist it!
|
||||
synchronized (mLock) {
|
||||
if (ps != null) {
|
||||
ps.setCeDataInode(ceDataInode, userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return batch.createAppData(volumeUuid, packageName, userId, flags, appId, seInfo,
|
||||
targetSdkVersion).whenComplete((ceDataInode, e) -> {
|
||||
// Note: this code block is executed with the Installer lock
|
||||
// already held, since it's invoked as a side-effect of
|
||||
// executeBatchLI()
|
||||
if ((e != null) && pkg.isSystem()) {
|
||||
logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
|
||||
+ ", but trying to recover: " + e);
|
||||
destroyAppDataLeafLIF(pkg, userId, flags);
|
||||
try {
|
||||
ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId,
|
||||
flags, appId, seInfo, pkg.getTargetSdkVersion());
|
||||
logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
|
||||
} catch (InstallerException e2) {
|
||||
logCriticalInfo(Log.DEBUG, "Recovery failed!");
|
||||
}
|
||||
} else if (e != null) {
|
||||
Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
|
||||
}
|
||||
|
||||
prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
|
||||
// Prepare the application profiles only for upgrades and
|
||||
// first boot (so that we don't repeat the same operation at
|
||||
// each boot).
|
||||
//
|
||||
// We only have to cover the upgrade and first boot here
|
||||
// because for app installs we prepare the profiles before
|
||||
// invoking dexopt (in installPackageLI).
|
||||
//
|
||||
// We also have to cover non system users because we do not
|
||||
// call the usual install package methods for them.
|
||||
//
|
||||
// NOTE: in order to speed up first boot time we only create
|
||||
// the current profile and do not update the content of the
|
||||
// reference profile. A system image should already be
|
||||
// configured with the right profile keys and the profiles
|
||||
// for the speed-profile prebuilds should already be copied.
|
||||
// That's done in #performDexOptUpgrade.
|
||||
//
|
||||
// TODO(calin, mathieuc): We should use .dm files for
|
||||
// prebuilds profiles instead of manually copying them in
|
||||
// #performDexOptUpgrade. When we do that we should have a
|
||||
// more granular check here and only update the existing
|
||||
// profiles.
|
||||
if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
|
||||
mArtManagerService.prepareAppProfiles(pkg, userId,
|
||||
/* updateReferenceProfileContent= */ false);
|
||||
}
|
||||
|
||||
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
|
||||
// TODO: mark this structure as dirty so we persist it!
|
||||
synchronized (mLock) {
|
||||
if (ps != null) {
|
||||
ps.setCeDataInode(ceDataInode, userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
|
||||
});
|
||||
}
|
||||
|
||||
private void prepareAppDataContentsLIF(AndroidPackage pkg, @Nullable PackageSetting pkgSetting,
|
||||
|
||||
@@ -102,6 +102,7 @@ import com.android.internal.util.XmlUtils;
|
||||
import com.android.permission.persistence.RuntimePermissionsPersistence;
|
||||
import com.android.permission.persistence.RuntimePermissionsState;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.pm.Installer.Batch;
|
||||
import com.android.server.pm.Installer.InstallerException;
|
||||
import com.android.server.pm.parsing.PackageInfoUtils;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
@@ -4148,24 +4149,12 @@ public final class Settings {
|
||||
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
|
||||
Trace.TRACE_TAG_PACKAGE_MANAGER);
|
||||
t.traceBegin("createNewUser-" + userHandle);
|
||||
String[] volumeUuids;
|
||||
String[] names;
|
||||
int[] appIds;
|
||||
String[] seinfos;
|
||||
int[] targetSdkVersions;
|
||||
int packagesCount;
|
||||
Installer.Batch batch = new Installer.Batch();
|
||||
final boolean skipPackageWhitelist = userTypeInstallablePackages == null;
|
||||
synchronized (mLock) {
|
||||
Collection<PackageSetting> packages = mPackages.values();
|
||||
packagesCount = packages.size();
|
||||
volumeUuids = new String[packagesCount];
|
||||
names = new String[packagesCount];
|
||||
appIds = new int[packagesCount];
|
||||
seinfos = new String[packagesCount];
|
||||
targetSdkVersions = new int[packagesCount];
|
||||
Iterator<PackageSetting> packagesIterator = packages.iterator();
|
||||
for (int i = 0; i < packagesCount; i++) {
|
||||
PackageSetting ps = packagesIterator.next();
|
||||
final int size = mPackages.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final PackageSetting ps = mPackages.valueAt(i);
|
||||
if (ps.pkg == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -4187,18 +4176,15 @@ public final class Settings {
|
||||
}
|
||||
// Need to create a data directory for all apps under this user. Accumulate all
|
||||
// required args and call the installer after mPackages lock has been released
|
||||
volumeUuids[i] = ps.volumeUuid;
|
||||
names[i] = ps.name;
|
||||
appIds[i] = ps.appId;
|
||||
seinfos[i] = AndroidPackageUtils.getSeInfo(ps.pkg, ps);
|
||||
targetSdkVersions[i] = ps.pkg.getTargetSdkVersion();
|
||||
final String seInfo = AndroidPackageUtils.getSeInfo(ps.pkg, ps);
|
||||
batch.createAppData(ps.volumeUuid, ps.name, userHandle,
|
||||
StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE, ps.appId,
|
||||
seInfo, ps.pkg.getTargetSdkVersion());
|
||||
}
|
||||
}
|
||||
t.traceBegin("createAppData");
|
||||
final int flags = StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE;
|
||||
try {
|
||||
installer.createAppDataBatched(volumeUuids, names, userHandle, flags, appIds, seinfos,
|
||||
targetSdkVersions);
|
||||
batch.execute(installer);
|
||||
} catch (InstallerException e) {
|
||||
Slog.w(TAG, "Failed to prepare app data", e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user