diff --git a/play-services-api/src/main/aidl/com/google/android/gms/dynamite/IDynamiteLoader.aidl b/play-services-api/src/main/aidl/com/google/android/gms/dynamite/IDynamiteLoader.aidl index 8cb2be99..017a2dc2 100644 --- a/play-services-api/src/main/aidl/com/google/android/gms/dynamite/IDynamiteLoader.aidl +++ b/play-services-api/src/main/aidl/com/google/android/gms/dynamite/IDynamiteLoader.aidl @@ -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; -} \ No newline at end of file + IObjectWrapper createModuleContext(IObjectWrapper wrappedContext, String moduleId, int minVersion) = 1; + IObjectWrapper createModuleContextNoCrashUtils(IObjectWrapper wrappedContext, String moduleId, int minVersion) = 3; + + int getIDynamiteLoaderVersion() = 5; +} 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 new file mode 100644 index 00000000..d97a79e9 --- /dev/null +++ b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteContext.java @@ -0,0 +1,95 @@ +/* + * 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; + } + } +} diff --git a/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteLoaderImpl.java b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteLoaderImpl.java index d4a272af..e27537d3 100644 --- a/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteLoaderImpl.java +++ b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteLoaderImpl.java @@ -35,28 +35,41 @@ public class DynamiteLoaderImpl extends IDynamiteLoader.Stub { @Override public IObjectWrapper createModuleContext(IObjectWrapper wrappedContext, String moduleId, int minVersion) throws RemoteException { + // We don't have crash utils, so just forward + return createModuleContextNoCrashUtils(wrappedContext, moduleId, minVersion); + } + + @Override + 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)); + } + + @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 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 getModuleVersion2NoCrashUtils(IObjectWrapper wrappedContext, String moduleId, boolean updateConfigIfRequired) throws RemoteException { 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)) { - @Override - public Context getApplicationContext() { - return context; - } - }); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "returning null instead", e); - return null; + if (context == null) { + Log.w(TAG, "Invalid client context"); + return 0; } - } - @Override - public int getModuleVersion(IObjectWrapper context, String moduleId) throws RemoteException { - return getModuleVersion2(context, moduleId, true); - } - - @Override - public int getModuleVersion2(IObjectWrapper context, String moduleId, boolean updateConfigIfRequired) throws RemoteException { try { return Class.forName("com.google.android.gms.dynamite.descriptors." + moduleId + ".ModuleDescriptor").getDeclaredField("MODULE_VERSION").getInt(null); } catch (Exception 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 new file mode 100644 index 00000000..3489ba9f --- /dev/null +++ b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteModuleInfo.java @@ -0,0 +1,51 @@ +/* + * 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; + } + } +}