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

Commit b6c3a604 authored by Alan Stokes's avatar Alan Stokes
Browse files

DexLoadReporter needs to handle arbitrary class loaders.

Also made sure we can handle null classpaths.

Test: atest -p services/core/java/com/android/server/pm/dex
Bug: 111336847
Change-Id: Idabf3fb9a09a0764e805679ac29cd8455e8dc267
parent 009b2dd1
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ import java.util.Set;
    }

    @Override
    public void report(List<BaseDexClassLoader> classLoadersChain, List<String> classPaths) {
    public void report(List<ClassLoader> classLoadersChain, List<String> classPaths) {
        if (classLoadersChain.size() != classPaths.size()) {
            Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch");
            return;
@@ -113,12 +113,12 @@ import java.util.Set;
        registerSecondaryDexForProfiling(dexPathsForRegistration);
    }

    private void notifyPackageManager(List<BaseDexClassLoader> classLoadersChain,
    private void notifyPackageManager(List<ClassLoader> classLoadersChain,
            List<String> classPaths) {
        // Get the class loader names for the binder call.
        List<String> classLoadersNames = new ArrayList<>(classPaths.size());
        for (BaseDexClassLoader bdc : classLoadersChain) {
            classLoadersNames.add(bdc.getClass().getName());
        for (ClassLoader classLoader : classLoadersChain) {
            classLoadersNames.add(classLoader.getClass().getName());
        }
        String packageName = ActivityThread.currentPackageName();
        try {
+1 −1
Original line number Diff line number Diff line
@@ -475,7 +475,7 @@ interface IPackageManager {
     * @param classPaths the class paths corresponding to the class loaders names from
     *     {@param classLoadersNames}. The the first element corresponds to the first class loader
     *     and so on. A classpath is represented as a list of dex files separated by
     *     {@code File.pathSeparator}.
     *     {@code File.pathSeparator}, or null if the class loader's classpath is not known.
     *     The dex files found in the first class path will be recorded in the usage file.
     * @param loaderIsa the ISA of the loader process
     */
+11 −3
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.util.Slog;
import android.util.jar.StrictJarFile;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
@@ -153,7 +154,7 @@ public class DexManager {
     * @param classPaths the class paths corresponding to the class loaders names from
     *     {@param classLoadersNames}. The the first element corresponds to the first class loader
     *     and so on. A classpath is represented as a list of dex files separated by
     *     {@code File.pathSeparator}.
     *     {@code File.pathSeparator}, or null if the class loader's classpath is not known.
     *     The dex files found in the first class path will be recorded in the usage file.
     * @param loaderIsa the ISA of the app loading the dex files
     * @param loaderUserId the user id which runs the code loading the dex files
@@ -169,7 +170,8 @@ public class DexManager {
        }
    }

    private void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
    @VisibleForTesting
    /*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
            List<String> classLoaderNames, List<String> classPaths, String loaderIsa,
            int loaderUserId) {
        if (classLoaderNames.size() != classPaths.size()) {
@@ -186,8 +188,14 @@ public class DexManager {
            return;
        }

        // The first classpath should never be null because the first classloader
        // should always be an instance of BaseDexClassLoader.
        String firstClassPath = classPaths.get(0);
        if (firstClassPath == null) {
            return;
        }
        // The classpath is represented as a list of dex files separated by File.pathSeparator.
        String[] dexPathsToRegister = classPaths.get(0).split(File.pathSeparator);
        String[] dexPathsToRegister = firstClassPath.split(File.pathSeparator);

        // Encode the class loader contexts for the dexPathsToRegister.
        String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
+2 −1
Original line number Diff line number Diff line
@@ -318,7 +318,8 @@ public final class DexoptUtils {
        // is fine (they come over binder). Even if something changes we expect the sizes to be
        // very small and it shouldn't matter much.
        for (int i = 1; i < classLoadersNames.size(); i++) {
            if (!ClassLoaderFactory.isValidClassLoaderName(classLoadersNames.get(i))) {
            if (!ClassLoaderFactory.isValidClassLoaderName(classLoadersNames.get(i))
                || classPaths.get(i) == null) {
                return null;
            }
            String classpath = encodeClasspath(classPaths.get(i).split(File.pathSeparator));
+28 −6
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ public class DexManagerTests {
    private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
    private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
            DelegateLastClassLoader.class.getName();
    private static final String UNSUPPORTED_CLASS_LOADER_NAME = "unsupported.class_loader";

    @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
    @Mock Installer mInstaller;
@@ -106,7 +107,7 @@ public class DexManagerTests {
        mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);

        mBarUser0UnsupportedClassLoader = new TestData(bar, isa, mUser0,
                "unsupported.class_loader");
                UNSUPPORTED_CLASS_LOADER_NAME);
        mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
                DELEGATE_LAST_CLASS_LOADER_NAME);

@@ -405,6 +406,24 @@ public class DexManagerTests {
        assertNoUseInfo(mBarUser0UnsupportedClassLoader);
    }

    @Test
    public void testNotifySupportedAndUnsupportedClassLoader() {
        String classPath = String.join(File.pathSeparator, mBarUser0.getSecondaryDexPaths());
        List<String> classLoaders =
                Arrays.asList(PATH_CLASS_LOADER_NAME, UNSUPPORTED_CLASS_LOADER_NAME);
        List<String> classPaths = Arrays.asList(classPath, classPath);
        notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0);

        assertNoUseInfo(mBarUser0);
    }

    @Test
    public void testNotifyNullClassPath() {
        notifyDexLoad(mBarUser0, null, mUser0);

        assertNoUseInfo(mBarUser0);
    }

    @Test
    public void testNotifyVariableClassLoader() {
        // Record bar secondaries with the default PathClassLoader.
@@ -500,14 +519,17 @@ public class DexManagerTests {
        // By default, assume a single class loader in the chain.
        // This makes writing tests much easier.
        List<String> classLoaders = Arrays.asList(testData.mClassLoader);
        List<String> classPaths = Arrays.asList(String.join(File.pathSeparator, dexPaths));
        List<String> classPaths = (dexPaths == null)
                                  ? Arrays.asList((String) null)
                                  : Arrays.asList(String.join(File.pathSeparator, dexPaths));
        notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
    }

    private void notifyDexLoad(TestData testData, List<String> classLoader, List<String> classPaths,
            int loaderUserId) {
        mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, classLoader, classPaths,
                testData.mLoaderIsa, loaderUserId);
    private void notifyDexLoad(TestData testData, List<String> classLoaders,
            List<String> classPaths, int loaderUserId) {
        // We call the internal function so any exceptions thrown cause test failures.
        mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, classLoaders,
                classPaths, testData.mLoaderIsa, loaderUserId);
    }

    private PackageUseInfo getPackageUseInfo(TestData testData) {
Loading