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

Verified Commit 5d50ae37 authored by Marvin W.'s avatar Marvin W. 🐿️
Browse files

Adjust dynamite loader to support chimera modules with merged class loaders

parent e42f6849
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -3,8 +3,12 @@ package com.google.android.gms.dynamite;
import com.google.android.gms.dynamic.IObjectWrapper;

interface IDynamiteLoader {
    int getModuleVersion(IObjectWrapper context, String moduleId) = 0;
    int getModuleVersion2(IObjectWrapper context, String moduleId, boolean updateConfigIfRequired) = 2;
    int getModuleVersion(IObjectWrapper wrappedContext, String moduleId) = 0;
    int getModuleVersion2(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired) = 2;
    int getModuleVersion2NoCrashUtils(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired) = 4;

    IObjectWrapper createModuleContext(IObjectWrapper context, String moduleId, int minVersion) = 1;
    IObjectWrapper createModuleContext(IObjectWrapper wrappedContext, String moduleId, int minVersion) = 1;
    IObjectWrapper createModuleContextNoCrashUtils(IObjectWrapper wrappedContext, String moduleId, int minVersion) = 3;

    int getIDynamiteLoaderVersion() = 5;
}
+95 −0
Original line number Diff line number Diff line
/*
 * SPDX-FileCopyrightText: 2021, microG Project Team
 * SPDX-License-Identifier: Apache-2.0
 */

package com.google.android.gms.chimera.container;

import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Process;
import android.util.Log;

import androidx.annotation.RequiresApi;

import org.microg.gms.common.Constants;

import java.io.File;

import dalvik.system.PathClassLoader;

public class DynamiteContext extends ContextWrapper {
    private static final String TAG = "DynamiteContext";
    private String moduleId;
    private Context originalContext;
    private Context gmsContext;
    private DynamiteContext appContext;

    public DynamiteContext(String moduleId, Context base, Context gmsContext, DynamiteContext appContext) {
        super(base);
        this.moduleId = moduleId;
        this.originalContext = base;
        this.gmsContext = gmsContext;
        this.appContext = appContext;
    }

    @Override
    public ClassLoader getClassLoader() {
        if (new DynamiteModuleInfo(moduleId).isMergeClassLoader()) {
            StringBuilder nativeLoaderDirs = new StringBuilder(gmsContext.getApplicationInfo().nativeLibraryDir);
            if (Build.VERSION.SDK_INT >= 23 && Process.is64Bit()) {
                for (String abi : Build.SUPPORTED_64_BIT_ABIS) {
                    nativeLoaderDirs.append(File.pathSeparator).append(gmsContext.getApplicationInfo().sourceDir).append("!/lib/").append(abi);
                }
            } else if (Build.VERSION.SDK_INT >= 21) {
                for (String abi : Build.SUPPORTED_32_BIT_ABIS) {
                    nativeLoaderDirs.append(File.pathSeparator).append(gmsContext.getApplicationInfo().sourceDir).append("!/lib/").append(abi);
                }
            } else {
                nativeLoaderDirs.append(File.pathSeparator).append(gmsContext.getApplicationInfo().sourceDir).append("!/lib/").append(Build.CPU_ABI);
            }
            return new PathClassLoader(gmsContext.getApplicationInfo().sourceDir, nativeLoaderDirs.toString(), originalContext.getClassLoader());
        } else {
            return gmsContext.getClassLoader();
        }
    }

    @Override
    public String getPackageName() {
        return gmsContext.getPackageName();
    }

    @Override
    public ApplicationInfo getApplicationInfo() {
        return gmsContext.getApplicationInfo();
    }

    @Override
    public Context getApplicationContext() {
        return appContext;
    }

    @RequiresApi(24)
    @Override
    public Context createDeviceProtectedStorageContext() {
        return new DynamiteContext(moduleId, originalContext.createDeviceProtectedStorageContext(), gmsContext.createDeviceProtectedStorageContext(), appContext);
    }

