Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 767b1214 authored by Jiakai Zhang's avatar Jiakai Zhang
Browse files

Update the logic for inheriting dexopt artifacts during inherit install.

The old code path assumes that the oat dir is in the following
hierarchy:

oat/
  <isa-1>/
    <filename-1>
    <filename-2>
    ...
  <isa-2>/
    ...

where <isa-*> are supported instruction sets.

At the time of writing, this assumption is true, but it may not be true
in the future as ART evolves. Besides, the old code path depends on the
InstructionSets class, a class for legacy dexopt, preventing us from
removing it.

The new code path preserves the entire oat dir hierarchy regardless of
the directory names and the depth, and it doesn't depend on
InstructionSets.

Bug: 258223472
Test: -
  1. Install a base APK using `adb install`.
  2. Check the files in the oat dir and their inodes.
  3. Install a split APK using `adb install -p` (inherit install).
  4. Verify that the existing files are there in the oat dir at the
     destination and their inodes are unchanged.
Flag: android.content.pm.alternative_for_dexopt_cleanup
Change-Id: Id6a7801b9f8bcec4fa81ce4c4543fee560847526
parent bfc47e1e
Loading
Loading
Loading
Loading
+3 −4
Original line number Diff line number Diff line
@@ -589,16 +589,15 @@ public class Installer extends SystemService {
    }

    /**
     * Creates an oat dir for given package and instruction set.
     * Creates an oat dir and its sub-dirs for given package.
     */
    public void createOatDir(String packageName, String oatDir, String dexInstructionSet)
    public void createOatDirs(String packageName, String oatDir, List<String> oatSubDirs)
            throws InstallerException {
        // This method should be allowed even if ART Service is enabled, because it's used for
        // creating oat dirs before creating hard links for partial installation.
        // TODO(b/274658735): Add an ART Service API to support hard linking.
        if (!checkBeforeRemote()) return;
        try {
            mInstalld.createOatDir(packageName, oatDir, dexInstructionSet);
            mInstalld.createOatDirs(packageName, oatDir, oatSubDirs);
        } catch (Exception e) {
            throw InstallerException.from(e);
        }
+53 −30
Original line number Diff line number Diff line
@@ -236,7 +236,9 @@ import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
@@ -252,6 +254,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    private static final String TAG = "PackageInstallerSession";
@@ -895,8 +898,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    private final List<File> mResolvedStagedFiles = new ArrayList<>();
    @GuardedBy("mLock")
    private final List<File> mResolvedInheritedFiles = new ArrayList<>();
    @GuardedBy("mLock")
    private final List<String> mResolvedInstructionSets = new ArrayList<>();
    @GuardedBy("mLock") private final List<String> mResolvedOatSubDirs = new ArrayList<>();
    @GuardedBy("mLock")
    private final List<String> mResolvedNativeLibPaths = new ArrayList<>();

@@ -3637,9 +3639,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                }

                if (isLinkPossible(fromFiles, toDir)) {
                    if (!mResolvedInstructionSets.isEmpty()) {
                    if (!mResolvedOatSubDirs.isEmpty()) {
                        final File oatDir = new File(toDir, "oat");
                        createOatDirs(tempPackageName, mResolvedInstructionSets, oatDir);
                        createOatDirs(tempPackageName, mResolvedOatSubDirs, oatDir);
                    }
                    // pre-create lib dirs for linking if necessary
                    if (!mResolvedNativeLibPaths.isEmpty()) {
@@ -4521,18 +4523,40 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                params.setDontKillApp(false);
            }

            // Inherit compiled oat directory.
            final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
            mInheritedFilesBase = packageInstallDir;

            // Keep track of all dexopt artifacts and their containing directories. If we're linking
            // (and not copying) inherited files, we can recreate the oat directory hierarchy and
            // link dexopt artifacts.
            // Note that not all dexopt artifacts are necessarily needed and usable at the
            // destination. Here, we blindly link all of them and let ART decide which ones to use.
            // ART replaces the unusable ones during the next dexopt (typically in a later stage of
            // the installation) and removes the unneeded ones during the next file GC (typically
            // when the device is idle).
            final File oatDir = new File(packageInstallDir, "oat");
            if (Flags.alternativeForDexoptCleanup()) {
                Path oatPath = oatDir.toPath();
                try (Stream<Path> stream = Files.walk(oatPath)) {
                    for (Path path : (Iterable<Path>) stream::iterator) {
                        if (Files.isRegularFile(path)) {
                            mResolvedInheritedFiles.add(path.toFile());
                            String subDir = oatPath.relativize(path.getParent()).toString();
                            if (!subDir.equals("") && !mResolvedOatSubDirs.contains(subDir)) {
                                mResolvedOatSubDirs.add(subDir);
                            }
                        }
                    }
                } catch (IOException | UncheckedIOException e) {
                    Slog.e(TAG, "Error walking directory " + oatDir, e);
                }
            } else {
                if (oatDir.exists()) {
                    final File[] archSubdirs = oatDir.listFiles();

                // Keep track of all instruction sets we've seen compiled output for.
                // If we're linking (and not copying) inherited files, we can recreate the
                // instruction set hierarchy and link compiled output.
                    if (archSubdirs != null && archSubdirs.length > 0) {
                    final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
                        final String[] instructionSets =
                                InstructionSets.getAllDexCodeInstructionSets();
                        for (File archSubDir : archSubdirs) {
                            // Skip any directory that isn't an ISA subdir.
                            if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
@@ -4544,11 +4568,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                                continue;
                            }

                        mResolvedInstructionSets.add(archSubDir.getName());
                            mResolvedOatSubDirs.add(archSubDir.getName());
                            mResolvedInheritedFiles.addAll(Arrays.asList(files));
                        }
                    }
                }
            }

            // Inherit native libraries for DONT_KILL sessions.
            if (mayInheritNativeLibs() && removeSplitList.isEmpty()) {
@@ -5091,16 +5116,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        throw new IOException("File: " + pathStr + " outside base: " + baseStr);
    }

    private void createOatDirs(String packageName, List<String> instructionSets, File fromDir)
    private void createOatDirs(String packageName, List<String> oatSubDirs, File fromDir)
            throws PackageManagerException {
        for (String instructionSet : instructionSets) {
        try {
                mInstaller.createOatDir(packageName, fromDir.getAbsolutePath(), instructionSet);
            mInstaller.createOatDirs(packageName, fromDir.getAbsolutePath(), oatSubDirs);
        } catch (InstallerException e) {
            throw PackageManagerException.from(e);
        }
    }
    }

    private void linkFile(String packageName, String relativePath, String fromBase, String toBase)
            throws IOException {