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

Commit 84f1294a authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Always derive native library paths at runtime.

Over time, we've unpacked native libraries at various places with
respect to their source APK.  Persisting this path in PackageSettings
has caused more pain recently with the switch to supporting multiArch
and cluster installs.

This change switches us to always derive the native library paths at
runtime based on the type of install.  This also ensures that
transitioning between a bundled system app and an upgraded system
app will always build the right path.

We still persist the last generated path into PackageSettings to make
cleanup at uninstall time easier.

Bug: 16208505, 16206748, 16212206
Change-Id: Ieb82a424ca4a92b5674983453c50ba4b695abfb0
parent 78311f36
Loading
Loading
Loading
Loading
+20 −12
Original line number Diff line number Diff line
@@ -491,18 +491,23 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
    public String nativeLibraryDir;

    /**
     * The path under the apps data directory we store unpacked libraries. For
     * new installs, we create subdirectories under legacyNativeLibraryDir that are
     * architecture specific. For legacy installs, the shared libraries are
     * placed directly under this path.
     * The root path where unpacked native libraries are stored.
     * <p>
     * When {@link #nativeLibraryRootRequiresIsa} is set, the libraries are
     * placed in ISA-specific subdirectories under this path, otherwise the
     * libraries are placed directly at this path.
     *
     * For "legacy" installs {@code nativeLibraryDir} will be equal to this path.
     * For newer installs, it will be derived based on the codePath and the primary
     * cpu abi.
     * @hide
     */
    public String nativeLibraryRootDir;

    /**
     * Flag indicating that ISA must be appended to
     * {@link #nativeLibraryRootDir} to be useful.
     *
     * @hide.
     * @hide
     */
    public String legacyNativeLibraryDir;
    public boolean nativeLibraryRootRequiresIsa;

    /**
     * The primary ABI that this application requires, This is inferred from the ABIs
@@ -683,7 +688,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        splitSourceDirs = orig.splitSourceDirs;
        splitPublicSourceDirs = orig.splitPublicSourceDirs;
        nativeLibraryDir = orig.nativeLibraryDir;
        legacyNativeLibraryDir = orig.legacyNativeLibraryDir;
        nativeLibraryRootDir = orig.nativeLibraryRootDir;
        nativeLibraryRootRequiresIsa = orig.nativeLibraryRootRequiresIsa;
        primaryCpuAbi = orig.primaryCpuAbi;
        secondaryCpuAbi = orig.secondaryCpuAbi;
        apkRoot = orig.apkRoot;
@@ -730,7 +736,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        dest.writeStringArray(splitSourceDirs);
        dest.writeStringArray(splitPublicSourceDirs);
        dest.writeString(nativeLibraryDir);
        dest.writeString(legacyNativeLibraryDir);
        dest.writeString(nativeLibraryRootDir);
        dest.writeInt(nativeLibraryRootRequiresIsa ? 1 : 0);
        dest.writeString(primaryCpuAbi);
        dest.writeString(secondaryCpuAbi);
        dest.writeString(apkRoot);
@@ -776,7 +783,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        splitSourceDirs = source.readStringArray();
        splitPublicSourceDirs = source.readStringArray();
        nativeLibraryDir = source.readString();
        legacyNativeLibraryDir = source.readString();
        nativeLibraryRootDir = source.readString();
        nativeLibraryRootRequiresIsa = source.readInt() != 0;
        primaryCpuAbi = source.readString();
        secondaryCpuAbi = source.readString();
        apkRoot = source.readString();
+1 −1
Original line number Diff line number Diff line
@@ -333,7 +333,7 @@ public class PackageParser {
    }

    public static final boolean isApkFile(File file) {
        return file.isFile() && file.getName().endsWith(".apk");
        return file.getName().endsWith(".apk");
    }

    /*
+93 −77
Original line number Diff line number Diff line
@@ -5061,7 +5061,7 @@ public class PackageManagerService extends IPackageManager.Stub {
            // Just create the setting, don't add it yet. For already existing packages
            // the PkgSetting exists already and doesn't have to be created.
            pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
                    destResourceFile, pkg.applicationInfo.legacyNativeLibraryDir,
                    destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
                    pkg.applicationInfo.primaryCpuAbi,
                    pkg.applicationInfo.secondaryCpuAbi,
                    pkg.applicationInfo.flags, user, false);
@@ -5287,7 +5287,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                            + pkg.applicationInfo.uid + "/fs_"
                            + currentUid;
                        pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;
                        pkg.applicationInfo.legacyNativeLibraryDir = pkg.applicationInfo.dataDir;
                        pkg.applicationInfo.nativeLibraryRootDir = pkg.applicationInfo.dataDir;
                        String msg = "Package " + pkg.packageName
                                + " has mismatched uid: "
                                + currentUid + " on disk, "
@@ -5346,22 +5346,20 @@ public class PackageManagerService extends IPackageManager.Stub {
            NativeLibraryHelper.removeNativeBinariesFromDirLI(
                    new File(codePath, LIB_DIR_NAME), false /* delete dirs */);
            setBundledAppAbisAndRoots(pkg, pkgSetting);
            setNativeLibraryPaths(pkg);
        } else {
            // TODO: We can probably be smarter about this stuff. For installed apps,
            // we can calculate this information at install time once and for all. For
            // system apps, we can probably assume that this information doesn't change
            // after the first boot scan. As things stand, we do lots of unnecessary work.
            // Give ourselves some initial paths; we'll come back for another
            // pass once we've determined ABI below.
            setNativeLibraryPaths(pkg);
            final boolean isAsec = isForwardLocked(pkg) || isExternal(pkg);
            final String nativeLibraryRootStr;
            final boolean useIsaSpecificSubdirs;
            if (pkg.applicationInfo.legacyNativeLibraryDir != null) {
                nativeLibraryRootStr = pkg.applicationInfo.legacyNativeLibraryDir;
                useIsaSpecificSubdirs = false;
            } else {
                nativeLibraryRootStr = new File(pkg.codePath, LIB_DIR_NAME).getAbsolutePath();
                useIsaSpecificSubdirs = true;
            }
            final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir;
            final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa;
            NativeLibraryHelper.Handle handle = null;
            try {
@@ -5467,6 +5465,10 @@ public class PackageManagerService extends IPackageManager.Stub {
                IoUtils.closeQuietly(handle);
            }
            // Now that we've calculated the ABIs and determined if it's an internal app,
            // we will go ahead and populate the nativeLibraryPath.
            setNativeLibraryPaths(pkg);
            if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
            final int[] userIds = sUserManager.getUserIds();
            synchronized (mInstallLock) {
@@ -5475,14 +5477,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                // this symlink for 64 bit libraries.
                if (pkg.applicationInfo.primaryCpuAbi != null &&
                        !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
                    final String nativeLibPath;
                    if (pkg.applicationInfo.legacyNativeLibraryDir != null) {
                        nativeLibPath = pkg.applicationInfo.legacyNativeLibraryDir;
                    } else {
                        nativeLibPath = new File(nativeLibraryRootStr,
                                VMRuntime.getInstructionSet(pkg.applicationInfo.primaryCpuAbi)).getAbsolutePath();
                    }
                    final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
                    for (int userId : userIds) {
                        if (mInstaller.linkNativeLibraryDirectory(pkg.packageName, nativeLibPath, userId) < 0) {
                            Slog.w(TAG, "Failed linking native library dir (user=" + userId
@@ -5498,19 +5493,20 @@ public class PackageManagerService extends IPackageManager.Stub {
            pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
        }
        Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName
                + " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
                + pkg.applicationInfo.nativeLibraryRootRequiresIsa);
        // Push the derived path down into PackageSettings so we know what to
        // clean up at uninstall time.
        pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;
        if (DEBUG_ABI_SELECTION) {
            Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +
                    " primary=" + pkg.applicationInfo.primaryCpuAbi +
                    " secondary=" + pkg.applicationInfo.secondaryCpuAbi);
        }
        // Check if we have a legacy native library path, use it if we do.
        pkg.applicationInfo.legacyNativeLibraryDir = pkgSetting.legacyNativeLibraryPathString;
        // Now that we've calculated the ABIs and determined if it's an internal app,
        // we will go ahead and populate the nativeLibraryPath.
        populateDefaultNativeLibraryPath(pkg, pkg.applicationInfo);
        if ((scanMode&SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
            // We don't do this here during boot because we can do it all
            // at once after scanning all existing packages.
@@ -6161,49 +6157,66 @@ public class PackageManagerService extends IPackageManager.Stub {
        return codeRoot.getPath();
    }
    private void populateDefaultNativeLibraryPath(PackageParser.Package pkg,
                                                  ApplicationInfo info) {
        if (info.legacyNativeLibraryDir != null) {
            // Not a cluster install.
            if (DEBUG_ABI_SELECTION) {
                Log.i(TAG, "Set nativeLibraryDir [non_cluster] for: " + pkg.packageName +
                        " to " + info.legacyNativeLibraryDir);
            }
            info.nativeLibraryDir = info.legacyNativeLibraryDir;
        } else if (info.primaryCpuAbi != null) {
            final boolean is64Bit = VMRuntime.is64BitAbi(info.primaryCpuAbi);
            if (info.apkRoot != null) {
    /**
     * Derive and set the location of native libraries for the given package,
     * which varies depending on where and how the package was installed.
     */
    private void setNativeLibraryPaths(PackageParser.Package pkg) {
        final ApplicationInfo info = pkg.applicationInfo;
        final String codePath = pkg.codePath;
        final File codeFile = new File(codePath);
        final boolean bundledApp = isSystemApp(info) && !isUpdatedSystemApp(info);
        final boolean asecApp = isForwardLocked(info) || isExternal(info);
        info.nativeLibraryRootDir = null;
        info.nativeLibraryRootRequiresIsa = false;
        info.nativeLibraryDir = null;
        if (bundledApp) {
            // Monolithic bundled install
            // TODO: support cluster bundled installs?
            final boolean is64Bit = (info.primaryCpuAbi != null)
                    && VMRuntime.is64BitAbi(info.primaryCpuAbi);
            // This is a bundled system app so choose the path based on the ABI.
            // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this
            // is just the default path.
                final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
            final String apkName = deriveCodePathName(codePath);
            final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME;
                info.nativeLibraryDir = (new File(info.apkRoot, new File(libDir, apkName).getAbsolutePath()))
            info.nativeLibraryRootDir = Environment.buildPath(new File(info.apkRoot), libDir,
                    apkName).getAbsolutePath();
            info.nativeLibraryRootRequiresIsa = false;
        } else if (isApkFile(codeFile)) {
            // Monolithic install
            if (asecApp) {
                info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
                        .getAbsolutePath();
                if (DEBUG_ABI_SELECTION) {
                    Log.i(TAG, "Set nativeLibraryDir [system] for: " + pkg.packageName +
                            " to " + info.nativeLibraryDir);
                }
                info.nativeLibraryRootRequiresIsa = false;
            } else {
                // Cluster install. legacyNativeLibraryDir == null && primaryCpuAbi = null
                // implies this must be a cluster package.
                final String codePath = pkg.codePath;
                final File libPath = new File(new File(codePath, LIB_DIR_NAME),
                        VMRuntime.getInstructionSet(info.primaryCpuAbi));
                info.nativeLibraryDir = libPath.getAbsolutePath();
                if (DEBUG_ABI_SELECTION) {
                    Log.i(TAG, "Set nativeLibraryDir [cluster] for: " + pkg.packageName +
                            " to " + info.nativeLibraryDir);
                }
                final String apkName = deriveCodePathName(codePath);
                info.nativeLibraryRootDir = new File(mAppLib32InstallDir, apkName)
                        .getAbsolutePath();
                info.nativeLibraryRootRequiresIsa = false;
            }
        } else {
            if (DEBUG_ABI_SELECTION) {
                Log.i(TAG, "Setting nativeLibraryDir to null for: " + pkg.packageName);
            // Cluster install
            info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
            info.nativeLibraryRootRequiresIsa = true;
        }
            info.nativeLibraryDir = null;
        if (info.nativeLibraryRootRequiresIsa) {
            if (info.primaryCpuAbi != null) {
                info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
                        VMRuntime.getInstructionSet(info.primaryCpuAbi)).getAbsolutePath();
            } else {
                Slog.w(TAG, "Package " + info.packageName
                        + " missing ABI; unable to derive nativeLibraryDir");
            }
        } else {
            info.nativeLibraryDir = info.nativeLibraryRootDir;
        }
    }
@@ -9124,13 +9137,13 @@ public class PackageManagerService extends IPackageManager.Stub {
        }
        /** Existing install */
        FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryRoot,
        FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryPath,
                String[] instructionSets, boolean isMultiArch) {
            super(null, false, null, 0, null, null, null, instructionSets, null, isMultiArch);
            this.codeFile = (codePath != null) ? new File(codePath) : null;
            this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
            this.legacyNativeLibraryPath = (legacyNativeLibraryRoot != null) ?
                    new File(legacyNativeLibraryRoot) : null;
            this.legacyNativeLibraryPath = (legacyNativeLibraryPath != null) ?
                    new File(legacyNativeLibraryPath) : null;
        }
        /** New install from existing */
@@ -9314,8 +9327,6 @@ public class PackageManagerService extends IPackageManager.Stub {
                pkg.applicationInfo.setResourcePath(pkg.codePath);
                pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);
                pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
                // Null out the legacy native dir so we stop using it.
                pkg.applicationInfo.legacyNativeLibraryDir = null;
                return true;
            }
@@ -9600,9 +9611,6 @@ public class PackageManagerService extends IPackageManager.Stub {
            pkg.applicationInfo.setResourcePath(getResourcePath());
            pkg.applicationInfo.setBaseResourcePath(getResourcePath());
            pkg.applicationInfo.setSplitResourcePaths(null);
            // ASEC installs are considered "legacy" because we don't support
            // multiarch on them yet, and use the old style paths on them.
            pkg.applicationInfo.legacyNativeLibraryDir = legacyNativeLibraryDir;
            return true;
        }
@@ -10074,7 +10082,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                res.removedInfo.args = createInstallArgsForExisting(0,
                        deletedPackage.applicationInfo.getCodePath(),
                        deletedPackage.applicationInfo.getResourcePath(),
                        deletedPackage.applicationInfo.legacyNativeLibraryDir,
                        deletedPackage.applicationInfo.nativeLibraryRootDir,
                        getAppDexInstructionSets(deletedPackage.applicationInfo),
                        isMultiArch(deletedPackage.applicationInfo));
            } else {
@@ -10384,6 +10392,9 @@ public class PackageManagerService extends IPackageManager.Stub {
        return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0;
    }
    private static boolean isForwardLocked(ApplicationInfo info) {
        return (info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0;
    }
    private boolean isForwardLocked(PackageSetting ps) {
        return (ps.pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0;
@@ -10405,6 +10416,10 @@ public class PackageManagerService extends IPackageManager.Stub {
        return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
    }
    private static boolean isExternal(ApplicationInfo info) {
        return (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
    }
    private static boolean isSystemApp(PackageParser.Package pkg) {
        return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    }
@@ -10429,6 +10444,10 @@ public class PackageManagerService extends IPackageManager.Stub {
        return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
    }
    private static boolean isUpdatedSystemApp(ApplicationInfo info) {
        return (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
    }
    private int packageFlagsToInstallFlags(PackageSetting ps) {
        int installFlags = 0;
        if (isExternal(ps)) {
@@ -12850,7 +12869,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                final boolean multiArch = isMultiArch(pkg.applicationInfo);
                InstallArgs srcArgs = createInstallArgsForExisting(currFlags,
                        pkg.applicationInfo.getCodePath(), pkg.applicationInfo.getResourcePath(),
                        pkg.applicationInfo.legacyNativeLibraryDir, instructionSets, multiArch);
                        pkg.applicationInfo.nativeLibraryRootDir, instructionSets, multiArch);
                MoveParams mp = new MoveParams(srcArgs, observer, newFlags, packageName,
                        instructionSets, pkg.applicationInfo.uid, user, multiArch);
                msg.obj = mp;
@@ -12977,9 +12996,6 @@ public class PackageManagerService extends IPackageManager.Stub {
                                        pkg.applicationInfo.setResourcePath(newResPath);
                                        pkg.applicationInfo.setBaseResourcePath(newResPath);
                                        pkg.applicationInfo.setSplitResourcePaths(null);
                                        // Null out the legacy nativeLibraryDir so that we stop using it and
                                        // always derive the codepath.
                                        pkg.applicationInfo.legacyNativeLibraryDir = null;
                                        PackageSetting ps = (PackageSetting) pkg.mExtras;
                                        ps.codePath = new File(pkg.applicationInfo.getCodePath());
+4 −3
Original line number Diff line number Diff line
@@ -57,10 +57,11 @@ class PackageSettingBase extends GrantedPermissions {
    String resourcePathString;

    /**
     * The path under which native libraries for legacy apps are unpacked.
     * Will be set to {@code null} for newer installs, where the path can be
     * derived from {@link #codePath} unambiguously.
     * The path under which native libraries have been unpacked. This path is
     * always derived at runtime, and is only stored here for cleanup when a
     * package is uninstalled.
     */
    @Deprecated
    String legacyNativeLibraryPathString;

    String primaryCpuAbiString;
+8 −7
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Map.Entry;

@@ -317,11 +318,11 @@ final class Settings {

    PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
            String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
            String nativeLibraryRoot, String primaryCpuAbi, String secondaryCpuAbi, int pkgFlags,
            String legacyNativeLibraryPathString, String primaryCpuAbi, String secondaryCpuAbi, int pkgFlags,
            UserHandle user, boolean add) {
        final String name = pkg.packageName;
        PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
                resourcePath, nativeLibraryRoot, primaryCpuAbi, secondaryCpuAbi,
                resourcePath, legacyNativeLibraryPathString, primaryCpuAbi, secondaryCpuAbi,
                pkg.mVersionCode, pkgFlags, user, add, true /* allowInstall */);
        return p;
    }
@@ -689,24 +690,24 @@ final class Settings {
        // pkg.mSetStopped = p.getStopped(userId);
        final String codePath = pkg.applicationInfo.getCodePath();
        final String resourcePath = pkg.applicationInfo.getResourcePath();
        final String legacyNativeLibraryPath = pkg.applicationInfo.nativeLibraryRootDir;
        // Update code path if needed
        if (!codePath.equalsIgnoreCase(p.codePathString)) {
        if (!Objects.equals(codePath, p.codePathString)) {
            Slog.w(PackageManagerService.TAG, "Code path for pkg : " + p.pkg.packageName +
                    " changing from " + p.codePathString + " to " + codePath);
            p.codePath = new File(codePath);
            p.codePathString = codePath;
        }
        //Update resource path if needed
        if (!resourcePath.equalsIgnoreCase(p.resourcePathString)) {
        if (!Objects.equals(resourcePath, p.resourcePathString)) {
            Slog.w(PackageManagerService.TAG, "Resource path for pkg : " + p.pkg.packageName +
                    " changing from " + p.resourcePathString + " to " + resourcePath);
            p.resourcePath = new File(resourcePath);
            p.resourcePathString = resourcePath;
        }
        // Update the native library paths if needed
        final String nativeLibraryRoot = pkg.applicationInfo.legacyNativeLibraryDir;
        if (nativeLibraryRoot != null && !nativeLibraryRoot.equalsIgnoreCase(p.legacyNativeLibraryPathString)) {
            p.legacyNativeLibraryPathString = nativeLibraryRoot;
        if (!Objects.equals(legacyNativeLibraryPath, p.legacyNativeLibraryPathString)) {
            p.legacyNativeLibraryPathString = legacyNativeLibraryPath;
        }

        // Update the required Cpu Abi