    public static DynamiteContext create(String moduleId, Context originalContext) {
        try {
            Context gmsContext = originalContext.createPackageContext(Constants.GMS_PACKAGE_NAME, new DynamiteModuleInfo(moduleId).getCreatePackageOptions());
            Context originalAppContext = originalContext.getApplicationContext();
            if (originalAppContext == null || originalAppContext == originalContext) {
                return new DynamiteContext(moduleId, originalContext, gmsContext, null);
            } else {
                return new DynamiteContext(moduleId, originalContext, gmsContext, new DynamiteContext(moduleId, originalAppContext, gmsContext, null));
            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.w(TAG, e);
            return null;
        }
    }
}
+28 −15
Original line number Diff line number Diff line
@@ -35,28 +35,41 @@ public class DynamiteLoaderImpl extends IDynamiteLoader.Stub {

    @Override
    public IObjectWrapper createModuleContext(IObjectWrapper wrappedContext, String moduleId, int minVersion) throws RemoteException {
        Log.d(TAG, "createModuleContext for " + moduleId + " at version " + minVersion);
        final Context context = (Context) ObjectWrapper.unwrap(wrappedContext);
        try {
            return ObjectWrapper.wrap(new ContextWrapper(context.createPackageContext(Constants.GMS_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY)) {
        // We don't have crash utils, so just forward
        return createModuleContextNoCrashUtils(wrappedContext, moduleId, minVersion);
    }

    @Override
                public Context getApplicationContext() {
                    return context;
    public IObjectWrapper createModuleContextNoCrashUtils(IObjectWrapper wrappedContext, String moduleId, int minVersion) throws RemoteException {
        Log.d(TAG, "createModuleContext for " + moduleId + " at version " + minVersion);
        final Context originalContext = (Context) ObjectWrapper.unwrap(wrappedContext);
        return ObjectWrapper.wrap(DynamiteContext.create(moduleId, originalContext));
    }
            });
        } catch (PackageManager.NameNotFoundException e) {
            Log.w(TAG, "returning null instead", e);
            return null;

    @Override
    public int getIDynamiteLoaderVersion() throws RemoteException {
        return 2;
    }

    @Override
    public int getModuleVersion(IObjectWrapper wrappedContext, String moduleId) throws RemoteException {
        return getModuleVersion2(wrappedContext, moduleId, true);
    }

    @Override
    public int getModuleVersion(IObjectWrapper context, String moduleId) throws RemoteException {
        return getModuleVersion2(context, moduleId, true);
    public int getModuleVersion2(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired) throws RemoteException {
        // We don't have crash utils, so just forward
        return getModuleVersion2NoCrashUtils(wrappedContext, moduleId, updateConfigIfRequired);
    }

    @Override
    public int getModuleVersion2(IObjectWrapper context, String moduleId, boolean updateConfigIfRequired) throws RemoteException {
    public int getModuleVersion2NoCrashUtils(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired) throws RemoteException {
        final Context context = (Context) ObjectWrapper.unwrap(wrappedContext);
        if (context == null) {
            Log.w(TAG, "Invalid client context");
            return 0;
        }

        try {
            return Class.forName("com.google.android.gms.dynamite.descriptors." + moduleId + ".ModuleDescriptor").getDeclaredField("MODULE_VERSION").getInt(null);
        } catch (Exception e) {
+51 −0
Original line number Diff line number Diff line
/*
 * SPDX-FileCopyrightText: 2021, microG Project Team
 * SPDX-License-Identifier: Apache-2.0
 */

package com.google.android.gms.chimera.container;

import static android.content.Context.CONTEXT_IGNORE_SECURITY;
import static android.content.Context.CONTEXT_INCLUDE_CODE;

public class DynamiteModuleInfo {
    private Class<?> descriptor;
    private String moduleId;

    public DynamiteModuleInfo(String moduleId) {
        this.moduleId = moduleId;
        try {
            this.descriptor = Class.forName("com.google.android.gms.dynamite.descriptors." + moduleId + ".ModuleDescriptor");
        } catch (Exception e) {
            // Ignore
        }
    }

    public String getModuleId() {
        return moduleId;
    }

    public int getVersion() {
        try {
            return descriptor.getDeclaredField("MODULE_VERSION").getInt(null);
        } catch (Exception e) {
            return 0;
        }
    }

    public int getCreatePackageOptions() {
        try {
            return descriptor.getDeclaredField("CREATE_PACKAGE_OPTIONS").getInt(null);
        } catch (Exception e) {
            return CONTEXT_INCLUDE_CODE | CONTEXT_IGNORE_SECURITY;
        }
    }

    public boolean isMergeClassLoader() {
        try {
            return descriptor.getDeclaredField("MERGE_CLASS_LOADER").getBoolean(null);
        } catch (Exception e) {
            return false;
        }
    }
}