diff --git a/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteContext.java b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteContext.java index d97a79e9..7f104fbe 100644 --- a/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteContext.java +++ b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteContext.java @@ -23,14 +23,14 @@ import dalvik.system.PathClassLoader; public class DynamiteContext extends ContextWrapper { private static final String TAG = "DynamiteContext"; - private String moduleId; + private DynamiteModuleInfo moduleInfo; private Context originalContext; private Context gmsContext; private DynamiteContext appContext; - public DynamiteContext(String moduleId, Context base, Context gmsContext, DynamiteContext appContext) { + public DynamiteContext(DynamiteModuleInfo moduleInfo, Context base, Context gmsContext, DynamiteContext appContext) { super(base); - this.moduleId = moduleId; + this.moduleInfo = moduleInfo; this.originalContext = base; this.gmsContext = gmsContext; this.appContext = appContext; @@ -38,23 +38,19 @@ public class DynamiteContext extends ContextWrapper { @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); + 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); } - return new PathClassLoader(gmsContext.getApplicationInfo().sourceDir, nativeLoaderDirs.toString(), originalContext.getClassLoader()); } else { - return gmsContext.getClassLoader(); + nativeLoaderDirs.append(File.pathSeparator).append(gmsContext.getApplicationInfo().sourceDir).append("!/lib/").append(Build.CPU_ABI); } + return new PathClassLoader(gmsContext.getApplicationInfo().sourceDir, nativeLoaderDirs.toString(), new FilteredClassLoader(originalContext.getClassLoader(), moduleInfo.getMergedClasses(), moduleInfo.getMergedPackages())); } @Override @@ -75,17 +71,18 @@ public class DynamiteContext extends ContextWrapper { @RequiresApi(24) @Override public Context createDeviceProtectedStorageContext() { - return new DynamiteContext(moduleId, originalContext.createDeviceProtectedStorageContext(), gmsContext.createDeviceProtectedStorageContext(), appContext); + return new DynamiteContext(moduleInfo, 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()); + DynamiteModuleInfo moduleInfo = new DynamiteModuleInfo(moduleId); + Context gmsContext = originalContext.createPackageContext(Constants.GMS_PACKAGE_NAME, 0); Context originalAppContext = originalContext.getApplicationContext(); if (originalAppContext == null || originalAppContext == originalContext) { - return new DynamiteContext(moduleId, originalContext, gmsContext, null); + return new DynamiteContext(moduleInfo, originalContext, gmsContext, null); } else { - return new DynamiteContext(moduleId, originalContext, gmsContext, new DynamiteContext(moduleId, originalAppContext, gmsContext, null)); + return new DynamiteContext(moduleInfo, originalContext, gmsContext, new DynamiteContext(moduleInfo, originalAppContext, gmsContext, null)); } } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, e); diff --git a/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteModuleInfo.java b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteModuleInfo.java index 3489ba9f..637a269f 100644 --- a/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteModuleInfo.java +++ b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteModuleInfo.java @@ -5,6 +5,9 @@ package com.google.android.gms.chimera.container; +import java.util.Collection; +import java.util.Collections; + import static android.content.Context.CONTEXT_IGNORE_SECURITY; import static android.content.Context.CONTEXT_INCLUDE_CODE; @@ -33,19 +36,19 @@ public class DynamiteModuleInfo { } } - public int getCreatePackageOptions() { + public Collection getMergedPackages() { try { - return descriptor.getDeclaredField("CREATE_PACKAGE_OPTIONS").getInt(null); + return (Collection) descriptor.getDeclaredField("MERGED_PACKAGES").get(null); } catch (Exception e) { - return CONTEXT_INCLUDE_CODE | CONTEXT_IGNORE_SECURITY; + return Collections.emptySet(); } } - public boolean isMergeClassLoader() { + public Collection getMergedClasses() { try { - return descriptor.getDeclaredField("MERGE_CLASS_LOADER").getBoolean(null); + return (Collection) descriptor.getDeclaredField("MERGED_CLASSES").get(null); } catch (Exception e) { - return false; + return Collections.emptySet(); } } } diff --git a/play-services-core/src/main/java/com/google/android/gms/chimera/container/FilteredClassLoader.java b/play-services-core/src/main/java/com/google/android/gms/chimera/container/FilteredClassLoader.java new file mode 100644 index 00000000..76731fc3 --- /dev/null +++ b/play-services-core/src/main/java/com/google/android/gms/chimera/container/FilteredClassLoader.java @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.chimera.container; + +import android.util.Log; + +import java.util.Collection; +import java.util.HashSet; + +public class FilteredClassLoader extends ClassLoader { + private static ClassLoader rootClassLoader; + private final Collection allowedClasses; + private final Collection allowedPackages; + + static { + rootClassLoader = ClassLoader.getSystemClassLoader(); + if (rootClassLoader == null) { + rootClassLoader = FilteredClassLoader.class.getClassLoader(); + while (rootClassLoader.getParent() != null) { + rootClassLoader = rootClassLoader.getParent(); + } + } + } + + public FilteredClassLoader(ClassLoader parent, Collection allowedClasses, Collection allowedPackages) { + super(parent); + this.allowedClasses = new HashSet<>(allowedClasses); + this.allowedPackages = new HashSet<>(allowedPackages); + } + + private String getPackageName(String name) { + int lastIndex = name.lastIndexOf("."); + if (lastIndex <= 0) return ""; + return name.substring(0, lastIndex); + } + + private String getClassName(String name) { + int lastIndex = name.indexOf("$"); + if (lastIndex <= 0) return name; + return name.substring(0, lastIndex); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.startsWith("java.")) return rootClassLoader.loadClass(name); + if (allowedClasses.contains(name) || allowedClasses.contains(getClassName(name))) + return super.loadClass(name, resolve); + if (allowedClasses.contains("!" + name) || allowedClasses.contains("!" + getClassName(name))) + return rootClassLoader.loadClass(name); + if (allowedPackages.contains(getPackageName(name))) return super.loadClass(name, resolve); + return rootClassLoader.loadClass(name); + } +}