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

Commit 6a5b8b1f authored by Jiyong Park's avatar Jiyong Park
Browse files

Introduce uses-native-library tag

Since Android 7.0, partners were able to export some of their native
shared libraries to apps. So far, the native libraries were provided to
all apps regardless of whether they are actually needed or not. Even
worse, it was impossible to prevent the installation of an app to the
device where the some (or all) of the required native libs don't exist;
the apps had to manually handle the case, which sometimes impossible
when the dependency is so fundamental.

This change introduces a new tag <uses-native-library> to the app
manifest. Similary to the existing <uses-library> tag which is for
(java) shared libraries, the new tag is used to describe the depedencies
to the native shared libraries.

Apps targeting Android S or higher are required to use the tag to import
the native shared libraries. Libraries that are not depended on won't be
available to the app even if the libraries are listed in the
public.libraries*.txt files. Furthermore, when the dependency can't be
satisfied for an app, the package manager refejects installing the app.

The dependency can be optional using the `android:required` attribute.
When it is set to true, the absence of the lib on the device doesn't
prevent the app from being installed. However, the app has to gracefully
deal with the absence.

The changed behavior only affects apps targeting S or higher. Existing
apps are unaffected; they still get all the public native libraries
regardless of whether they have <uses-native-library> tags or not; the
tags are simply ignored.

This is the first version of the implementation and therefore needs
further refinements. The followings are two major TODOs.

1) The native shared lib dependencies of the java shared libraries
are not enforced. For example, if an app depends on a java shared
library foo and foo depends on some native shared libraries, the
classloader where code from foo is loaded still gets all native shared
libraries. This should be fixed.

2) New APIs should be added. SharedLibraryInfo should be extended to
represent native shared libraries. The meaning of
ApplicationInfo.sharedLibraryFiles should be revised. Finally, the new
tag should be made public.

