core/java/com/android/internal/policy/IKeyguardExitCallback.aidl \
core/java/com/android/internal/policy/IKeyguardService.aidl \
core/java/com/android/internal/os/IDropBoxManagerService.aidl \
+ core/java/com/android/internal/os/IParcelFileDescriptorFactory.aidl \
core/java/com/android/internal/os/IResultReceiver.aidl \
core/java/com/android/internal/statusbar/IStatusBar.aidl \
core/java/com/android/internal/statusbar/IStatusBarService.aidl \
public static class PackageLite {
public final String packageName;
public final int versionCode;
+ public final int installLocation;
+ public final VerifierInfo[] verifiers;
/** Names of any split APKs, ordered by parsed splitName */
public final String[] splitNames;
/** Paths of any split APKs, ordered by parsed splitName */
public final String[] splitCodePaths;
- private PackageLite(String packageName, int versionCode, String[] splitNames,
- String codePath, String baseCodePath, String[] splitCodePaths) {
- this.packageName = packageName;
- this.versionCode = versionCode;
+ private PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
+ String[] splitCodePaths) {
+ this.packageName = baseApk.packageName;
+ this.versionCode = baseApk.versionCode;
+ this.installLocation = baseApk.installLocation;
+ this.verifiers = baseApk.verifiers;
this.splitNames = splitNames;
this.codePath = codePath;
- this.baseCodePath = baseCodePath;
+ this.baseCodePath = baseApk.codePath;
this.splitCodePaths = splitCodePaths;
}
* Lightweight parsed details about a single APK file.
*/
public static class ApkLite {
+ public final String codePath;
public final String packageName;
public final String splitName;
public final int versionCode;
public final VerifierInfo[] verifiers;
public final Signature[] signatures;
- public ApkLite(String packageName, String splitName, int versionCode,
+ public ApkLite(String codePath, String packageName, String splitName, int versionCode,
int installLocation, List<VerifierInfo> verifiers, Signature[] signatures) {
+ this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
this.versionCode = versionCode;
private static PackageLite parseMonolithicPackageLite(File packageFile, int flags)
throws PackageParserException {
- final ApkLite lite = parseApkLite(packageFile, flags);
+ final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
- return new PackageLite(lite.packageName, lite.versionCode, null,
- packagePath, packagePath, null);
+ return new PackageLite(packagePath, baseApk, null, null);
}
private static PackageLite parseClusterPackageLite(File packageDir, int flags)
String packageName = null;
int versionCode = 0;
- final ArrayMap<String, File> apks = new ArrayMap<>();
+ final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
for (File file : files) {
if (isApkFile(file)) {
final ApkLite lite = parseApkLite(file, flags);
}
// Assert that each split is defined only once
- if (apks.put(lite.splitName, file) != null) {
+ if (apks.put(lite.splitName, lite) != null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Split name " + lite.splitName
+ " defined more than once; most recent was " + file);
}
}
- final File baseApk = apks.remove(null);
+ final ApkLite baseApk = apks.remove(null);
if (baseApk == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Missing base APK in " + packageDir);
Arrays.sort(splitNames, sSplitNameComparator);
for (int i = 0; i < size; i++) {
- splitCodePaths[i] = apks.get(splitNames[i]).getAbsolutePath();
+ splitCodePaths[i] = apks.get(splitNames[i]).codePath;
}
}
final String codePath = packageDir.getAbsolutePath();
- final String baseCodePath = baseApk.getAbsolutePath();
- return new PackageLite(packageName, versionCode, splitNames, codePath, baseCodePath,
- splitCodePaths);
+ return new PackageLite(codePath, baseApk, splitNames, splitCodePaths);
}
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
}
final AttributeSet attrs = parser;
- return parseApkLite(res, parser, attrs, flags, signatures);
+ return parseApkLite(apkPath, res, parser, attrs, flags, signatures);
} catch (XmlPullParserException | IOException | RuntimeException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
(splitName != null) ? splitName.intern() : splitName);
}
- private static ApkLite parseApkLite(Resources res, XmlPullParser parser,
+ private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParser parser,
AttributeSet attrs, int flags, Signature[] signatures) throws IOException,
XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
}
}
- return new ApkLite(packageSplit.first, packageSplit.second, versionCode,
+ return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
installLocation, verifiers, signatures);
}
package com.android.internal.app;
-import android.os.ParcelFileDescriptor;
+import com.android.internal.os.IParcelFileDescriptorFactory;
import android.content.pm.PackageInfoLite;
import android.content.res.ObbInfo;
interface IMediaContainerService {
- String copyResourceToContainer(String packagePath, String containerId, String key,
- String resFileName, String publicResFileName, boolean isExternal,
- boolean isForwardLocked, String abiOverride);
- int copyResource(String packagePath, in ParcelFileDescriptor outStream);
+ String copyPackageToContainer(String packagePath, String containerId, String key,
+ boolean isExternal, boolean isForwardLocked, String abiOverride);
+ int copyPackage(String packagePath, in IParcelFileDescriptorFactory target);
+
PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, long threshold,
String abiOverride);
boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked, long threshold);
final long[] apkHandles;
public static Handle create(File packageFile) throws IOException {
- final PackageLite lite;
try {
- lite = PackageParser.parsePackageLite(packageFile, 0);
+ final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
+ return create(lite);
} catch (PackageParserException e) {
throw new IOException("Failed to parse package: " + packageFile, e);
}
+ }
+ public static Handle create(PackageLite lite) throws IOException {
final List<String> codePaths = lite.getAllCodePaths();
final int size = codePaths.size();
final long[] apkHandles = new long[size];
return false;
}
- public static int extractPublicFiles(String packagePath, File publicZipFile)
+ /**
+ * Extract public files for the single given APK.
+ */
+ public static int extractPublicFiles(String apkPath, File publicZipFile)
throws IOException {
final FileOutputStream fstr;
final ZipOutputStream publicZipOutStream;
int size = 0;
try {
- final ZipFile privateZip = new ZipFile(packagePath);
+ final ZipFile privateZip = new ZipFile(apkPath);
try {
// Copy manifest, resources.arsc and res directory to public zip
for (final ZipEntry zipEntry : Collections.list(privateZip.entries())) {
--- /dev/null
+/*
+ * Copyright (C) 2014 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.internal.os;
+
+import android.os.ParcelFileDescriptor;
+
+/** {@hide} */
+interface IParcelFileDescriptorFactory {
+ // NOTE: implementors should carefully sanitize the incoming name
+ // using something like FileUtils.isValidExtFilename()
+ ParcelFileDescriptor open(String name, int mode);
+}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.defcontainer" coreApp="true">
- <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/>
- <uses-permission android:name="android.permission.ACCESS_ALL_DOWNLOADS"/>
<uses-permission android:name="android.permission.ASEC_ACCESS"/>
<uses-permission android:name="android.permission.ASEC_CREATE"/>
<uses-permission android:name="android.permission.ASEC_DESTROY"/>
import android.app.IntentService;
import android.content.Intent;
import android.content.pm.IPackageManager;
-import android.content.pm.LimitedLengthInputStream;
-import android.content.pm.MacAuthenticatedInputStream;
import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.res.ObbInfo;
import android.content.res.ObbScanner;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.content.NativeLibraryHelper;
-import com.android.internal.content.NativeLibraryHelper.Handle;
import com.android.internal.content.PackageHelper;
+import com.android.internal.os.IParcelFileDescriptorFactory;
+import com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
import libcore.io.Streams;
-import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.security.DigestException;
-import java.security.GeneralSecurityException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.Mac;
-import javax.crypto.NoSuchPaddingException;
-
-/*
- * This service copies a downloaded apk to a file passed in as
- * a ParcelFileDescriptor or to a newly created container specified
- * by parameters. The DownloadManager gives access to this process
- * based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER
- * permission to access apks downloaded via the download manager.
+/**
+ * Service that offers to inspect and copy files that may reside on removable
+ * storage. This is designed to prevent the system process from holding onto
+ * open files that cause the kernel to kill it when the underlying device is
+ * removed.
*/
public class DefaultContainerService extends IntentService {
private static final String TAG = "DefContainer";
private static final String LIB_DIR_NAME = "lib";
+ // TODO: migrate native code unpacking to always be a derivative work
+
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
/**
- * Creates a new container and copies resource there.
- * @param packageURI the uri of resource to be copied. Can be either
- * a content uri or a file uri
- * @param cid the id of the secure container that should
- * be used for creating a secure container into which the resource
- * will be copied.
- * @param key Refers to key used for encrypting the secure container
- * @param resFileName Name of the target resource file(relative to newly
- * created secure container)
- * @return Returns the new cache path where the resource has been copied into
+ * Creates a new container and copies package there.
*
+ * @param packagePath absolute path to the package to be copied. Can be
+ * a single monolithic APK file or a cluster directory
+ * containing one or more APKs.
+ * @param containerId the id of the secure container that should be used
+ * for creating a secure container into which the resource
+ * will be copied.
+ * @param key Refers to key used for encrypting the secure container
+ * @return Returns the new cache path where the resource has been copied
+ * into
*/
@Override
- public String copyResourceToContainer(final String packagePath, final String cid,
- final String key, final String resFileName, final String publicResFileName,
+ public String copyPackageToContainer(String packagePath, String containerId, String key,
boolean isExternal, boolean isForwardLocked, String abiOverride) {
- if (packagePath == null || cid == null) {
+ if (packagePath == null || containerId == null) {
return null;
}
}
}
- Handle handle = null;
+ PackageLite pkg = null;
+ NativeLibraryHelper.Handle handle = null;
try {
- handle = Handle.create(new File(packagePath));
- return copyResourceInner(packagePath, cid, key, resFileName, publicResFileName,
- isExternal, isForwardLocked, handle, abiOverride);
- } catch (IOException ioe) {
- Slog.w(TAG, "Problem opening APK: " + packagePath);
+ final File packageFile = new File(packagePath);
+ pkg = PackageParser.parsePackageLite(packageFile, 0);
+ handle = NativeLibraryHelper.Handle.create(pkg);
+ return copyPackageToContainerInner(pkg, handle, containerId, key, isExternal,
+ isForwardLocked, abiOverride);
+ } catch (PackageParserException | IOException e) {
+ Slog.w(TAG, "Failed to parse package at " + packagePath);
return null;
} finally {
IoUtils.closeQuietly(handle);
}
/**
- * Copy specified resource to output stream
+ * Copy package to the target location.
*
- * @param packageURI the uri of resource to be copied. Should be a file
- * uri
- * @param encryptionParams parameters describing the encryption used for
- * this file
- * @param outStream Remote file descriptor to be used for copying
+ * @param packagePath absolute path to the package to be copied. Can be
+ * a single monolithic APK file or a cluster directory
+ * containing one or more APKs.
* @return returns status code according to those in
* {@link PackageManager}
*/
@Override
- public int copyResource(final String packagePath, ParcelFileDescriptor outStream) {
- if (packagePath == null || outStream == null) {
+ public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
+ if (packagePath == null || target == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
- InputStream in = null;
- OutputStream out = null;
+ PackageLite pkg = null;
try {
- in = new FileInputStream(packagePath);
- out = new ParcelFileDescriptor.AutoCloseOutputStream(outStream);
- Streams.copy(in, out);
- return PackageManager.INSTALL_SUCCEEDED;
- } catch (IOException e) {
- Slog.e(TAG, "Failed to copy " + packagePath, e);
+ final File packageFile = new File(packagePath);
+ pkg = PackageParser.parsePackageLite(packageFile, 0);
+ return copyPackageInner(pkg, target);
+ } catch (PackageParserException | IOException | RemoteException e) {
+ Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- } finally {
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(in);
}
}
/**
- * Determine the recommended install location for package
- * specified by file uri location.
+ * Parse given package and return minimal details.
*
- * @return Returns PackageInfoLite object containing
- * the package info and recommended app location.
+ * @param packagePath absolute path to the package to be copied. Can be
+ * a single monolithic APK file or a cluster directory
+ * containing one or more APKs.
*/
@Override
public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,
return ret;
}
- final File apkFile = new File(packagePath);
- final PackageParser.ApkLite pkg;
+ final File packageFile = new File(packagePath);
+ final PackageParser.PackageLite pkg;
try {
- pkg = PackageParser.parseApkLite(apkFile, 0);
+ pkg = PackageParser.parsePackageLite(packageFile, 0);
} catch (PackageParserException e) {
- Slog.w(TAG, "Failed to parse package");
+ Slog.w(TAG, "Failed to parse package at " + packagePath);
- if (!apkFile.exists()) {
+ if (!packageFile.exists()) {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
} else {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
ret.versionCode = pkg.versionCode;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
-
- ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,
- packagePath, flags, threshold, abiOverride);
+ ret.recommendedInstallLocation = recommendAppInstallLocation(pkg, flags, threshold,
+ abiOverride);
return ret;
}
+ /**
+ * Determine if package will fit on internal storage.
+ *
+ * @param packagePath absolute path to the package to be copied. Can be
+ * a single monolithic APK file or a cluster directory
+ * containing one or more APKs.
+ */
@Override
public boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked,
long threshold) throws RemoteException {
- final File apkFile = new File(packagePath);
+ final File packageFile = new File(packagePath);
+ final PackageParser.PackageLite pkg;
try {
- return isUnderInternalThreshold(apkFile, isForwardLocked, threshold);
- } catch (IOException e) {
- return true;
+ pkg = PackageParser.parsePackageLite(packageFile, 0);
+ return isUnderInternalThreshold(pkg, isForwardLocked, threshold);
+ } catch (PackageParserException | IOException e) {
+ Slog.w(TAG, "Failed to parse package at " + packagePath);
+ return false;
}
}
+ /**
+ * Determine if package will fit on external storage.
+ *
+ * @param packagePath absolute path to the package to be copied. Can be
+ * a single monolithic APK file or a cluster directory
+ * containing one or more APKs.
+ */
@Override
public boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked,
String abiOverride) throws RemoteException {
- final File apkFile = new File(packagePath);
+ final File packageFile = new File(packagePath);
+ final PackageParser.PackageLite pkg;
try {
- return isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride);
- } catch (IOException e) {
- return true;
+ pkg = PackageParser.parsePackageLite(packageFile, 0);
+ return isUnderExternalThreshold(pkg, isForwardLocked, abiOverride);
+ } catch (PackageParserException | IOException e) {
+ Slog.w(TAG, "Failed to parse package at " + packagePath);
+ return false;
}
}
}
}
+ /**
+ * Calculate estimated footprint of given package post-installation.
+ *
+ * @param packagePath absolute path to the package to be copied. Can be
+ * a single monolithic APK file or a cluster directory
+ * containing one or more APKs.
+ */
@Override
public long calculateInstalledSize(String packagePath, boolean isForwardLocked,
String abiOverride) throws RemoteException {
final File packageFile = new File(packagePath);
+ final PackageParser.PackageLite pkg;
try {
- return calculateContainerSize(packageFile, isForwardLocked, abiOverride) * 1024 * 1024;
- } catch (IOException e) {
+ pkg = PackageParser.parsePackageLite(packageFile, 0);
+ return calculateContainerSize(pkg, isForwardLocked, abiOverride) * 1024 * 1024;
+ } catch (PackageParserException | IOException e) {
/*
* Okay, something failed, so let's just estimate it to be 2x
* the file size. Note this will be 0 if the file doesn't exist.
}
path.delete();
}
-
+
+ @Override
public IBinder onBind(Intent intent) {
return mBinder;
}
- private String copyResourceInner(String packagePath, String newCid, String key, String resFileName,
- String publicResFileName, boolean isExternal, boolean isForwardLocked,
- Handle handle, String abiOverride) {
+ private String copyPackageToContainerInner(PackageLite pkg, NativeLibraryHelper.Handle handle,
+ String newCid, String key, boolean isExternal, boolean isForwardLocked,
+ String abiOverride) {
+ // TODO: extend to support copying all split APKs
+ if (!ArrayUtils.isEmpty(pkg.splitNames)) {
+ throw new UnsupportedOperationException("Copying split APKs not yet supported");
+ }
+
+ final String resFileName = "pkg.apk";
+ final String publicResFileName = "res.zip";
+
// The .apk file
- String codePath = packagePath;
+ String codePath = pkg.baseCodePath;
File codeFile = new File(codePath);
String[] abiList = Build.SUPPORTED_ABIS;
}
}
- final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
+ final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle, abiList);
// Calculate size of container needed to hold base APK.
final int sizeMb;
try {
- sizeMb = calculateContainerSize(handle, codeFile, abi, isForwardLocked);
+ sizeMb = calculateContainerSize(pkg, handle, isForwardLocked, abiIndex);
} catch (IOException e) {
Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath());
return null;
final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);
if (sharedLibraryDir.mkdir()) {
int ret = PackageManager.INSTALL_SUCCEEDED;
- if (abi >= 0) {
+ if (abiIndex >= 0) {
ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
- sharedLibraryDir, abiList[abi]);
- } else if (abi != PackageManager.NO_NATIVE_LIBRARIES) {
- ret = abi;
+ sharedLibraryDir, abiList[abiIndex]);
+ } else if (abiIndex != PackageManager.NO_NATIVE_LIBRARIES) {
+ ret = abiIndex;
}
if (ret != PackageManager.INSTALL_SUCCEEDED) {
return newCachePath;
}
+ private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
+ throws IOException, RemoteException {
+ // TODO: extend to support copying all split APKs
+ if (!ArrayUtils.isEmpty(pkg.splitNames)) {
+ throw new UnsupportedOperationException("Copying split APKs not yet supported");
+ }
+
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = new FileInputStream(pkg.baseCodePath);
+ out = new ParcelFileDescriptor.AutoCloseOutputStream(
+ target.open(null, ParcelFileDescriptor.MODE_READ_WRITE));
+ Streams.copy(in, out);
+ return PackageManager.INSTALL_SUCCEEDED;
+ } finally {
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(in);
+ }
+ }
+
private static final int PREFER_INTERNAL = 1;
private static final int PREFER_EXTERNAL = 2;
- private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags,
- long threshold, String abiOverride) {
+ private int recommendAppInstallLocation(PackageLite pkg, int flags, long threshold,
+ String abiOverride) {
int prefer;
boolean checkBoth = false;
}
/* No install flags. Check for manifest option. */
- if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
prefer = PREFER_INTERNAL;
break check_inner;
- } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ } else if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
prefer = PREFER_EXTERNAL;
checkBoth = true;
break check_inner;
- } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+ } else if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
// We default to preferring internal storage.
prefer = PREFER_INTERNAL;
checkBoth = true;
final boolean emulated = Environment.isExternalStorageEmulated();
- final File apkFile = new File(archiveFilePath);
-
boolean fitsOnInternal = false;
if (checkBoth || prefer == PREFER_INTERNAL) {
try {
- fitsOnInternal = isUnderInternalThreshold(apkFile, isForwardLocked, threshold);
+ fitsOnInternal = isUnderInternalThreshold(pkg, isForwardLocked, threshold);
} catch (IOException e) {
return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
}
boolean fitsOnSd = false;
if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) {
try {
- fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride);
+ fitsOnSd = isUnderExternalThreshold(pkg, isForwardLocked, abiOverride);
} catch (IOException e) {
return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
}
/**
* Measure a file to see if it fits within the free space threshold.
*
- * @param apkFile file to check
* @param threshold byte threshold to compare against
* @return true if file fits under threshold
* @throws FileNotFoundException when APK does not exist
*/
- private boolean isUnderInternalThreshold(File apkFile, boolean isForwardLocked, long threshold)
- throws IOException {
- long size = apkFile.length();
- if (size == 0 && !apkFile.exists()) {
- throw new FileNotFoundException();
- }
+ private boolean isUnderInternalThreshold(PackageLite pkg, boolean isForwardLocked,
+ long threshold) throws IOException {
+ long sizeBytes = 0;
+ for (String codePath : pkg.getAllCodePaths()) {
+ sizeBytes += new File(codePath).length();
- if (isForwardLocked) {
- size += PackageHelper.extractPublicFiles(apkFile.getAbsolutePath(), null);
+ if (isForwardLocked) {
+ sizeBytes += PackageHelper.extractPublicFiles(codePath, null);
+ }
}
- final StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
- final long availInternalSize = (long) internalStats.getAvailableBlocks()
- * (long) internalStats.getBlockSize();
-
- return (availInternalSize - size) > threshold;
+ final StatFs stat = new StatFs(Environment.getDataDirectory().getPath());
+ final long availBytes = stat.getAvailableBytes();
+ return (availBytes - sizeBytes) > threshold;
}
-
/**
* Measure a file to see if it fits in the external free space.
*
- * @param apkFile file to check
* @return true if file fits
* @throws IOException when file does not exist
*/
- private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked, String abiOverride)
- throws IOException {
+ private boolean isUnderExternalThreshold(PackageLite pkg, boolean isForwardLocked,
+ String abiOverride) throws IOException {
if (Environment.isExternalStorageEmulated()) {
return false;
}
- final int sizeMb = calculateContainerSize(apkFile, isForwardLocked, abiOverride);
+ final int sizeMb = calculateContainerSize(pkg, isForwardLocked, abiOverride);
final int availSdMb;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return availSdMb > sizeMb;
}
- private int calculateContainerSize(File apkFile, boolean forwardLocked,
- String abiOverride) throws IOException {
- Handle handle = null;
+ private int calculateContainerSize(PackageLite pkg, boolean isForwardLocked, String abiOverride)
+ throws IOException {
+ NativeLibraryHelper.Handle handle = null;
try {
- handle = Handle.create(apkFile);
+ handle = NativeLibraryHelper.Handle.create(pkg);
final int abi = NativeLibraryHelper.findSupportedAbi(handle,
(abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS);
- return calculateContainerSize(handle, apkFile, abi, forwardLocked);
+ return calculateContainerSize(pkg, handle, isForwardLocked, abi);
} finally {
IoUtils.closeQuietly(handle);
}
}
/**
- * Calculate the container size for an APK. Takes into account the
+ * Calculate the container size for a package.
*
- * @param apkFile file from which to calculate size
* @return size in megabytes (2^20 bytes)
* @throws IOException when there is a problem reading the file
*/
- private int calculateContainerSize(NativeLibraryHelper.Handle handle,
- File apkFile, int abiIndex, boolean forwardLocked) throws IOException {
- // Calculate size of container needed to hold base APK.
- long sizeBytes = apkFile.length();
- if (sizeBytes == 0 && !apkFile.exists()) {
- throw new FileNotFoundException();
+ private int calculateContainerSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
+ boolean isForwardLocked, int abiIndex) throws IOException {
+ // Calculate size of container needed to hold APKs.
+ long sizeBytes = 0;
+ for (String codePath : pkg.getAllCodePaths()) {
+ sizeBytes += new File(codePath).length();
+
+ if (isForwardLocked) {
+ sizeBytes += PackageHelper.extractPublicFiles(codePath, null);
+ }
}
// Check all the native files that need to be copied and add that to the
Build.SUPPORTED_ABIS[abiIndex]);
}
- if (forwardLocked) {
- sizeBytes += PackageHelper.extractPublicFiles(apkFile.getPath(), null);
- }
-
int sizeMb = (int) (sizeBytes >> 20);
if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) {
sizeMb++;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
+import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
+import static android.system.OsConstants.O_CREAT;
+import static android.system.OsConstants.O_EXCL;
+import static android.system.OsConstants.O_RDWR;
import static android.system.OsConstants.S_IRGRP;
import static android.system.OsConstants.S_IROTH;
import static android.system.OsConstants.S_IRWXU;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
+import com.android.internal.os.IParcelFileDescriptorFactory;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
+import libcore.io.Libcore;
/**
* Keep track of all those .apks everywhere.
if ((state != null) && !state.timeoutExtended()) {
final InstallArgs args = state.getInstallArgs();
- final Uri fromUri = Uri.fromFile(args.fromFile);
+ final Uri originUri = Uri.fromFile(args.originFile);
- Slog.i(TAG, "Verification timed out for " + fromUri);
+ Slog.i(TAG, "Verification timed out for " + originUri);
mPendingVerification.remove(verificationId);
int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
- Slog.i(TAG, "Continuing with installation of " + fromUri);
+ Slog.i(TAG, "Continuing with installation of " + originUri);
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
- broadcastPackageVerified(verificationId, fromUri,
+ broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW,
state.getInstallArgs().getUser());
try {
Slog.e(TAG, "Could not contact the ContainerService");
}
} else {
- broadcastPackageVerified(verificationId, fromUri,
+ broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_REJECT,
state.getInstallArgs().getUser());
}
mPendingVerification.remove(verificationId);
final InstallArgs args = state.getInstallArgs();
- final Uri fromUri = Uri.fromFile(args.fromFile);
+ final Uri originUri = Uri.fromFile(args.originFile);
int ret;
if (state.isInstallAllowed()) {
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- broadcastPackageVerified(verificationId, fromUri,
+ broadcastPackageVerified(verificationId, originUri,
response.code, state.getInstallArgs().getUser());
try {
ret = args.copyApk(mContainerService, true);
* copied/renamed into place. This could be a single monolithic APK
* file, or a cluster directory. This location may be untrusted.
*/
- private final File mFromFile;
+ final File originFile;
+
+ /**
+ * Flag indicating that {@link #originFile} lives in a trusted location,
+ * meaning downstream users don't need to defensively copy the contents.
+ */
+ boolean originTrusted;
final IPackageInstallObserver observer;
final IPackageInstallObserver2 observer2;
final String packageAbiOverride;
final String packageInstructionSetOverride;
- InstallParams(File fromFile, IPackageInstallObserver observer,
+ InstallParams(File originFile, IPackageInstallObserver observer,
IPackageInstallObserver2 observer2, int flags, String installerPackageName,
VerificationParams verificationParams, UserHandle user, String packageAbiOverride) {
super(user);
- mFromFile = Preconditions.checkNotNull(fromFile);
+ this.originFile = Preconditions.checkNotNull(originFile);
+ this.originTrusted = false;
this.observer = observer;
this.observer2 = observer2;
this.flags = flags;
public String toString() {
return "InstallParams{"
+ Integer.toHexString(System.identityHashCode(this))
- + " " + mFromFile + "}";
+ + " " + originFile + "}";
}
public ManifestDigest getManifestDigest() {
}
// Remote call to find out default install location
- final String fromPath = getFromFile().getAbsolutePath();
- pkgLite = mContainerService.getMinimalPackageInfo(fromPath, flags, lowThreshold,
+ final String originPath = originFile.getAbsolutePath();
+ pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags, lowThreshold,
packageAbiOverride);
/*
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
final long size = mContainerService.calculateInstalledSize(
- fromPath, isForwardLocked(), packageAbiOverride);
+ originPath, isForwardLocked(), packageAbiOverride);
if (mInstaller.freeCache(size + lowThreshold) >= 0) {
- pkgLite = mContainerService.getMinimalPackageInfo(fromPath,
- flags, lowThreshold, packageAbiOverride);
+ pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
+ lowThreshold, packageAbiOverride);
}
/*
* The cache free must have deleted the file we
// TODO: send verifier the install session instead of uri
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
- verification.setDataAndType(Uri.fromFile(getFromFile()), PACKAGE_MIME_TYPE);
+ verification.setDataAndType(Uri.fromFile(originFile), PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
final List<ResolveInfo> receivers = queryIntentReceivers(verification,
public boolean isForwardLocked() {
return (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
}
-
- public File getFromFile() {
- return mFromFile;
- }
}
/*
int mRet;
MoveParams(InstallArgs srcArgs, IPackageMoveObserver observer, int flags,
- String packageName, String dataDir, String instructionSet,
- int uid, UserHandle user) {
+ String packageName, String instructionSet, int uid, UserHandle user) {
super(user);
this.srcArgs = srcArgs;
this.observer = observer;
this.uid = uid;
if (srcArgs != null) {
final String codePath = srcArgs.getCodePath();
- targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName, dataDir,
+ targetArgs = createInstallArgsForMoveTarget(codePath, flags, packageName,
instructionSet);
} else {
targetArgs = null;
}
private InstallArgs createInstallArgsForMoveTarget(String codePath, int flags, String pkgName,
- String dataDir, String instructionSet) {
+ String instructionSet) {
final File codeFile = new File(codePath);
if (installOnSd(flags) || installForwardLocked(flags)) {
String cid = getNextCodePath(codePath, pkgName, "/"
return new AsecInstallArgs(codeFile, cid, instructionSet, installOnSd(flags),
installForwardLocked(flags));
} else {
- return new FileInstallArgs(codeFile, pkgName, dataDir, instructionSet);
+ return new FileInstallArgs(codeFile, pkgName, instructionSet);
}
}
static abstract class InstallArgs {
- /**
- * Location where install is coming from, before it has been
- * copied/renamed into place. This could be a single monolithic APK
- * file, or a cluster directory. This location is typically untrusted.
- */
- final File fromFile;
+ /** @see InstallParams#originFile */
+ final File originFile;
+ /** @see InstallParams#originTrusted */
+ final boolean originTrusted;
+
+ // TODO: define inherit location
final IPackageInstallObserver observer;
final IPackageInstallObserver2 observer2;
final String instructionSet;
final String abiOverride;
- InstallArgs(File fromFile, IPackageInstallObserver observer,
+ InstallArgs(File originFile, boolean originTrusted, IPackageInstallObserver observer,
IPackageInstallObserver2 observer2, int flags, String installerPackageName,
ManifestDigest manifestDigest, UserHandle user, String instructionSet,
String abiOverride) {
- this.fromFile = fromFile;
+ this.originFile = originFile;
+ this.originTrusted = originTrusted;
this.flags = flags;
this.observer = observer;
this.observer2 = observer2;
/** New install */
FileInstallArgs(InstallParams params) {
- super(params.getFromFile(), params.observer, params.observer2, params.flags,
- params.installerPackageName, params.getManifestDigest(), params.getUser(),
- params.packageInstructionSetOverride, params.packageAbiOverride);
+ super(params.originFile, params.originTrusted, params.observer, params.observer2,
+ params.flags, params.installerPackageName, params.getManifestDigest(),
+ params.getUser(), params.packageInstructionSetOverride,
+ params.packageAbiOverride);
}
/** Existing install */
FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
String instructionSet) {
- super(null, null, null, 0, null, null, null, instructionSet, null);
+ super(null, false, null, null, 0, null, null, null, instructionSet, null);
File codeFile = new File(fullCodePath);
installDir = codeFile.getParentFile();
codeFileName = fullCodePath;
}
/** New install from existing */
- FileInstallArgs(File fromFile, String pkgName, String dataDir, String instructionSet) {
- super(fromFile, null, null, 0, null, null, null, instructionSet, null);
+ FileInstallArgs(File originFile, String pkgName, String instructionSet) {
+ super(originFile, true, null, null, 0, null, null, null, instructionSet, null);
installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
String apkName = getNextCodePath(null, pkgName, ".apk");
codeFileName = new File(installDir, apkName + ".apk").getPath();
lowThreshold = dsm.getMemoryLowThreshold();
}
- return imcs.checkInternalFreeStorage(fromFile.getAbsolutePath(), isFwdLocked(),
+ return imcs.checkInternalFreeStorage(originFile.getAbsolutePath(), isFwdLocked(),
lowThreshold);
}
createCopyFile();
}
// Get a ParcelFileDescriptor to write to the output file
- File codeFile = new File(codeFileName);
+ final File codeFile = new File(codeFileName);
if (!created) {
try {
codeFile.createNewFile();
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
}
- ParcelFileDescriptor out = null;
- try {
- out = ParcelFileDescriptor.open(codeFile, ParcelFileDescriptor.MODE_READ_WRITE);
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "Failed to create file descriptor for : " + codeFileName);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- }
+
+ // TODO: extend to support copying into clusters
+ final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
+ @Override
+ public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
+ try {
+ return ParcelFileDescriptor.open(codeFile,
+ ParcelFileDescriptor.MODE_READ_WRITE);
+ } catch (FileNotFoundException e) {
+ throw new RemoteException(e.getMessage());
+ }
+ }
+ };
+
// Copy the resource now
- int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- try {
- ret = imcs.copyResource(fromFile.getAbsolutePath(), out);
- } finally {
- IoUtils.closeQuietly(out);
- }
+ int ret = imcs.copyPackage(originFile.getAbsolutePath(), target);
if (isFwdLocked()) {
final File destResourceFile = new File(getResourcePath());
/** New install */
AsecInstallArgs(InstallParams params) {
- super(params.getFromFile(), params.observer, params.observer2, params.flags,
- params.installerPackageName, params.getManifestDigest(), params.getUser(),
- params.packageInstructionSetOverride, params.packageAbiOverride);
+ super(params.originFile, params.originTrusted, params.observer, params.observer2,
+ params.flags, params.installerPackageName, params.getManifestDigest(),
+ params.getUser(), params.packageInstructionSetOverride,
+ params.packageAbiOverride);
}
/** Existing install */
AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath,
String instructionSet, boolean isExternal, boolean isForwardLocked) {
- super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null, instructionSet, null);
+ super(null, false, null, null, (isExternal ? INSTALL_EXTERNAL : 0)
+ | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+ instructionSet, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
}
AsecInstallArgs(String cid, String instructionSet, boolean isForwardLocked) {
- super(null, null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null, instructionSet, null);
+ super(null, false, null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
+ | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+ instructionSet, null);
this.cid = cid;
setCachePath(PackageHelper.getSdDir(cid));
}
/** New install from existing */
- AsecInstallArgs(File fromFile, String cid, String instructionSet,
+ AsecInstallArgs(File originPackageFile, String cid, String instructionSet,
boolean isExternal, boolean isForwardLocked) {
- super(fromFile, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0)
- | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0),
- null, null, null, instructionSet, null);
+ super(originPackageFile, true, null, null, (isExternal ? INSTALL_EXTERNAL : 0)
+ | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+ instructionSet, null);
this.cid = cid;
}
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
- return imcs.checkExternalFreeStorage(fromFile.getAbsolutePath(), isFwdLocked(),
+ return imcs.checkExternalFreeStorage(originFile.getAbsolutePath(), isFwdLocked(),
abiOverride);
}
PackageHelper.destroySdDir(cid);
}
- final String newCachePath = imcs.copyResourceToContainer(fromFile.getAbsolutePath(),
- cid, getEncryptKey(), RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(),
+ final String newCachePath = imcs.copyPackageToContainer(
+ originFile.getAbsolutePath(), cid, getEncryptKey(), isExternal(),
isFwdLocked(), abiOverride);
if (newCachePath != null) {
* anyway.
*/
if (returnCode != PackageManager.MOVE_SUCCEEDED) {
- processPendingMove(new MoveParams(null, observer, 0, packageName, null,
- null, -1, user),
+ processPendingMove(new MoveParams(null, observer, 0, packageName, null, -1, user),
returnCode);
} else {
Message msg = mHandler.obtainMessage(INIT_COPY);
pkg.applicationInfo.sourceDir, pkg.applicationInfo.publicSourceDir,
pkg.applicationInfo.nativeLibraryDir, instructionSet);
MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
- pkg.applicationInfo.dataDir, instructionSet, pkg.applicationInfo.uid, user);
+ instructionSet, pkg.applicationInfo.uid, user);
msg.obj = mp;
mHandler.sendMessage(msg);
}