Merge "Install archived package." into main
diff --git a/core/java/android/content/pm/ArchivedPackageParcel.aidl b/core/java/android/content/pm/ArchivedPackageParcel.aidl
new file mode 100644
index 0000000..b34b708
--- /dev/null
+++ b/core/java/android/content/pm/ArchivedPackageParcel.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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 android.content.pm;
+
+import android.content.pm.SigningDetails;
+
+/**
+ * Contains fields required for archived package installation,
+ * i.e. installation without an APK.
+ * @hide
+ */
+parcelable ArchivedPackageParcel {
+ String packageName;
+ SigningDetails signingDetails;
+ int versionCode;
+ int versionCodeMajor;
+ int targetSdkVersion;
+ boolean clearUserDataAllowed;
+ boolean backupAllowed;
+ boolean defaultToDeviceProtectedStorage;
+ boolean requestLegacyExternalStorage;
+ boolean userDataFragile;
+ boolean clearUserDataOnFailedRestoreAllowed;
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4ed4dd3..916c249 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -22,6 +22,7 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.ChangedPackages;
import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
@@ -833,4 +834,6 @@
void registerPackageMonitorCallback(IRemoteCallback callback, int userId);
void unregisterPackageMonitorCallback(IRemoteCallback callback);
+
+ ArchivedPackageParcel getArchivedPackage(in String apkPath);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d2173a6..c384389 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1682,6 +1682,13 @@
public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
/**
+ * Flag parameter for {@link PackageInstaller.SessionParams} to indicate that this
+ * session is for archived package installation.
+ * @hide
+ */
+ public static final int INSTALL_ARCHIVED = 1 << 27;
+
+ /**
* Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
* a development-only feature and should not be used on end user devices.
*
diff --git a/core/java/android/content/pm/SigningDetails.aidl b/core/java/android/content/pm/SigningDetails.aidl
new file mode 100644
index 0000000..95f3ca7
--- /dev/null
+++ b/core/java/android/content/pm/SigningDetails.aidl
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2023 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 android.content.pm;
+
+parcelable SigningDetails;
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 269bec2..0127adc 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
@@ -184,6 +185,42 @@
mIsSdkLibrary = isSdkLibrary;
}
+ public ApkLite(String path, ArchivedPackageParcel archivedPackage) {
+ mPath = path;
+ mPackageName = archivedPackage.packageName;
+ mSplitName = null; // base.apk
+ mSplitTypes = null;
+ mFeatureSplit = false;
+ mConfigForSplit = null;
+ mUsesSplitName = null;
+ mRequiredSplitTypes = null;
+ mSplitRequired = hasAnyRequiredSplitTypes();
+ mVersionCode = archivedPackage.versionCode;
+ mVersionCodeMajor = archivedPackage.versionCodeMajor;
+ mRevisionCode = 0;
+ mInstallLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ mVerifiers = new VerifierInfo[]{};
+ mSigningDetails = archivedPackage.signingDetails;
+ mCoreApp = false;
+ mDebuggable = false;
+ mProfileableByShell = false;
+ mMultiArch = false;
+ mUse32bitAbi = false;
+ mUseEmbeddedDex = false;
+ mExtractNativeLibs = false;
+ mIsolatedSplits = false;
+ mTargetPackageName = null;
+ mOverlayIsStatic = false;
+ mOverlayPriority = 0;
+ mRequiredSystemPropertyName = null;
+ mRequiredSystemPropertyValue = null;
+ mMinSdkVersion = ApkLiteParseUtils.DEFAULT_MIN_SDK_VERSION;
+ mTargetSdkVersion = archivedPackage.targetSdkVersion;
+ mRollbackDataPolicy = 0;
+ mHasDeviceAdminReceiver = false;
+ mIsSdkLibrary = false;
+ }
+
/**
* Return {@link #mVersionCode} and {@link #mVersionCodeMajor} combined together as a
* single long value. The {@link #mVersionCodeMajor} is placed in the upper 32 bits.
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 4f6bcb6..7e67396 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -73,7 +73,7 @@
// Constants copied from services.jar side since they're not accessible
private static final String ANDROID_RES_NAMESPACE =
"http://schemas.android.com/apk/res/android";
- private static final int DEFAULT_MIN_SDK_VERSION = 1;
+ public static final int DEFAULT_MIN_SDK_VERSION = 1;
private static final int DEFAULT_TARGET_SDK_VERSION = 0;
public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
private static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index e2789c9..51dbde3 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
import com.android.internal.util.ArrayUtils;
@@ -78,6 +79,8 @@
private final int mInstallLocation;
/** Information about a package verifiers as used during package verification */
private final @NonNull VerifierInfo[] mVerifiers;
+ /** Signing-related data of an application package */
+ private final @NonNull SigningDetails mSigningDetails;
/** Indicate whether any split APKs that are features. Ordered by splitName */
private final @Nullable boolean[] mIsFeatureSplits;
@@ -123,6 +126,7 @@
mVersionCodeMajor = baseApk.getVersionCodeMajor();
mInstallLocation = baseApk.getInstallLocation();
mVerifiers = baseApk.getVerifiers();
+ mSigningDetails = baseApk.getSigningDetails();
mBaseRevisionCode = baseApk.getRevisionCode();
mCoreApp = baseApk.isCoreApp();
mDebuggable = baseApk.isDebuggable();
@@ -325,6 +329,14 @@
}
/**
+ * Signing-related data of an application package
+ */
+ @DataClass.Generated.Member
+ public @NonNull SigningDetails getSigningDetails() {
+ return mSigningDetails;
+ }
+
+ /**
* Indicate whether any split APKs that are features. Ordered by splitName
*/
@DataClass.Generated.Member
@@ -415,11 +427,10 @@
}
@DataClass.Generated(
- time = 1643132127068L,
+ time = 1693264166050L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
- inputSignatures =
- "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index f8313e7..dd04340 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1144,8 +1144,16 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
- parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
- AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
+ if (request.getPackageLite() == null || !PackageInstallerSession.isArchivedInstallation(
+ request.getInstallFlags())) {
+ // TODO: pass packageLite from install request instead of reparsing the package
+ parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
+ AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
+ } else {
+ // Archived install mode, no APK.
+ parsedPackage = pp.parsePackageFromPackageLite(request.getPackageLite(),
+ parseFlags);
+ }
} catch (PackageManagerException e) {
throw new PrepareFailure("Failed parse during installPackageLI", e);
} finally {
@@ -1547,6 +1555,7 @@
// TODO: Are these system flags actually set properly at this stage?
boolean isUpdatedSystemAppInferred =
pkgSetting != null && pkgSetting.isSystem();
+ // derivePackageAbi works OK for archived packages despite logging some errors.
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage,
systemApp, (isUpdatedSystemAppFromExistingSetting
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 6c26531..fe7c086 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -35,6 +35,7 @@
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
+import android.content.pm.parsing.PackageLite;
import android.net.Uri;
import android.os.Build;
import android.os.Process;
@@ -93,6 +94,8 @@
private int[] mNewUsers;
@Nullable
private AndroidPackage mPkg;
+ @Nullable
+ private PackageLite mPackageLite;
private int mReturnCode;
private int mInternalErrorCode;
@Nullable
@@ -142,6 +145,7 @@
params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
params.mDataLoaderType, params.mPackageSource,
params.mApplicationEnabledSettingPersistent);
+ mPackageLite = params.mPackageLite;
mPackageMetrics = new PackageMetrics(this);
mIsInstallInherit = params.mIsInherit;
mSessionId = params.mSessionId;
@@ -306,6 +310,11 @@
}
@Nullable
+ public PackageLite getPackageLite() {
+ return mPackageLite;
+ }
+
+ @Nullable
public String getTraceMethod() {
return mInstallArgs == null ? null : mInstallArgs.mTraceMethod;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 923bbab..bf88580 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -31,6 +31,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
import static android.content.pm.PackageManager.INSTALL_FAILED_PRE_APPROVAL_NOT_AVAILABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -52,6 +53,7 @@
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
import static com.android.server.pm.PackageManagerServiceUtils.isInstalledByAdb;
+import static com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
import android.Manifest;
import android.annotation.AnyThread;
@@ -838,6 +840,10 @@
params.dataLoaderParams.getComponentName().getPackageName());
}
+ static boolean isArchivedInstallation(int installFlags) {
+ return (installFlags & PackageManager.INSTALL_ARCHIVED) != 0;
+ }
+
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
@@ -896,6 +902,10 @@
return isSystemDataLoaderInstallation(this.params);
}
+ private boolean isArchivedInstallation() {
+ return isArchivedInstallation(this.params.installFlags);
+ }
+
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
@@ -1146,6 +1156,17 @@
if (isIncrementalInstallation() && !IncrementalManager.isAllowed()) {
throw new IllegalArgumentException("Incremental installation not allowed.");
}
+
+ if (isArchivedInstallation()) {
+ if (params.mode != SessionParams.MODE_FULL_INSTALL) {
+ throw new IllegalArgumentException(
+ "Archived installation can only be full install.");
+ }
+ if (!isStreamingInstallation() || !isSystemDataLoaderInstallation()) {
+ throw new IllegalArgumentException(
+ "Archived installation can only use Streaming System DataLoader.");
+ }
+ }
}
/**
@@ -1462,6 +1483,58 @@
}
@GuardedBy("mLock")
+ private List<ApkLite> getAddedApkLitesLocked() throws PackageManagerException {
+ if (!isArchivedInstallation()) {
+ List<File> files = getAddedApksLocked();
+ final List<ApkLite> result = new ArrayList<>(files.size());
+
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ for (int i = 0, size = files.size(); i < size; ++i) {
+ final ParseResult<ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
+ input.reset(), files.get(i),
+ ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
+ if (parseResult.isError()) {
+ throw new PackageManagerException(parseResult.getErrorCode(),
+ parseResult.getErrorMessage(), parseResult.getException());
+ }
+ result.add(parseResult.getResult());
+ }
+
+ return result;
+ }
+
+ InstallationFile[] files = getInstallationFilesLocked();
+ final List<ApkLite> result = new ArrayList<>(files.length);
+
+ for (int i = 0, size = files.length; i < size; ++i) {
+ File file = new File(stageDir, files[i].getName());
+ if (!sAddedApkFilter.accept(file)) {
+ continue;
+ }
+
+ final Metadata metadata;
+ try {
+ metadata = Metadata.fromByteArray(files[i].getMetadata());
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to ", e);
+ }
+ if (metadata.getMode() != Metadata.ARCHIVED) {
+ throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
+ "File metadata is not for ARCHIVED package: " + file);
+ }
+
+ var archPkg = metadata.getArchivedPackage();
+ if (archPkg.packageName == null || archPkg.signingDetails == null) {
+ throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
+ "ArchivedPackage does not contain required info: " + file);
+ }
+ result.add(new ApkLite(file.getAbsolutePath(), archPkg));
+ }
+ return result;
+ }
+
+ @GuardedBy("mLock")
private List<File> getRemovedFilesLocked() {
String[] names = getNamesLocked();
return filterFiles(stageDir, names, sRemovedFilter);
@@ -3176,7 +3249,7 @@
}
}
- final List<File> addedFiles = getAddedApksLocked();
+ final List<ApkLite> addedFiles = getAddedApkLitesLocked();
if (addedFiles.isEmpty()
&& (removeSplitList.size() == 0 || getStagedAppMetadataFile() != null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -3190,15 +3263,7 @@
final ArraySet<String> requiredSplitTypes = new ArraySet<>();
final ArrayMap<String, ApkLite> splitApks = new ArrayMap<>();
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- for (File addedFile : addedFiles) {
- final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
- addedFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
- if (result.isError()) {
- throw new PackageManagerException(result.getErrorCode(),
- result.getErrorMessage(), result.getException());
- }
-
- final ApkLite apk = result.getResult();
+ for (ApkLite apk : addedFiles) {
if (!stagedSplits.add(apk.getSplitName())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Split " + apk.getSplitName() + " was defined multiple times");
@@ -3214,7 +3279,7 @@
}
mHasDeviceAdminReceiver = apk.isHasDeviceAdminReceiver();
- assertApkConsistentLocked(String.valueOf(addedFile), apk);
+ assertApkConsistentLocked(String.valueOf(apk), apk);
// Take this opportunity to enforce uniform naming
final String targetName = ApkLiteParseUtils.splitNameToFileName(apk);
@@ -3235,7 +3300,10 @@
}
final File targetFile = new File(stageDir, targetName);
- resolveAndStageFileLocked(addedFile, targetFile, apk.getSplitName());
+ if (!isArchivedInstallation()) {
+ final File sourceFile = new File(apk.getPath());
+ resolveAndStageFileLocked(sourceFile, targetFile, apk.getSplitName());
+ }
// Base is coming from session
if (apk.getSplitName() == null) {
@@ -4005,6 +4073,11 @@
NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
}
+ // Skip native libraries processing for archival installation.
+ if (isArchivedInstallation()) {
+ return;
+ }
+
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(packageLite);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c0c3ec4..64e8f7a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -77,6 +77,7 @@
import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.Checksum;
@@ -6298,6 +6299,39 @@
}
}
+ @Override
+ public ArchivedPackageParcel getArchivedPackage(String apkPath) {
+ final ParsedPackage parsedPackage;
+ try (PackageParser2 pp = mInjector.getPreparingPackageParser()) {
+ parsedPackage = pp.parsePackage(new File(apkPath),
+ getDefParseFlags() | ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES, false);
+ } catch (PackageManagerException e) {
+ throw new IllegalArgumentException("Failed parse", e);
+ }
+
+ ArchivedPackageParcel archPkg = new ArchivedPackageParcel();
+ archPkg.packageName = parsedPackage.getPackageName();
+ archPkg.signingDetails = parsedPackage.getSigningDetails();
+
+ long longVersionCode = parsedPackage.getLongVersionCode();
+ archPkg.versionCodeMajor = (int) (longVersionCode >> 32);
+ archPkg.versionCode = (int) longVersionCode;
+
+ archPkg.targetSdkVersion = parsedPackage.getTargetSdkVersion();
+
+ // These get translated in flags important for user data management.
+ archPkg.clearUserDataAllowed = parsedPackage.isClearUserDataAllowed();
+ archPkg.backupAllowed = parsedPackage.isBackupAllowed();
+ archPkg.defaultToDeviceProtectedStorage =
+ parsedPackage.isDefaultToDeviceProtectedStorage();
+ archPkg.requestLegacyExternalStorage = parsedPackage.isRequestLegacyExternalStorage();
+ archPkg.userDataFragile = parsedPackage.isUserDataFragile();
+ archPkg.clearUserDataOnFailedRestoreAllowed =
+ parsedPackage.isClearUserDataOnFailedRestoreAllowed();
+
+ return archPkg;
+ }
+
/**
* Wait for the handler to finish handling all pending messages.
* @param timeoutMillis Maximum time in milliseconds to wait.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index db997d8..2028231 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -919,16 +919,22 @@
final File packageFile = new File(packagePath);
final long sizeBytes;
- try {
- sizeBytes = InstallLocationUtils.calculateInstalledSize(pkg, abiOverride);
- } catch (IOException e) {
- if (!packageFile.exists()) {
- ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI;
- } else {
- ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
- }
+ if (!PackageInstallerSession.isArchivedInstallation(flags)) {
+ try {
+ sizeBytes = InstallLocationUtils.calculateInstalledSize(pkg, abiOverride);
+ } catch (IOException e) {
+ if (!packageFile.exists()) {
+ ret.recommendedInstallLocation =
+ InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI;
+ } else {
+ ret.recommendedInstallLocation =
+ InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
+ }
- return ret;
+ return ret;
+ }
+ } else {
+ sizeBytes = 0;
}
final PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2304a23..d9f1df5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -43,6 +43,7 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
@@ -82,6 +83,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
@@ -105,6 +107,7 @@
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.IntArray;
+import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -246,6 +249,8 @@
return runStreamingInstall();
case "install-incremental":
return runIncrementalInstall();
+ case "install-archived":
+ return runArchivedInstall();
case "install-abandon":
case "install-destroy":
return runInstallAbandon();
@@ -1549,6 +1554,16 @@
return doRunInstall(params);
}
+ private int runArchivedInstall() throws RemoteException {
+ final InstallParams params = makeInstallParams(UNSUPPORTED_INSTALL_CMD_OPTS);
+ params.sessionParams.installFlags |= PackageManager.INSTALL_ARCHIVED;
+ if (params.sessionParams.dataLoaderParams == null) {
+ params.sessionParams.setDataLoaderParams(
+ PackageManagerShellCommandDataLoader.getStreamingDataLoaderParams(this));
+ }
+ return doRunInstall(params);
+ }
+
private int runIncrementalInstall() throws RemoteException {
final InstallParams params = makeInstallParams(UNSUPPORTED_INSTALL_CMD_OPTS);
if (params.sessionParams.dataLoaderParams == null) {
@@ -1579,6 +1594,8 @@
final boolean isStreaming = params.sessionParams.dataLoaderParams != null;
final boolean isApex =
(params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
+ final boolean installArchived =
+ (params.sessionParams.installFlags & PackageManager.INSTALL_ARCHIVED) != 0;
ArrayList<String> args = getRemainingArgs();
@@ -1595,6 +1612,13 @@
return 1;
}
+ if (installArchived) {
+ if (hasSplits) {
+ pw.println("Error: can't have SPLIT(s) for Archival install");
+ return 1;
+ }
+ }
+
if (!isStreaming) {
if (fromStdIn && hasSplits) {
pw.println("Error: can't specify SPLIT(s) along with STDIN");
@@ -1613,8 +1637,8 @@
boolean abandonSession = true;
try {
if (isStreaming) {
- if (doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex)
- != PackageInstaller.STATUS_SUCCESS) {
+ if (doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex,
+ installArchived) != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
} else {
@@ -3856,7 +3880,7 @@
}
private int doAddFiles(int sessionId, ArrayList<String> args, long sessionSizeBytes,
- boolean isApex) throws RemoteException {
+ boolean isApex, boolean installArchived) throws RemoteException {
PackageInstaller.Session session = null;
try {
session = new PackageInstaller.Session(
@@ -3865,9 +3889,17 @@
// 1. Single file from stdin.
if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
final String name = "base" + RANDOM.nextInt() + "." + (isApex ? "apex" : "apk");
- final Metadata metadata = Metadata.forStdIn(name);
- session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes,
- metadata.toByteArray(), null);
+ final long size;
+ final Metadata metadata;
+ if (!installArchived) {
+ metadata = Metadata.forStdIn(name);
+ size = sessionSizeBytes;
+ } else {
+ metadata = Metadata.forArchived(
+ getArchivedPackage(STDIN_PATH, sessionSizeBytes));
+ size = -1;
+ }
+ session.addFile(LOCATION_DATA_APP, name, size, metadata.toByteArray(), null);
return 0;
}
@@ -3876,16 +3908,21 @@
if (delimLocation != -1) {
// 2. File with specified size read from stdin.
+ if (installArchived) {
+ getOutPrintWriter().println(
+ "Error: can't install with size from STDIN for Archival install");
+ return 1;
+ }
if (processArgForStdin(arg, session) != 0) {
return 1;
}
} else {
// 3. Local file.
- processArgForLocalFile(arg, session);
+ processArgForLocalFile(arg, session, installArchived);
}
}
return 0;
- } catch (IllegalArgumentException e) {
+ } catch (IOException | IllegalArgumentException e) {
getErrPrintWriter().println("Failed to add file(s), reason: " + e);
getOutPrintWriter().println("Failure [failed to add file(s)]");
return 1;
@@ -3974,26 +4011,67 @@
}
}
- private void processArgForLocalFile(String arg, PackageInstaller.Session session) {
+ private ArchivedPackageParcel getArchivedPackage(String inPath, long sizeBytes)
+ throws RemoteException, IOException {
+ final var fdWithSize = openInFile(inPath, sizeBytes);
+ if (fdWithSize.first == null) {
+ throw new IllegalArgumentException("Error: Can't open file: " + inPath);
+ }
+
+ File tmpFile = null;
+ final ParcelFileDescriptor fd = fdWithSize.first;
+ try (InputStream inStream = new AutoCloseInputStream(fd)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ File tmpStagingDir = Environment.getDataAppDirectory(null);
+ tmpFile = new File(tmpStagingDir, "tmdl" + RANDOM.nextInt() + ".tmp");
+
+ try (OutputStream outStream = new FileOutputStream(tmpFile)) {
+ Streams.copy(inStream, outStream);
+ }
+
+ return mInterface.getArchivedPackage(tmpFile.getAbsolutePath());
+ } finally {
+ if (tmpFile != null) {
+ tmpFile.delete();
+ }
+ Binder.restoreCallingIdentity(identity);
+ }
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Error: Can't stage file: " + inPath, e);
+ }
+ }
+
+ private void processArgForLocalFile(String arg, PackageInstaller.Session session,
+ boolean installArchived) throws IOException, RemoteException {
final String inPath = arg;
final File file = new File(inPath);
final String name = file.getName();
- final long size = getFileStatSize(file);
- final Metadata metadata = Metadata.forLocalFile(inPath);
+ final long size;
+ final Metadata metadata;
+ if (installArchived) {
+ metadata = Metadata.forArchived(getArchivedPackage(inPath, -1));
+ size = 0;
+ } else {
+ metadata = Metadata.forLocalFile(inPath);
+ size = getFileStatSize(file);
+ }
byte[] v4signatureBytes = null;
- // Try to load the v4 signature file for the APK; it might not exist.
- final String v4SignaturePath = inPath + V4Signature.EXT;
- final ParcelFileDescriptor pfd = openFileForSystem(v4SignaturePath, "r");
- if (pfd != null) {
- try {
- final V4Signature v4signature = V4Signature.readFrom(pfd);
- v4signatureBytes = v4signature.toByteArray();
- } catch (IOException ex) {
- Slog.e(TAG, "V4 signature file exists but failed to be parsed.", ex);
- } finally {
- IoUtils.closeQuietly(pfd);
+ if (!installArchived) {
+ // Try to load the v4 signature file for the APK; it might not exist.
+ final String v4SignaturePath = inPath + V4Signature.EXT;
+ final ParcelFileDescriptor pfd = openFileForSystem(v4SignaturePath, "r");
+ if (pfd != null) {
+ try {
+ final V4Signature v4signature = V4Signature.readFrom(pfd);
+ v4signatureBytes = v4signature.toByteArray();
+ } catch (IOException ex) {
+ Slog.e(TAG, "V4 signature file exists but failed to be parsed.", ex);
+ } finally {
+ IoUtils.closeQuietly(pfd);
+ }
}
}
@@ -4015,6 +4093,32 @@
return 0;
}
+ private Pair<ParcelFileDescriptor, Long> openInFile(String inPath, long sizeBytes)
+ throws IOException {
+ final ParcelFileDescriptor fd;
+ if (STDIN_PATH.equals(inPath)) {
+ fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ } else if (inPath != null) {
+ fd = openFileForSystem(inPath, "r");
+ if (fd == null) {
+ return Pair.create(null, -1L);
+ }
+ sizeBytes = fd.getStatSize();
+ if (sizeBytes < 0) {
+ fd.close();
+ getErrPrintWriter().println("Unable to get size of: " + inPath);
+ return Pair.create(null, -1L);
+ }
+ } else {
+ fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ }
+ if (sizeBytes <= 0) {
+ getErrPrintWriter().println("Error: must specify an APK size");
+ return Pair.create(null, 1L);
+ }
+ return Pair.create(fd, sizeBytes);
+ }
+
private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
boolean logSuccess) throws RemoteException {
PackageInstaller.Session session = null;
@@ -4024,26 +4128,13 @@
final PrintWriter pw = getOutPrintWriter();
- final ParcelFileDescriptor fd;
- if (STDIN_PATH.equals(inPath)) {
- fd = ParcelFileDescriptor.dup(getInFileDescriptor());
- } else if (inPath != null) {
- fd = openFileForSystem(inPath, "r");
- if (fd == null) {
- return -1;
- }
- sizeBytes = fd.getStatSize();
- if (sizeBytes < 0) {
- getErrPrintWriter().println("Unable to get size of: " + inPath);
- return -1;
- }
- } else {
- fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ final var fdWithSize = openInFile(inPath, sizeBytes);
+ if (fdWithSize.first == null) {
+ long resultCode = fdWithSize.second;
+ return (int) resultCode;
}
- if (sizeBytes <= 0) {
- getErrPrintWriter().println("Error: must specify an APK size");
- return 1;
- }
+ final ParcelFileDescriptor fd = fdWithSize.first;
+ sizeBytes = fdWithSize.second;
session.write(splitName, 0, sizeBytes, fd);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index a1e5153..fbe5a51 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -18,9 +18,11 @@
import android.annotation.NonNull;
import android.content.ComponentName;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.DataLoaderParams;
import android.content.pm.InstallationFile;
import android.content.pm.PackageInstaller;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ShellCommand;
import android.service.dataloader.DataLoaderService;
@@ -37,6 +39,7 @@
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
+import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicLong;
@@ -136,9 +139,13 @@
* Everything streamed.
*/
static final byte STREAMING = 3;
+ /**
+ * Archived install.
+ */
+ static final byte ARCHIVED = 4;
private final byte mMode;
- private final String mData;
+ private final byte[] mData;
private final String mSalt;
private static AtomicLong sGlobalSalt = new AtomicLong((new SecureRandom()).nextLong());
@@ -156,6 +163,21 @@
return new Metadata(LOCAL_FILE, filePath, nextGlobalSalt().toString());
}
+ /** @hide */
+ @VisibleForTesting
+ public static Metadata forArchived(ArchivedPackageParcel archivedPackage) {
+ Parcel parcel = Parcel.obtain();
+ byte[] bytes;
+ try {
+ parcel.writeParcelable(archivedPackage, 0);
+ bytes = parcel.marshall();
+ } finally {
+ parcel.recycle();
+ }
+
+ return new Metadata(ARCHIVED, bytes, null);
+ }
+
static Metadata forDataOnlyStreaming(String fileId) {
return new Metadata(DATA_ONLY_STREAMING, fileId);
}
@@ -169,8 +191,12 @@
}
private Metadata(byte mode, String data, String salt) {
+ this(mode, (data != null ? data : "").getBytes(StandardCharsets.UTF_8), salt);
+ }
+
+ private Metadata(byte mode, byte[] data, String salt) {
this.mMode = mode;
- this.mData = (data == null) ? "" : data;
+ this.mData = data;
this.mSalt = salt;
}
@@ -181,22 +207,21 @@
int offset = 0;
final byte mode = bytes[offset];
offset += 1;
- final String data;
+ final byte[] data;
final String salt;
switch (mode) {
case LOCAL_FILE: {
int dataSize = ByteBuffer.wrap(bytes, offset, 4).order(
ByteOrder.LITTLE_ENDIAN).getInt();
offset += 4;
- data = new String(bytes, offset, dataSize, StandardCharsets.UTF_8);
+ data = Arrays.copyOfRange(bytes, offset, offset + dataSize);
offset += dataSize;
salt = new String(bytes, offset, bytes.length - offset,
StandardCharsets.UTF_8);
break;
}
default:
- data = new String(bytes, offset, bytes.length - offset,
- StandardCharsets.UTF_8);
+ data = Arrays.copyOfRange(bytes, offset, bytes.length);
salt = null;
break;
}
@@ -207,7 +232,7 @@
@VisibleForTesting
public byte[] toByteArray() {
final byte[] result;
- final byte[] dataBytes = this.mData.getBytes(StandardCharsets.UTF_8);
+ final byte[] dataBytes = this.mData;
switch (this.mMode) {
case LOCAL_FILE: {
int dataSize = dataBytes.length;
@@ -237,9 +262,26 @@
return this.mMode;
}
- String getData() {
+ byte[] getData() {
return this.mData;
}
+
+ ArchivedPackageParcel getArchivedPackage() {
+ if (getMode() != ARCHIVED) {
+ throw new IllegalStateException("Not an archived package metadata.");
+ }
+
+ Parcel parcel = Parcel.obtain();
+ ArchivedPackageParcel result;
+ try {
+ parcel.unmarshall(this.mData, 0, this.mData.length);
+ parcel.setDataPosition(0);
+ result = parcel.readParcelable(ArchivedPackageParcel.class.getClassLoader());
+ } finally {
+ parcel.recycle();
+ }
+ return result;
+ }
}
private static class DataLoader implements DataLoaderService.DataLoader {
@@ -278,7 +320,9 @@
case Metadata.LOCAL_FILE: {
ParcelFileDescriptor incomingFd = null;
try {
- incomingFd = getLocalFilePFD(shellCommand, metadata.getData());
+ final String filePath = new String(metadata.getData(),
+ StandardCharsets.UTF_8);
+ incomingFd = getLocalFilePFD(shellCommand, filePath);
mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
incomingFd);
} finally {
@@ -286,6 +330,10 @@
}
break;
}
+ case Metadata.ARCHIVED: {
+ // Do nothing, metadata already contains everything needed for install.
+ break;
+ }
default:
Slog.e(TAG, "Unsupported metadata mode: " + metadata.getMode());
return false;
diff --git a/services/core/java/com/android/server/pm/PrepareFailure.java b/services/core/java/com/android/server/pm/PrepareFailure.java
index 3180bac..09cb6b9 100644
--- a/services/core/java/com/android/server/pm/PrepareFailure.java
+++ b/services/core/java/com/android/server/pm/PrepareFailure.java
@@ -42,7 +42,8 @@
}
PrepareFailure(String message, Exception e) {
- super(((PackageManagerException) e).error,
+ super(e instanceof PackageManagerException ? ((PackageManagerException) e).error
+ : PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
ExceptionUtils.getCompleteMessage(message, e));
}
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 3a1fd7c..8c73ce8 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -191,7 +191,7 @@
// Perform package verification and enable rollback (unless we are simply moving the
// package).
if (!mOriginInfo.mExisting) {
- if (!isApex()) {
+ if (!isApex() && !isArchivedInstallation()) {
// TODO(b/182426975): treat APEX as APK when APK verification is concerned
sendApkVerificationRequest(pkgLite);
}
@@ -896,6 +896,9 @@
public boolean isApex() {
return (mInstallFlags & PackageManager.INSTALL_APEX) != 0;
}
+ public boolean isArchivedInstallation() {
+ return (mInstallFlags & PackageManager.INSTALL_ARCHIVED) != 0;
+ }
public boolean isStaged() {
return mIsStaged;
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index f5ba3f6..d82a500 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -22,6 +22,7 @@
import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
@@ -183,6 +184,23 @@
}
/**
+ * Creates a ParsedPackage from PackageLite without any additional parsing or processing.
+ * Most fields will get reasonable default values, corresponding to "deleted-keep-data".
+ */
+ @AnyThread
+ public ParsedPackage parsePackageFromPackageLite(PackageLite packageLite, int flags)
+ throws PackageManagerException {
+ ParseInput input = mSharedResult.get().reset();
+ ParseResult<ParsingPackage> result = parsingUtils.parsePackageFromPackageLite(input,
+ packageLite, flags);
+ if (result.isError()) {
+ throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
+ result.getException());
+ }
+ return result.getResult().hideAsParsed();
+ }
+
+ /**
* Removes the cached value for the thread the parser was created on. It is assumed that
* any threads created for parallel parsing will be created and released, so they don't
* need an explicit close call.
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index f1f0fa3..699ccbd 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -375,6 +375,10 @@
ParsingPackage setBaseRevisionCode(int baseRevisionCode);
+ ParsingPackage setVersionCode(int vesionCode);
+
+ ParsingPackage setVersionCodeMajor(int vesionCodeMajor);
+
ParsingPackage setVersionName(String versionName);
ParsingPackage setCompileSdkVersion(int compileSdkVersion);
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index dc022f7..d9ad353 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -474,15 +474,117 @@
}
}
- private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
- String codePath, SplitAssetLoader assetLoader, int flags) {
- final String apkPath = apkFile.getAbsolutePath();
+ /**
+ * Creates ParsingPackage using only PackageLite.
+ * Missing fields will contain reasonable defaults.
+ * Used for packageless (aka archived) package installation.
+ */
+ public ParseResult<ParsingPackage> parsePackageFromPackageLite(ParseInput input,
+ PackageLite lite, int flags) {
+ final String volumeUuid = getVolumeUuid(lite.getPath());
+ final String pkgName = lite.getPackageName();
+ final TypedArray manifestArray = null;
+ final ParsingPackage pkg = mCallback.startParsingPackage(pkgName,
+ lite.getBaseApkPath(), lite.getPath(), manifestArray, lite.isCoreApp());
+
+ final int targetSdk = lite.getTargetSdk();
+ final String versionName = null;
+ final int compileSdkVersion = 0;
+ final String compileSdkVersionCodeName = null;
+ final boolean isolatedSplitLoading = false;
+
+ // Normally set from manifestArray.
+ pkg.setVersionCode(lite.getVersionCode());
+ pkg.setVersionCodeMajor(lite.getVersionCodeMajor());
+ pkg.setBaseRevisionCode(lite.getBaseRevisionCode());
+ pkg.setVersionName(versionName);
+ pkg.setCompileSdkVersion(compileSdkVersion);
+ pkg.setCompileSdkVersionCodeName(compileSdkVersionCodeName);
+ pkg.setIsolatedSplitLoading(isolatedSplitLoading);
+ pkg.setTargetSdkVersion(targetSdk);
+
+ // parseBaseApkTags
+ pkg.setInstallLocation(lite.getInstallLocation())
+ .setTargetSandboxVersion(PARSE_DEFAULT_TARGET_SANDBOX)
+ /* Set the global "on SD card" flag */
+ .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
+
+ // parseBaseAppBasicFlags
+ pkg
+ // Default true
+ .setBackupAllowed(true)
+ .setClearUserDataAllowed(true)
+ .setClearUserDataOnFailedRestoreAllowed(true)
+ .setAllowNativeHeapPointerTagging(true)
+ .setEnabled(true)
+ .setExtractNativeLibrariesRequested(true)
+ // targetSdkVersion gated
+ .setAllowAudioPlaybackCapture(targetSdk >= Build.VERSION_CODES.Q)
+ .setHardwareAccelerated(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ .setRequestLegacyExternalStorage(targetSdk < Build.VERSION_CODES.Q)
+ .setCleartextTrafficAllowed(targetSdk < Build.VERSION_CODES.P)
+ // Ints
+ .setCategory(ApplicationInfo.CATEGORY_UNDEFINED)
+ // Floats Default 0f
+ .setMaxAspectRatio(0f)
+ .setMinAspectRatio(0f);
+
+ // No APK - no code.
+ pkg.setDeclaredHavingCode(false);
+
+ final String taskAffinity = null;
+ ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
+ pkgName, pkgName, taskAffinity, input);
+ if (taskAffinityResult.isError()) {
+ return input.error(taskAffinityResult);
+ }
+ pkg.setTaskAffinity(taskAffinityResult.getResult());
+
+ final CharSequence pname = null;
+ ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+ pkgName, null /*defProc*/, pname, flags, mSeparateProcesses, input);
+ if (processNameResult.isError()) {
+ return input.error(processNameResult);
+ }
+ pkg.setProcessName(processNameResult.getResult());
+
+ pkg.setGwpAsanMode(-1);
+ pkg.setMemtagMode(-1);
+
+ afterParseBaseApplication(pkg);
+
+ final ParseResult<ParsingPackage> result = validateBaseApkTags(input, pkg);
+ if (result.isError()) {
+ return result;
+ }
+
+ pkg.setVolumeUuid(volumeUuid);
+
+ if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
+ pkg.setSigningDetails(lite.getSigningDetails());
+ } else {
+ pkg.setSigningDetails(SigningDetails.UNKNOWN);
+ }
+
+ return input.success(pkg
+ .set32BitAbiPreferred(lite.isUse32bitAbi()));
+ }
+
+ private static String getVolumeUuid(final String apkPath) {
String volumeUuid = null;
if (apkPath.startsWith(MNT_EXPAND)) {
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}
+ return volumeUuid;
+ }
+
+ private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
+ String codePath, SplitAssetLoader assetLoader, int flags) {
+ final String apkPath = apkFile.getAbsolutePath();
+
+ final String volumeUuid = getVolumeUuid(apkPath);
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
@@ -882,7 +984,7 @@
}
pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
- R.styleable.AndroidManifest_installLocation, sa))
+ R.styleable.AndroidManifest_installLocation, sa))
.setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
@@ -932,6 +1034,10 @@
}
}
+ return validateBaseApkTags(input, pkg);
+ }
+
+ private ParseResult<ParsingPackage> validateBaseApkTags(ParseInput input, ParsingPackage pkg) {
if (!ParsedAttributionUtils.isCombinationValid(pkg.getAttributions())) {
return input.error(
INSTALL_PARSE_FAILED_BAD_MANIFEST,
@@ -2199,15 +2305,19 @@
pkg.sortServices();
}
- // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
- // every activity info has had a chance to set it from its attributes.
+ afterParseBaseApplication(pkg);
+
+ return input.success(pkg);
+ }
+
+ // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
+ // every activity info has had a chance to set it from its attributes.
+ private void afterParseBaseApplication(ParsingPackage pkg) {
setMaxAspectRatio(pkg);
setMinAspectRatio(pkg);
setSupportsSizeChanges(pkg);
pkg.setHasDomainUrls(hasDomainURLs(pkg));
-
- return input.success(pkg);
}
/**