Bug: 142191088
Test: atest CtsUsesNativeLibraryTest
Change-Id: Iceb038aa86872d23e9faf582ae91b1fdcaf5c64c
parent 43c87db6
Loading
Loading
Loading
Loading
+20 −8
Original line number Diff line number Diff line
@@ -48,17 +48,18 @@ public class ApplicationLoaders {
                               ClassLoader parent, String classLoaderName) {
        return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
                              librarySearchPath, libraryPermittedPath, parent, classLoaderName,
                              null);
                              null, null);
    }

    ClassLoader getClassLoaderWithSharedLibraries(
            String zip, int targetSdkVersion, boolean isBundled,
            String librarySearchPath, String libraryPermittedPath,
            ClassLoader parent, String classLoaderName,
            List<ClassLoader> sharedLibraries) {
            List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries) {
        // For normal usage the cache key used is the same as the zip path.
        return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
                              libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
                              libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries,
                              nativeSharedLibraries);
    }

    /**
@@ -77,14 +78,22 @@ public class ApplicationLoaders {
            return loader;
        }

        // TODO(b/142191088): allow (Java) shared libraries to have <uses-native-library>
        // Until that is supported, assume that all native shared libraries are used.
        // "ALL" is a magic string that libnativeloader uses to unconditionally add all available
        // native shared libraries to the classloader.
        List<String> nativeSharedLibraries = new ArrayList<>();
        nativeSharedLibraries.add("ALL");
        return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
              librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries);
              librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries,
              nativeSharedLibraries);
    }

    private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                                       String librarySearchPath, String libraryPermittedPath,
                                       ClassLoader parent, String cacheKey,
                                       String classLoaderName, List<ClassLoader> sharedLibraries) {
                                       String classLoaderName, List<ClassLoader> sharedLibraries,
                                       List<String> nativeSharedLibraries) {
        /*
         * This is the parent we use if they pass "null" in.  In theory
         * this should be the "system" class loader; in practice we
@@ -113,7 +122,8 @@ public class ApplicationLoaders {

                ClassLoader classloader = ClassLoaderFactory.createClassLoader(
                        zip,  librarySearchPath, libraryPermittedPath, parent,
                        targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
                        targetSdkVersion, isBundled, classLoaderName, sharedLibraries,
                        nativeSharedLibraries);

                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

@@ -185,7 +195,8 @@ public class ApplicationLoaders {
        // assume cached libraries work with current sdk since they are built-in
        ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/,
                null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/,
                null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/);
                null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/,
                null /* nativeSharedLibraries */);

        if (classLoader == null) {
            // bad configuration or break in classloading code
@@ -255,7 +266,8 @@ public class ApplicationLoaders {
        // The cache key is passed separately to enable the stub WebView to be cached under the
        // stub's APK path, when the actual package path is the donor APK.
        return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
                              cacheKey, null /* classLoaderName */, null /* sharedLibraries */);
                              cacheKey, null /* classLoaderName */, null /* sharedLibraries */,
                              null /* nativeSharedLibraries */);
    }

    /**
+22 −1
Original line number Diff line number Diff line
@@ -412,6 +412,12 @@ public final class LoadedApk {
            return;
        }
        for (SharedLibraryInfo lib : sharedLibraries) {
            if (lib.isNative()) {
                // Native shared lib doesn't contribute to the native lib search path. Its name is
                // sent to libnativeloader and then the native shared lib is exported from the
                // default linker namespace.
                continue;
            }
            List<String> paths = lib.getAllCodePaths();
            outSeenPaths.addAll(paths);
            for (String path : paths) {
@@ -696,6 +702,12 @@ public final class LoadedApk {
        }
        List<ClassLoader> loaders = new ArrayList<>();
        for (SharedLibraryInfo info : sharedLibraries) {
            if (info.isNative()) {
                // Native shared lib doesn't contribute to the native lib search path. Its name is
                // sent to libnativeloader and then the native shared lib is exported from the
                // default linker namespace.
                continue;
            }
            loaders.add(createSharedLibraryLoader(
                    info, isBundledApp, librarySearchPath, libraryPermittedPath));
        }
@@ -898,10 +910,19 @@ public final class LoadedApk {
                    mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
                    libraryPermittedPath);

            List<String> nativeSharedLibraries = new ArrayList<>();
            if (mApplicationInfo.sharedLibraryInfos != null) {
                for (SharedLibraryInfo info : mApplicationInfo.sharedLibraryInfos) {
                    if (info.isNative()) {
                        nativeSharedLibraries.add(info.getName());
                    }
                }
            }

            mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
                    zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                    libraryPermittedPath, mBaseClassLoader,
                    mApplicationInfo.classLoaderName, sharedLibraries);
                    mApplicationInfo.classLoaderName, sharedLibraries, nativeSharedLibraries);
            mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);

            setThreadPolicy(oldPolicy);
+24 −1
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ public final class SharedLibraryInfo implements Parcelable {

    private final long mVersion;
    private final @Type int mType;
    private final boolean mIsNative;
    private final VersionedPackage mDeclaringPackage;
    private final List<VersionedPackage> mDependentPackages;
    private List<SharedLibraryInfo> mDependencies;
@@ -93,13 +94,14 @@ public final class SharedLibraryInfo implements Parcelable {
     * @param type The lib type.
     * @param declaringPackage The package that declares the library.
     * @param dependentPackages The packages that depend on the library.
     * @param isNative indicate if this shared lib is a native lib or not (i.e. java)
     *
     * @hide
     */
    public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
            String name, long version, int type,
            VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
            List<SharedLibraryInfo> dependencies) {
            List<SharedLibraryInfo> dependencies, boolean isNative) {
        mPath = path;
        mPackageName = packageName;
        mCodePaths = codePaths;
@@ -109,6 +111,16 @@ public final class SharedLibraryInfo implements Parcelable {
        mDeclaringPackage = declaringPackage;
        mDependentPackages = dependentPackages;
        mDependencies = dependencies;
        mIsNative = isNative;
    }

    /** @hide */
    public SharedLibraryInfo(String path, String packageName, List<String> codePaths,
            String name, long version, int type,
            VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages,
            List<SharedLibraryInfo> dependencies) {
        this(path, packageName, codePaths, name, version, type, declaringPackage, dependentPackages,
            dependencies, false /* isNative */);
    }

    private SharedLibraryInfo(Parcel parcel) {
@@ -125,6 +137,7 @@ public final class SharedLibraryInfo implements Parcelable {
        mDeclaringPackage = parcel.readParcelable(null);
        mDependentPackages = parcel.readArrayList(null);
        mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR);
        mIsNative = parcel.readBoolean();
    }

    /**
@@ -136,6 +149,15 @@ public final class SharedLibraryInfo implements Parcelable {
        return mType;
    }

    /**
     * Tells whether this library is a native shared library or not.
     *
     * @hide
     */
    public boolean isNative() {
        return mIsNative;
    }

    /**
     * Gets the library name an app defines in its manifest
     * to depend on the library.
@@ -320,6 +342,7 @@ public final class SharedLibraryInfo implements Parcelable {
        parcel.writeParcelable(mDeclaringPackage, flags);
        parcel.writeList(mDependentPackages);
        parcel.writeTypedList(mDependencies);
        parcel.writeBoolean(mIsNative);
    }

    private static String typeToString(int type) {
+6 −0
Original line number Diff line number Diff line
@@ -92,6 +92,10 @@ public interface ParsingPackage extends ParsingPackageRead {

    ParsingPackage addUsesOptionalLibrary(String libraryName);

    ParsingPackage addUsesNativeLibrary(String libraryName);

    ParsingPackage addUsesOptionalNativeLibrary(String libraryName);

    ParsingPackage addUsesStaticLibrary(String libraryName);

    ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
@@ -219,6 +223,8 @@ public interface ParsingPackage extends ParsingPackageRead {

    ParsingPackage removeUsesOptionalLibrary(String libraryName);

    ParsingPackage removeUsesOptionalNativeLibrary(String libraryName);

    ParsingPackage setAnyDensity(int anyDensity);

    ParsingPackage setAppComponentFactory(String appComponentFactory);
+44 −0
Original line number Diff line number Diff line
@@ -184,6 +184,13 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
    @DataClass.ParcelWith(ForInternedStringList.class)
    protected List<String> usesOptionalLibraries = emptyList();

    @NonNull
    @DataClass.ParcelWith(ForInternedStringList.class)
    protected List<String> usesNativeLibraries = emptyList();
    @NonNull
    @DataClass.ParcelWith(ForInternedStringList.class)
    protected List<String> usesOptionalNativeLibraries = emptyList();

    @NonNull
    @DataClass.ParcelWith(ForInternedStringList.class)
    private List<String> usesStaticLibraries = emptyList();
@@ -668,6 +675,27 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
        return this;
    }

    @Override
    public final ParsingPackageImpl addUsesOptionalNativeLibrary(String libraryName) {
        this.usesOptionalNativeLibraries = CollectionUtils.add(this.usesOptionalNativeLibraries,
                TextUtils.safeIntern(libraryName));
        return this;
    }

    @Override
    public final ParsingPackageImpl addUsesNativeLibrary(String libraryName) {
        this.usesNativeLibraries = CollectionUtils.add(this.usesNativeLibraries,
                TextUtils.safeIntern(libraryName));
        return this;
    }


    @Override public ParsingPackageImpl removeUsesOptionalNativeLibrary(String libraryName) {
        this.usesOptionalNativeLibraries = CollectionUtils.remove(this.usesOptionalNativeLibraries,
                libraryName);
        return this;
    }

    @Override
    public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
        this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
@@ -982,6 +1010,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
        sForInternedStringList.parcel(this.libraryNames, dest, flags);
        sForInternedStringList.parcel(this.usesLibraries, dest, flags);
        sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
        sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
        sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
        sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
        dest.writeLongArray(this.usesStaticLibrariesVersions);

@@ -1144,6 +1174,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
        this.libraryNames = sForInternedStringList.unparcel(in);
        this.usesLibraries = sForInternedStringList.unparcel(in);
        this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
        this.usesNativeLibraries = sForInternedStringList.unparcel(in);
        this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
        this.usesStaticLibraries = sForInternedStringList.unparcel(in);
        this.usesStaticLibrariesVersions = in.createLongArray();

@@ -1415,6 +1447,18 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
        return usesOptionalLibraries;
    }

    @NonNull
    @Override
    public List<String> getUsesNativeLibraries() {
        return usesNativeLibraries;
    }

    @NonNull
    @Override
    public List<String> getUsesOptionalNativeLibraries() {
        return usesOptionalNativeLibraries;
    }

    @NonNull
    @Override
    public List<String> getUsesStaticLibraries() {
Loading