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

Commit de898ff4 authored by Adam Lesinski's avatar Adam Lesinski
Browse files

Shared library resource support

Shared libraries can now export resources for applications
to use.

Exporting resources works the same way the framework exports
resources, by defining the public symbols in res/values/public.xml.

Building a shared library requires aapt to be invoked with the
--shared-lib option. Shared libraries will be assigned a package
ID of 0x00 at build-time. At runtime, all loaded shared libraries
will be assigned a new package ID.

Currently, shared libraries should not import other shared libraries,
as those dependencies will not be loaded at runtime.

At runtime, reflection is used to update the package ID of resource
symbols in the shared library's R class file. The package name of
the R class file is assumed to be the same as the shared library's
package name declared in its manifest. This will be customizable in
a future commit.

See /tests/SharedLibrary/ for examples of a shared library and its
client.

Bug:12724178
Change-Id: I60c0cb8ab87849f8f8a1a13431562fe8603020a7
parent 05f79758
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -119,7 +119,8 @@ namespace {


    int parse_manifest(const void *data, size_t size, const char *target_package_name)
    int parse_manifest(const void *data, size_t size, const char *target_package_name)
    {
    {
        ResXMLTree parser(data, size);
        ResXMLTree parser;
        parser.setTo(data, size);
        if (parser.getError() != NO_ERROR) {
        if (parser.getError() != NO_ERROR) {
            ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
            ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
            return -1;
            return -1;
+2 −2
Original line number Original line Diff line number Diff line
@@ -1592,10 +1592,10 @@ public final class ActivityThread {
    /**
    /**
     * Creates the top level resources for the given package.
     * Creates the top level resources for the given package.
     */
     */
    Resources getTopLevelResources(String resDir, String[] overlayDirs,
    Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs,
            int displayId, Configuration overrideConfiguration,
            int displayId, Configuration overrideConfiguration,
            LoadedApk pkgInfo) {
            LoadedApk pkgInfo) {
        return mResourcesManager.getTopLevelResources(resDir, overlayDirs, displayId,
        return mResourcesManager.getTopLevelResources(resDir, overlayDirs, libDirs, displayId,
                overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
                overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
    }
    }


+1 −1
Original line number Original line Diff line number Diff line
@@ -805,7 +805,7 @@ final class ApplicationPackageManager extends PackageManager {
        }
        }
        Resources r = mContext.mMainThread.getTopLevelResources(
        Resources r = mContext.mMainThread.getTopLevelResources(
                app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
                app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
                app.resourceDirs, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
                app.resourceDirs, null, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
        if (r != null) {
        if (r != null) {
            return r;
            return r;
        }
        }
+3 −2
Original line number Original line Diff line number Diff line
@@ -2044,8 +2044,9 @@ class ContextImpl extends Context {
                    || (compatInfo != null && compatInfo.applicationScale
                    || (compatInfo != null && compatInfo.applicationScale
                            != resources.getCompatibilityInfo().applicationScale)) {
                            != resources.getCompatibilityInfo().applicationScale)) {
                resources = mResourcesManager.getTopLevelResources(
                resources = mResourcesManager.getTopLevelResources(
                        packageInfo.getResDir(), packageInfo.getOverlayDirs(), displayId,
                        packageInfo.getResDir(), packageInfo.getOverlayDirs(),
                        overrideConfiguration, compatInfo, activityToken);
                        packageInfo.getApplicationInfo().sharedLibraryFiles,
                        displayId, overrideConfiguration, compatInfo, activityToken);
            }
            }
        }
        }
        mResources = resources;
        mResources = resources;
+96 −2
Original line number Original line Diff line number Diff line
@@ -41,6 +41,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.UserHandle;
import android.util.AndroidRuntimeException;
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayAdjustments;
import android.view.DisplayAdjustments;
import android.view.Display;
import android.view.Display;


@@ -48,6 +49,8 @@ import java.io.File;
import java.io.IOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URL;
import java.util.Enumeration;
import java.util.Enumeration;


@@ -485,7 +488,7 @@ public final class LoadedApk {
    public Resources getResources(ActivityThread mainThread) {
    public Resources getResources(ActivityThread mainThread) {
        if (mResources == null) {
        if (mResources == null) {
            mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs,
            mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs,
                    Display.DEFAULT_DISPLAY, null, this);
                    mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
        }
        }
        return mResources;
        return mResources;
    }
    }
@@ -531,9 +534,100 @@ public final class LoadedApk {
            }
            }
        }
        }


        // Rewrite the R 'constants' for all library apks.
        SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
                .getAssignedPackageIdentifiers();
        final int N = packageIdentifiers.size();
        for (int i = 0; i < N; i++) {
            final int id = packageIdentifiers.keyAt(i);
            if (id == 0x01 || id == 0x7f) {
                continue;
            }

            rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
        }

        return app;
        return app;
    }
    }


    private void rewriteIntField(Field field, int packageId) throws IllegalAccessException {
        int requiredModifiers = Modifier.STATIC | Modifier.PUBLIC;
        int bannedModifiers = Modifier.FINAL;

        int mod = field.getModifiers();
        if ((mod & requiredModifiers) != requiredModifiers ||
                (mod & bannedModifiers) != 0) {
            throw new IllegalArgumentException("Field " + field.getName() +
                    " is not rewritable");
        }

        if (field.getType() != int.class && field.getType() != Integer.class) {
            throw new IllegalArgumentException("Field " + field.getName() +
                    " is not an integer");
        }

        try {
            int resId = field.getInt(null);
            field.setInt(null, (resId & 0x00ffffff) | (packageId << 24));
        } catch (IllegalAccessException e) {
            // This should not occur (we check above if we can write to it)
            throw new IllegalArgumentException(e);
        }
    }

    private void rewriteIntArrayField(Field field, int packageId) {
        int requiredModifiers = Modifier.STATIC | Modifier.PUBLIC;

        if ((field.getModifiers() & requiredModifiers) != requiredModifiers) {
            throw new IllegalArgumentException("Field " + field.getName() +
                    " is not rewritable");
        }

        if (field.getType() != int[].class) {
            throw new IllegalArgumentException("Field " + field.getName() +
                    " is not an integer array");
        }

        try {
            int[] array = (int[]) field.get(null);
            for (int i = 0; i < array.length; i++) {
                array[i] = (array[i] & 0x00ffffff) | (packageId << 24);
            }
        } catch (IllegalAccessException e) {
            // This should not occur (we check above if we can write to it)
            throw new IllegalArgumentException(e);
        }
    }

    private void rewriteRValues(ClassLoader cl, String packageName, int id) {
        try {
            final Class<?> rClazz = cl.loadClass(packageName + ".R");
            Class<?>[] declaredClasses = rClazz.getDeclaredClasses();
            for (Class<?> clazz : declaredClasses) {
                try {
                    if (clazz.getSimpleName().equals("styleable")) {
                        for (Field field : clazz.getDeclaredFields()) {
                            if (field.getType() == int[].class) {
                                rewriteIntArrayField(field, id);
                            }
                        }

                    } else {
                        for (Field field : clazz.getDeclaredFields()) {
                            rewriteIntField(field, id);
                        }
                    }
                } catch (Exception e) {
                    throw new IllegalArgumentException("Failed to rewrite R values for " +
                            clazz.getName(), e);
                }
            }

        } catch (Exception e) {
            throw new IllegalArgumentException("Failed to rewrite R values", e);
        }
    }

    public void removeContextRegistrations(Context context,
    public void removeContextRegistrations(Context context,
            String who, String what) {
            String who, String what) {
        final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
        final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
Loading