Merge changes I02651e12,I8b7ad8f6
* changes: [pm] Extract the profiles from the apk in install stage [pm] Add --no-profile option into install command
This commit is contained in:
@@ -1432,6 +1432,7 @@ public abstract class PackageManager {
|
||||
INSTALL_ALLOW_DOWNGRADE,
|
||||
INSTALL_STAGED,
|
||||
INSTALL_REQUEST_UPDATE_OWNERSHIP,
|
||||
INSTALL_DONT_EXTRACT_BASELINE_PROFILES,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface InstallFlags {}
|
||||
@@ -1646,6 +1647,13 @@ public abstract class PackageManager {
|
||||
*/
|
||||
public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
|
||||
|
||||
/**
|
||||
* Flag parameter for {@link PackageInstaller.SessionParams} to indicate that do not extract
|
||||
* the baseline profiles when parsing the apk
|
||||
* @hide
|
||||
*/
|
||||
public static final int INSTALL_DONT_EXTRACT_BASELINE_PROFILES = 1 << 27;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = true, value = {
|
||||
DONT_KILL_APP,
|
||||
|
||||
@@ -23,6 +23,9 @@ import android.content.pm.parsing.ApkLiteParseUtils;
|
||||
import android.content.pm.parsing.PackageLite;
|
||||
import android.content.pm.parsing.result.ParseInput;
|
||||
import android.content.pm.parsing.result.ParseResult;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.JsonReader;
|
||||
@@ -33,6 +36,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.security.VerityUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -44,6 +48,7 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* Helper class used to compute and validate the location of dex metadata files.
|
||||
@@ -62,6 +67,11 @@ public class DexMetadataHelper {
|
||||
|
||||
private static final String DEX_METADATA_FILE_EXTENSION = ".dm";
|
||||
|
||||
private static final String PROFILE_FILE_NAME = "primary.prof";
|
||||
private static final String PROFILE_METADATA_FILE_NAME = "primary.profm";
|
||||
private static final String BASELINE_PROFILE_SOURCE_RELATIVE_PATH = "dexopt/baseline.prof";
|
||||
private static final String BASELINE_PROFILE_METADATA_RELATIVE_PATH = "dexopt/baseline.profm";
|
||||
|
||||
private DexMetadataHelper() {}
|
||||
|
||||
/** Return true if the given file is a dex metadata file. */
|
||||
@@ -313,4 +323,76 @@ public class DexMetadataHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the baseline profiles from the assets directory in the apk. Then create a
|
||||
* ZIP archive with the (.dm) file extension (e.g. foo.dm from foo.apk) to include the
|
||||
* baseline profiles and put the DexMetadata file in the same directory with the apk.
|
||||
*
|
||||
* @param assetManager The {@link AssetManager} to use.
|
||||
* @param apkPath The path of the apk
|
||||
* @return {@code true} if the extraction is successful. Otherwise, return {@code false}.
|
||||
*
|
||||
* @see #buildDexMetadataPathForApk(String)
|
||||
*/
|
||||
public static boolean extractBaselineProfilesToDexMetadataFileFromApk(AssetManager assetManager,
|
||||
String apkPath) {
|
||||
if (!ApkLiteParseUtils.isApkPath(apkPath)) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "It is not an apk file: " + apkPath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the name of the DexMetadata file from the path of the apk
|
||||
final File dmFile = new File(buildDexMetadataPathForApk(apkPath));
|
||||
boolean success = false;
|
||||
|
||||
// load profile and profile metadata from assets directory in the apk
|
||||
try (InputStream profileIs = openStreamFromAssets(assetManager,
|
||||
BASELINE_PROFILE_SOURCE_RELATIVE_PATH);
|
||||
InputStream profileMetadataIs = openStreamFromAssets(assetManager,
|
||||
BASELINE_PROFILE_METADATA_RELATIVE_PATH)) {
|
||||
// Create the zip archive file and write the baseline profiles into it.
|
||||
try (FileOutputStream fos = new FileOutputStream(dmFile)) {
|
||||
try (ZipOutputStream zipOs = new ZipOutputStream(fos)) {
|
||||
zipOs.putNextEntry(new ZipEntry(PROFILE_FILE_NAME));
|
||||
zipOs.write(profileIs.readAllBytes());
|
||||
zipOs.closeEntry();
|
||||
|
||||
zipOs.putNextEntry(new ZipEntry(PROFILE_METADATA_FILE_NAME));
|
||||
zipOs.write(profileMetadataIs.readAllBytes());
|
||||
zipOs.closeEntry();
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (DEBUG) {
|
||||
Log.e(TAG, "Extract baseline profiles from apk failed: " + e.getMessage());
|
||||
}
|
||||
} finally {
|
||||
if (!success) {
|
||||
if (dmFile.exists()) {
|
||||
dmFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an {@link AutoCloseInputStream} from assets with the path.
|
||||
*
|
||||
* @param assetManager The {@link AssetManager} to use.
|
||||
* @param path The source file's relative path.
|
||||
* @return An AutoCloseInputStream in case the file was successfully read.
|
||||
* @throws IOException If anything goes wrong while opening or reading the file.
|
||||
*/
|
||||
private static AutoCloseInputStream openStreamFromAssets(AssetManager assetManager, String path)
|
||||
throws IOException {
|
||||
AssetFileDescriptor descriptor = assetManager.openFd(path);
|
||||
// Based on the java doc of AssetFileDescriptor#createInputStream, it will always return
|
||||
// an AutoCloseInputStream. It should be fine we cast it from FileInputStream to
|
||||
// AutoCloseInputStream here.
|
||||
return (AutoCloseInputStream) descriptor.createInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1077,6 +1077,8 @@ final class InstallPackageHelper {
|
||||
final boolean isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
|
||||
final boolean isRollback =
|
||||
request.getInstallReason() == PackageManager.INSTALL_REASON_ROLLBACK;
|
||||
final boolean extractProfile =
|
||||
((installFlags & PackageManager.INSTALL_DONT_EXTRACT_BASELINE_PROFILES) == 0);
|
||||
@PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
|
||||
if (request.isInstallMove()) {
|
||||
// moving a complete application; perform an initial scan on the new install location
|
||||
@@ -1112,7 +1114,9 @@ final class InstallPackageHelper {
|
||||
@ParsingPackageUtils.ParseFlags final int parseFlags =
|
||||
mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_CHATTY
|
||||
| ParsingPackageUtils.PARSE_ENFORCE_CODE
|
||||
| (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
|
||||
| (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0)
|
||||
| (extractProfile
|
||||
? ParsingPackageUtils.PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK : 0);
|
||||
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
|
||||
final ParsedPackage parsedPackage;
|
||||
|
||||
@@ -3436,6 +3436,10 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
sessionParams.installFlags |=
|
||||
PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
|
||||
break;
|
||||
case "--no-profile":
|
||||
sessionParams.installFlags |=
|
||||
PackageManager.INSTALL_DONT_EXTRACT_BASELINE_PROFILES;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown option " + opt);
|
||||
}
|
||||
@@ -4324,7 +4328,7 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
|
||||
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
|
||||
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
|
||||
pw.println(" [--enable-rollback]");
|
||||
pw.println(" [--enable-rollback] [--no-profile]");
|
||||
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
|
||||
pw.println(" [--apex] [--force-non-staged] [--staged-ready-timeout TIMEOUT]");
|
||||
pw.println(" [PATH [SPLIT...]|-]");
|
||||
@@ -4357,6 +4361,7 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
pw.println(" --apex: install an .apex file, not an .apk");
|
||||
pw.println(" --force-non-staged: force the installation to run under a non-staged");
|
||||
pw.println(" session, which may complete without requiring a reboot");
|
||||
pw.println(" --no-profile: don't extract the profiles from the apk");
|
||||
pw.println(" --staged-ready-timeout: By default, staged sessions wait "
|
||||
+ DEFAULT_STAGED_READY_TIMEOUT_MS);
|
||||
pw.println(" milliseconds for pre-reboot verification to complete when");
|
||||
@@ -4378,7 +4383,7 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
|
||||
pw.println(" [--preload] [--instant] [--full] [--dont-kill]");
|
||||
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
|
||||
pw.println(" [--multi-package] [--staged] [--update-ownership]");
|
||||
pw.println(" [--multi-package] [--staged] [--no-profile] [--update-ownership]");
|
||||
pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
|
||||
pw.println(" to push data into the session, and \"install-commit\" to finish.");
|
||||
pw.println("");
|
||||
|
||||
@@ -50,6 +50,7 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.Property;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.SigningDetails;
|
||||
import android.content.pm.dex.DexMetadataHelper;
|
||||
import android.content.pm.parsing.ApkLiteParseUtils;
|
||||
import android.content.pm.parsing.FrameworkParsingPackageUtils;
|
||||
import android.content.pm.parsing.PackageLite;
|
||||
@@ -73,6 +74,7 @@ import android.os.SystemProperties;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.os.ext.SdkExtensions;
|
||||
import android.os.incremental.IncrementalManager;
|
||||
import android.permission.PermissionManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
@@ -240,6 +242,11 @@ public class ParsingPackageUtils {
|
||||
public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
|
||||
public static final int PARSE_APK_IN_APEX = 1 << 9;
|
||||
|
||||
/**
|
||||
* This flag is to determine whether to extract the baseline profiles from the apk or not.
|
||||
*/
|
||||
public static final int PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK = 1 << 10;
|
||||
|
||||
public static final int PARSE_CHATTY = 1 << 31;
|
||||
|
||||
/** The total maximum number of activities, services, providers and activity-aliases */
|
||||
@@ -251,14 +258,16 @@ public class ParsingPackageUtils {
|
||||
private static final int MAX_PERMISSION_NAME_LENGTH = 512;
|
||||
|
||||
@IntDef(flag = true, prefix = { "PARSE_" }, value = {
|
||||
PARSE_APK_IN_APEX,
|
||||
PARSE_CHATTY,
|
||||
PARSE_COLLECT_CERTIFICATES,
|
||||
PARSE_ENFORCE_CODE,
|
||||
PARSE_EXTERNAL_STORAGE,
|
||||
PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK,
|
||||
PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
|
||||
PARSE_IGNORE_PROCESSES,
|
||||
PARSE_IS_SYSTEM_DIR,
|
||||
PARSE_MUST_BE_APK,
|
||||
PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ParseFlags {}
|
||||
@@ -559,6 +568,26 @@ public class ParsingPackageUtils {
|
||||
pkg.setSigningDetails(SigningDetails.UNKNOWN);
|
||||
}
|
||||
|
||||
// 1. The apkFile is an apk file
|
||||
// 2. The flags include PARSE_EXTRACT_PROFILE_FROM_APK
|
||||
// 3. The apk patch is NOT an incremental path
|
||||
// 4. If the .dm file exists in the current apk directory, it means the caller
|
||||
// prepares the .dm file. Don't extract the profiles from the apk again.
|
||||
if (ApkLiteParseUtils.isApkFile(apkFile)
|
||||
&& (flags & PARSE_EXTRACT_BASELINE_PROFILES_FROM_APK) != 0
|
||||
&& !IncrementalManager.isIncrementalPath(apkPath)
|
||||
&& DexMetadataHelper.findDexMetadataForFile(apkFile) == null) {
|
||||
// Extract the baseline profiles from the apk if the profiles exist in the assets
|
||||
// directory in the apk.
|
||||
boolean extractedResult =
|
||||
DexMetadataHelper.extractBaselineProfilesToDexMetadataFileFromApk(assets,
|
||||
apkPath);
|
||||
|
||||
if (DEBUG_JAR) {
|
||||
Slog.d(TAG, "Extract profiles " + (extractedResult ? "success" : "fail"));
|
||||
}
|
||||
}
|
||||
|
||||
return input.success(pkg);
|
||||
} catch (Exception e) {
|
||||
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
|
||||
|
||||
Reference in New Issue
Block a user