From d237cf526ebd0a278cef590cfb61275a7ec2eb12 Mon Sep 17 00:00:00 2001 From: brian Date: Wed, 15 Apr 2026 17:25:15 -0400 Subject: [PATCH 1/9] maps: reuse selected module context in mapbox --- .../android/gms/maps/internal/CreatorImpl.java | 13 +++++++++++-- .../org/microg/gms/maps/mapbox/AbstractGoogleMap.kt | 4 ++-- .../kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt | 2 +- .../org/microg/gms/maps/mapbox/LiteGoogleMap.kt | 2 +- .../org/microg/gms/maps/mapbox/MapFragment.kt | 11 ++++++----- .../kotlin/org/microg/gms/maps/mapbox/MapView.kt | 6 +++--- .../org/microg/gms/maps/mapbox/utils/MapContext.kt | 7 ++++++- .../microg/gms/maps/mapbox/utils/MultiArchLoader.kt | 3 +-- .../java/org/microg/gms/maps/MapsContextLoader.java | 12 ++++++++++-- 9 files changed, 41 insertions(+), 19 deletions(-) diff --git a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java index 0041f0638b..10c1cd0451 100644 --- a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java +++ b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java @@ -40,6 +40,15 @@ @Keep public class CreatorImpl extends ICreator.Stub { private static final String TAG = "GmsMapCreator"; + private final Context mapsContext; + + public CreatorImpl() { + this(null); + } + + public CreatorImpl(Context mapsContext) { + this.mapsContext = mapsContext; + } @Override public void init(IObjectWrapper resources) { @@ -48,12 +57,12 @@ public void init(IObjectWrapper resources) { @Override public IMapFragmentDelegate newMapFragmentDelegate(IObjectWrapper activity) { - return new MapFragmentImpl(ObjectWrapper.unwrapTyped(activity, Activity.class)); + return new MapFragmentImpl(ObjectWrapper.unwrapTyped(activity, Activity.class), mapsContext); } @Override public IMapViewDelegate newMapViewDelegate(IObjectWrapper context, GoogleMapOptions options) { - return new MapViewImpl(ObjectWrapper.unwrapTyped(context, Context.class), options); + return new MapViewImpl(ObjectWrapper.unwrapTyped(context, Context.class), mapsContext, options); } @Override diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt index 05f3b216f4..93b51d0e05 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt @@ -15,9 +15,9 @@ import org.microg.gms.maps.mapbox.model.DefaultInfoWindowAdapter import org.microg.gms.maps.mapbox.model.InfoWindow import org.microg.gms.maps.mapbox.utils.MapContext -abstract class AbstractGoogleMap(context: Context) : IGoogleMapDelegate.Stub() { +abstract class AbstractGoogleMap(context: Context, mapsContext: Context?) : IGoogleMapDelegate.Stub() { - internal val mapContext = MapContext(context) + internal val mapContext = MapContext(context, mapsContext) val dpiFactor: Float get() = mapContext.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index e542fb6a53..ec5f0aa3f3 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -77,7 +77,7 @@ fun runOnMainLooper(forceQueue: Boolean = false, method: () -> Unit) { } } -class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context) { +class GoogleMapImpl(context: Context, mapsContext: Context?, var options: GoogleMapOptions) : AbstractGoogleMap(context, mapsContext) { val view: FrameLayout var map: MapboxMap? = null diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index f4395633c6..15d39e8e32 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -66,7 +66,7 @@ class MetaSnapshot( ) } -class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context) { +class LiteGoogleMapImpl(context: Context, mapsContext: Context?, var options: GoogleMapOptions) : AbstractGoogleMap(context, mapsContext) { internal val view: FrameLayout = FrameLayout(mapContext) val map: ImageView diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt index 69135ab299..d87bff2f18 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt @@ -17,6 +17,7 @@ package org.microg.gms.maps.mapbox import android.app.Activity +import android.content.Context import android.os.Bundle import android.os.Parcel import android.util.Base64 @@ -30,7 +31,7 @@ import com.google.android.gms.maps.internal.IGoogleMapDelegate import com.google.android.gms.maps.internal.IMapFragmentDelegate import com.google.android.gms.maps.internal.IOnMapReadyCallback -class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stub() { +class MapFragmentImpl(private val activity: Activity, private val mapsContext: Context?) : IMapFragmentDelegate.Stub() { private var map: IGoogleMapDelegate? = null private var options: GoogleMapOptions? = null @@ -54,9 +55,9 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu options = GoogleMapOptions() } if (options?.liteMode == true) { - map = LiteGoogleMapImpl(activity, options ?: GoogleMapOptions()) + map = LiteGoogleMapImpl(activity, mapsContext, options ?: GoogleMapOptions()) } else { - map = GoogleMapImpl(activity, options ?: GoogleMapOptions()) + map = GoogleMapImpl(activity, mapsContext, options ?: GoogleMapOptions()) } } @@ -67,9 +68,9 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu Log.d(TAG, "onCreateView: ${options?.camera?.target}") if (map == null) { map = if (options?.liteMode == true) { - LiteGoogleMapImpl(activity, options ?: GoogleMapOptions()) + LiteGoogleMapImpl(activity, mapsContext, options ?: GoogleMapOptions()) } else { - GoogleMapImpl(activity, options ?: GoogleMapOptions()) + GoogleMapImpl(activity, mapsContext, options ?: GoogleMapOptions()) } } map!!.apply { diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt index ec3231a6fd..13abb18255 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt @@ -27,7 +27,7 @@ import com.google.android.gms.maps.internal.IGoogleMapDelegate import com.google.android.gms.maps.internal.IMapViewDelegate import com.google.android.gms.maps.internal.IOnMapReadyCallback -class MapViewImpl(private val context: Context, options: GoogleMapOptions?) : IMapViewDelegate.Stub() { +class MapViewImpl(private val context: Context, private val mapsContext: Context?, options: GoogleMapOptions?) : IMapViewDelegate.Stub() { private val options: GoogleMapOptions = options ?: GoogleMapOptions() private var map: IGoogleMapDelegate? = null @@ -35,9 +35,9 @@ class MapViewImpl(private val context: Context, options: GoogleMapOptions?) : IM override fun onCreate(savedInstanceState: Bundle?) { Log.d(TAG, "onCreate: ${options?.camera?.target}") map = if (options.liteMode) { - LiteGoogleMapImpl(context, options) + LiteGoogleMapImpl(context, mapsContext, options) } else { - GoogleMapImpl(context, options) + GoogleMapImpl(context, mapsContext, options) }.apply { this.onCreate(savedInstanceState) } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt index f889239647..3ee9050055 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt @@ -24,7 +24,12 @@ import android.view.LayoutInflater import org.microg.gms.common.Constants import java.io.File -class MapContext(private val context: Context) : ContextWrapper(context.createPackageContext(Constants.GMS_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY)) { +class MapContext(private val context: Context, mapsContext: Context?) : ContextWrapper( + mapsContext ?: context.createPackageContext( + Constants.GMS_PACKAGE_NAME, + Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY + ) +) { private var layoutInflater: LayoutInflater? = null private val appContext: Context get() = context.applicationContext ?: context diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt index 5a37a6d83c..7d148981ea 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt @@ -21,7 +21,6 @@ import android.content.Context import android.content.pm.ApplicationInfo import android.util.Log import com.mapbox.mapboxsdk.LibraryLoader -import org.microg.gms.common.Constants import org.microg.gms.common.PackageUtils import java.io.* import java.util.zip.ZipFile @@ -42,7 +41,7 @@ class MultiArchLoader(private val mapContext: Context, private val appContext: C val cacheFileStamp = File("${appContext.cacheDir.absolutePath}/.gmscore/$path.stamp") val cacheVersion = kotlin.runCatching { cacheFileStamp.readText() }.getOrNull() // TODO: Use better version indicator - val mapVersion = PackageUtils.versionName(mapContext, Constants.GMS_PACKAGE_NAME) + val mapVersion = PackageUtils.versionName(mapContext, mapContext.applicationInfo.packageName) val apkFile = File(mapContext.packageCodePath) if (!cacheFile.exists() || cacheVersion == null || cacheVersion != mapVersion) { val zipFile = ZipFile(apkFile) diff --git a/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java b/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java index 4577b3d4a3..d6c551fcc3 100644 --- a/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java +++ b/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java @@ -18,6 +18,8 @@ import com.google.android.gms.maps.model.RuntimeRemoteException; import org.microg.gms.common.Constants; +import java.lang.reflect.InvocationTargetException; + public class MapsContextLoader { private static final String TAG = "MapsContextLoader"; private static final String DYNAMITE_MODULE_DEFAULT = "com.google.android.gms.maps_dynamite"; @@ -68,14 +70,20 @@ public static ICreator getCreator(Context context, @Nullable MapsInitializer.Ren try { Context mapsContext = getMapsContext(context, preferredRenderer); Class clazz = mapsContext.getClassLoader().loadClass("com.google.android.gms.maps.internal.CreatorImpl"); - creator = ICreator.Stub.asInterface((IBinder) clazz.newInstance()); + try { + creator = ICreator.Stub.asInterface((IBinder) clazz.getConstructor(Context.class).newInstance(mapsContext)); + } catch (NoSuchMethodException e) { + creator = ICreator.Stub.asInterface((IBinder) clazz.newInstance()); + } creator.initV2(ObjectWrapper.wrap(mapsContext.getResources()), Constants.GMS_VERSION_CODE); } catch (ClassNotFoundException e) { throw new IllegalStateException("Unable to find dynamic class com.google.android.gms.maps.internal.CreatorImpl"); } catch (IllegalAccessException e) { - throw new IllegalStateException("Unable to call the default constructor of com.google.android.gms.maps.internal.CreatorImpl"); + throw new IllegalStateException("Unable to call a constructor of com.google.android.gms.maps.internal.CreatorImpl"); } catch (InstantiationException e) { throw new IllegalStateException("Unable to instantiate the dynamic class com.google.android.gms.maps.internal.CreatorImpl"); + } catch (InvocationTargetException e) { + throw new IllegalStateException("Unable to invoke the constructor of com.google.android.gms.maps.internal.CreatorImpl", e); } catch (RemoteException e) { throw new RuntimeRemoteException(e); } From 19f52fa5c8260c8692ec7fa32f0b59a4364b10d8 Mon Sep 17 00:00:00 2001 From: brian Date: Wed, 15 Apr 2026 17:47:55 -0400 Subject: [PATCH 2/9] maps: restore MapContext formatting --- .../kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt index 3ee9050055..99817540ab 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt @@ -24,12 +24,7 @@ import android.view.LayoutInflater import org.microg.gms.common.Constants import java.io.File -class MapContext(private val context: Context, mapsContext: Context?) : ContextWrapper( - mapsContext ?: context.createPackageContext( - Constants.GMS_PACKAGE_NAME, - Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY - ) -) { +class MapContext(private val context: Context, mapsContext: Context?) : ContextWrapper(mapsContext ?: context.createPackageContext(Constants.GMS_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY)) { private var layoutInflater: LayoutInflater? = null private val appContext: Context get() = context.applicationContext ?: context From f867ebdf25ae4e2b81d8bd55473cfef5e1ed26c2 Mon Sep 17 00:00:00 2001 From: brian Date: Wed, 15 Apr 2026 19:37:45 -0400 Subject: [PATCH 3/9] maps: drop mapbox compatibility shim --- .../google/android/gms/maps/internal/CreatorImpl.java | 4 ---- .../org/microg/gms/maps/mapbox/AbstractGoogleMap.kt | 2 +- .../kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt | 2 +- .../kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt | 2 +- .../kotlin/org/microg/gms/maps/mapbox/MapFragment.kt | 2 +- .../main/kotlin/org/microg/gms/maps/mapbox/MapView.kt | 2 +- .../org/microg/gms/maps/mapbox/utils/MapContext.kt | 3 +-- .../java/org/microg/gms/maps/MapsContextLoader.java | 10 ++-------- 8 files changed, 8 insertions(+), 19 deletions(-) diff --git a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java index 10c1cd0451..01f56673a8 100644 --- a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java +++ b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java @@ -42,10 +42,6 @@ public class CreatorImpl extends ICreator.Stub { private static final String TAG = "GmsMapCreator"; private final Context mapsContext; - public CreatorImpl() { - this(null); - } - public CreatorImpl(Context mapsContext) { this.mapsContext = mapsContext; } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt index 93b51d0e05..f8062bec43 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt @@ -15,7 +15,7 @@ import org.microg.gms.maps.mapbox.model.DefaultInfoWindowAdapter import org.microg.gms.maps.mapbox.model.InfoWindow import org.microg.gms.maps.mapbox.utils.MapContext -abstract class AbstractGoogleMap(context: Context, mapsContext: Context?) : IGoogleMapDelegate.Stub() { +abstract class AbstractGoogleMap(context: Context, mapsContext: Context) : IGoogleMapDelegate.Stub() { internal val mapContext = MapContext(context, mapsContext) diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index ec5f0aa3f3..867c590e7c 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -77,7 +77,7 @@ fun runOnMainLooper(forceQueue: Boolean = false, method: () -> Unit) { } } -class GoogleMapImpl(context: Context, mapsContext: Context?, var options: GoogleMapOptions) : AbstractGoogleMap(context, mapsContext) { +class GoogleMapImpl(context: Context, mapsContext: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context, mapsContext) { val view: FrameLayout var map: MapboxMap? = null diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 15d39e8e32..1867201a5a 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -66,7 +66,7 @@ class MetaSnapshot( ) } -class LiteGoogleMapImpl(context: Context, mapsContext: Context?, var options: GoogleMapOptions) : AbstractGoogleMap(context, mapsContext) { +class LiteGoogleMapImpl(context: Context, mapsContext: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context, mapsContext) { internal val view: FrameLayout = FrameLayout(mapContext) val map: ImageView diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt index d87bff2f18..7819e6fe56 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt @@ -31,7 +31,7 @@ import com.google.android.gms.maps.internal.IGoogleMapDelegate import com.google.android.gms.maps.internal.IMapFragmentDelegate import com.google.android.gms.maps.internal.IOnMapReadyCallback -class MapFragmentImpl(private val activity: Activity, private val mapsContext: Context?) : IMapFragmentDelegate.Stub() { +class MapFragmentImpl(private val activity: Activity, private val mapsContext: Context) : IMapFragmentDelegate.Stub() { private var map: IGoogleMapDelegate? = null private var options: GoogleMapOptions? = null diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt index 13abb18255..da193adca2 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt @@ -27,7 +27,7 @@ import com.google.android.gms.maps.internal.IGoogleMapDelegate import com.google.android.gms.maps.internal.IMapViewDelegate import com.google.android.gms.maps.internal.IOnMapReadyCallback -class MapViewImpl(private val context: Context, private val mapsContext: Context?, options: GoogleMapOptions?) : IMapViewDelegate.Stub() { +class MapViewImpl(private val context: Context, private val mapsContext: Context, options: GoogleMapOptions?) : IMapViewDelegate.Stub() { private val options: GoogleMapOptions = options ?: GoogleMapOptions() private var map: IGoogleMapDelegate? = null diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt index 99817540ab..8902a23194 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt @@ -21,10 +21,9 @@ import android.content.ContextWrapper import android.content.Intent import android.content.SharedPreferences import android.view.LayoutInflater -import org.microg.gms.common.Constants import java.io.File -class MapContext(private val context: Context, mapsContext: Context?) : ContextWrapper(mapsContext ?: context.createPackageContext(Constants.GMS_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY)) { +class MapContext(private val context: Context, mapsContext: Context) : ContextWrapper(mapsContext) { private var layoutInflater: LayoutInflater? = null private val appContext: Context get() = context.applicationContext ?: context diff --git a/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java b/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java index d6c551fcc3..aac634ecf0 100644 --- a/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java +++ b/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java @@ -18,8 +18,6 @@ import com.google.android.gms.maps.model.RuntimeRemoteException; import org.microg.gms.common.Constants; -import java.lang.reflect.InvocationTargetException; - public class MapsContextLoader { private static final String TAG = "MapsContextLoader"; private static final String DYNAMITE_MODULE_DEFAULT = "com.google.android.gms.maps_dynamite"; @@ -70,11 +68,7 @@ public static ICreator getCreator(Context context, @Nullable MapsInitializer.Ren try { Context mapsContext = getMapsContext(context, preferredRenderer); Class clazz = mapsContext.getClassLoader().loadClass("com.google.android.gms.maps.internal.CreatorImpl"); - try { - creator = ICreator.Stub.asInterface((IBinder) clazz.getConstructor(Context.class).newInstance(mapsContext)); - } catch (NoSuchMethodException e) { - creator = ICreator.Stub.asInterface((IBinder) clazz.newInstance()); - } + creator = ICreator.Stub.asInterface((IBinder) clazz.getConstructor(Context.class).newInstance(mapsContext)); creator.initV2(ObjectWrapper.wrap(mapsContext.getResources()), Constants.GMS_VERSION_CODE); } catch (ClassNotFoundException e) { throw new IllegalStateException("Unable to find dynamic class com.google.android.gms.maps.internal.CreatorImpl"); @@ -82,7 +76,7 @@ public static ICreator getCreator(Context context, @Nullable MapsInitializer.Ren throw new IllegalStateException("Unable to call a constructor of com.google.android.gms.maps.internal.CreatorImpl"); } catch (InstantiationException e) { throw new IllegalStateException("Unable to instantiate the dynamic class com.google.android.gms.maps.internal.CreatorImpl"); - } catch (InvocationTargetException e) { + } catch (ReflectiveOperationException e) { throw new IllegalStateException("Unable to invoke the constructor of com.google.android.gms.maps.internal.CreatorImpl", e); } catch (RemoteException e) { throw new RuntimeRemoteException(e); From 2291b2b76c5da9e084dfb2d83953d5bdda5eb2ee Mon Sep 17 00:00:00 2001 From: brian Date: Thu, 16 Apr 2026 06:20:40 -0400 Subject: [PATCH 4/9] maps/mapbox: use loader-provided resources and classloader --- .../gms/maps/internal/CreatorImpl.java | 15 +++--- .../gms/maps/mapbox/AbstractGoogleMap.kt | 4 +- .../org/microg/gms/maps/mapbox/GoogleMap.kt | 4 +- .../microg/gms/maps/mapbox/LiteGoogleMap.kt | 2 +- .../org/microg/gms/maps/mapbox/MapFragment.kt | 10 ++-- .../org/microg/gms/maps/mapbox/MapView.kt | 6 +-- .../gms/maps/mapbox/utils/MapContext.kt | 23 ++++++++- .../gms/maps/mapbox/utils/MultiArchLoader.kt | 50 +++---------------- .../microg/gms/maps/MapsContextLoader.java | 6 +-- 9 files changed, 51 insertions(+), 69 deletions(-) diff --git a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java index 01f56673a8..ef6dd0dd47 100644 --- a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java +++ b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java @@ -36,15 +36,11 @@ import org.microg.gms.maps.mapbox.StreetViewPanoramaFragmentImpl; import org.microg.gms.maps.mapbox.StreetViewPanoramaViewImpl; import org.microg.gms.maps.mapbox.model.BitmapDescriptorFactoryImpl; +import org.microg.gms.maps.mapbox.utils.MapContext; @Keep public class CreatorImpl extends ICreator.Stub { private static final String TAG = "GmsMapCreator"; - private final Context mapsContext; - - public CreatorImpl(Context mapsContext) { - this.mapsContext = mapsContext; - } @Override public void init(IObjectWrapper resources) { @@ -53,12 +49,12 @@ public void init(IObjectWrapper resources) { @Override public IMapFragmentDelegate newMapFragmentDelegate(IObjectWrapper activity) { - return new MapFragmentImpl(ObjectWrapper.unwrapTyped(activity, Activity.class), mapsContext); + return new MapFragmentImpl(ObjectWrapper.unwrapTyped(activity, Activity.class)); } @Override public IMapViewDelegate newMapViewDelegate(IObjectWrapper context, GoogleMapOptions options) { - return new MapViewImpl(ObjectWrapper.unwrapTyped(context, Context.class), mapsContext, options); + return new MapViewImpl(ObjectWrapper.unwrapTyped(context, Context.class), options); } @Override @@ -73,8 +69,9 @@ public IBitmapDescriptorFactoryDelegate newBitmapDescriptorFactoryDelegate() { @Override public void initV2(IObjectWrapper resources, int flags) { - BitmapDescriptorFactoryImpl.INSTANCE.initialize(ObjectWrapper.unwrapTyped(resources, Resources.class), null); - //ResourcesContainer.set((Resources) ObjectWrapper.unwrap(resources)); + Resources mapResources = ObjectWrapper.unwrapTyped(resources, Resources.class); + BitmapDescriptorFactoryImpl.INSTANCE.initialize(mapResources, null); + MapContext.setModuleResources(mapResources); Log.d(TAG, "initV2 " + flags); } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt index f8062bec43..05f3b216f4 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/AbstractGoogleMap.kt @@ -15,9 +15,9 @@ import org.microg.gms.maps.mapbox.model.DefaultInfoWindowAdapter import org.microg.gms.maps.mapbox.model.InfoWindow import org.microg.gms.maps.mapbox.utils.MapContext -abstract class AbstractGoogleMap(context: Context, mapsContext: Context) : IGoogleMapDelegate.Stub() { +abstract class AbstractGoogleMap(context: Context) : IGoogleMapDelegate.Stub() { - internal val mapContext = MapContext(context, mapsContext) + internal val mapContext = MapContext(context) val dpiFactor: Float get() = mapContext.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index 867c590e7c..2ffc79c4fa 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -77,7 +77,7 @@ fun runOnMainLooper(forceQueue: Boolean = false, method: () -> Unit) { } } -class GoogleMapImpl(context: Context, mapsContext: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context, mapsContext) { +class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context) { val view: FrameLayout var map: MapboxMap? = null @@ -130,7 +130,7 @@ class GoogleMapImpl(context: Context, mapsContext: Context, var options: GoogleM init { BitmapDescriptorFactoryImpl.initialize(mapContext.resources, context.resources) - LibraryLoader.setLibraryLoader(MultiArchLoader(mapContext, context)) + LibraryLoader.setLibraryLoader(MultiArchLoader(mapContext)) runOnMainLooper { Mapbox.getInstance(mapContext, BuildConfig.MAPBOX_KEY, WellKnownTileServer.Mapbox) } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt index 1867201a5a..f4395633c6 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/LiteGoogleMap.kt @@ -66,7 +66,7 @@ class MetaSnapshot( ) } -class LiteGoogleMapImpl(context: Context, mapsContext: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context, mapsContext) { +class LiteGoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractGoogleMap(context) { internal val view: FrameLayout = FrameLayout(mapContext) val map: ImageView diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt index 7819e6fe56..281ebbfe48 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt @@ -31,7 +31,7 @@ import com.google.android.gms.maps.internal.IGoogleMapDelegate import com.google.android.gms.maps.internal.IMapFragmentDelegate import com.google.android.gms.maps.internal.IOnMapReadyCallback -class MapFragmentImpl(private val activity: Activity, private val mapsContext: Context) : IMapFragmentDelegate.Stub() { +class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stub() { private var map: IGoogleMapDelegate? = null private var options: GoogleMapOptions? = null @@ -55,9 +55,9 @@ class MapFragmentImpl(private val activity: Activity, private val mapsContext: C options = GoogleMapOptions() } if (options?.liteMode == true) { - map = LiteGoogleMapImpl(activity, mapsContext, options ?: GoogleMapOptions()) + map = LiteGoogleMapImpl(activity, options ?: GoogleMapOptions()) } else { - map = GoogleMapImpl(activity, mapsContext, options ?: GoogleMapOptions()) + map = GoogleMapImpl(activity, options ?: GoogleMapOptions()) } } @@ -68,9 +68,9 @@ class MapFragmentImpl(private val activity: Activity, private val mapsContext: C Log.d(TAG, "onCreateView: ${options?.camera?.target}") if (map == null) { map = if (options?.liteMode == true) { - LiteGoogleMapImpl(activity, mapsContext, options ?: GoogleMapOptions()) + LiteGoogleMapImpl(activity, options ?: GoogleMapOptions()) } else { - GoogleMapImpl(activity, mapsContext, options ?: GoogleMapOptions()) + GoogleMapImpl(activity, options ?: GoogleMapOptions()) } } map!!.apply { diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt index da193adca2..ec3231a6fd 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapView.kt @@ -27,7 +27,7 @@ import com.google.android.gms.maps.internal.IGoogleMapDelegate import com.google.android.gms.maps.internal.IMapViewDelegate import com.google.android.gms.maps.internal.IOnMapReadyCallback -class MapViewImpl(private val context: Context, private val mapsContext: Context, options: GoogleMapOptions?) : IMapViewDelegate.Stub() { +class MapViewImpl(private val context: Context, options: GoogleMapOptions?) : IMapViewDelegate.Stub() { private val options: GoogleMapOptions = options ?: GoogleMapOptions() private var map: IGoogleMapDelegate? = null @@ -35,9 +35,9 @@ class MapViewImpl(private val context: Context, private val mapsContext: Context override fun onCreate(savedInstanceState: Bundle?) { Log.d(TAG, "onCreate: ${options?.camera?.target}") map = if (options.liteMode) { - LiteGoogleMapImpl(context, mapsContext, options) + LiteGoogleMapImpl(context, options) } else { - GoogleMapImpl(context, mapsContext, options) + GoogleMapImpl(context, options) }.apply { this.onCreate(savedInstanceState) } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt index 8902a23194..252467c2d9 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt @@ -16,14 +16,16 @@ package org.microg.gms.maps.mapbox.utils +import android.content.res.AssetManager import android.content.Context import android.content.ContextWrapper import android.content.Intent +import android.content.res.Resources import android.content.SharedPreferences import android.view.LayoutInflater import java.io.File -class MapContext(private val context: Context, mapsContext: Context) : ContextWrapper(mapsContext) { +class MapContext(private val context: Context) : ContextWrapper(context) { private var layoutInflater: LayoutInflater? = null private val appContext: Context get() = context.applicationContext ?: context @@ -48,6 +50,14 @@ class MapContext(private val context: Context, mapsContext: Context) : ContextWr return appContext.packageName } + override fun getResources(): Resources { + return getModuleResources() + } + + override fun getAssets(): AssetManager { + return resources.assets + } + override fun getClassLoader(): ClassLoader { return MapContext::class.java.classLoader!! } @@ -75,6 +85,17 @@ class MapContext(private val context: Context, mapsContext: Context) : ContextWr } companion object { + private var moduleResources: Resources? = null + + @JvmStatic + fun setModuleResources(resources: Resources) { + moduleResources = resources + } + + private fun getModuleResources(): Resources { + return moduleResources ?: throw IllegalStateException("Resources have not been initialized") + } + val TAG = "GmsMapContext" } } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt index 7d148981ea..4ad143fe14 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt @@ -18,43 +18,16 @@ package org.microg.gms.maps.mapbox.utils import android.annotation.SuppressLint import android.content.Context -import android.content.pm.ApplicationInfo import android.util.Log import com.mapbox.mapboxsdk.LibraryLoader -import org.microg.gms.common.PackageUtils -import java.io.* -import java.util.zip.ZipFile -class MultiArchLoader(private val mapContext: Context, private val appContext: Context) : LibraryLoader() { +class MultiArchLoader(private val mapContext: Context) : LibraryLoader() { @SuppressLint("UnsafeDynamicallyLoadedCode") override fun load(name: String) { try { - val otherAppInfo = mapContext.packageManager.getApplicationInfo(appContext.packageName, 0) - var primaryCpuAbi = ApplicationInfo::class.java.getField("primaryCpuAbi").get(otherAppInfo) as String? - if (primaryCpuAbi == "armeabi") { - primaryCpuAbi = "armeabi-v7a" - } - if (primaryCpuAbi != null) { - val path = "lib/$primaryCpuAbi/lib$name.so" - val cacheFile = File("${appContext.cacheDir.absolutePath}/.gmscore/$path") - cacheFile.parentFile?.mkdirs() - val cacheFileStamp = File("${appContext.cacheDir.absolutePath}/.gmscore/$path.stamp") - val cacheVersion = kotlin.runCatching { cacheFileStamp.readText() }.getOrNull() - // TODO: Use better version indicator - val mapVersion = PackageUtils.versionName(mapContext, mapContext.applicationInfo.packageName) - val apkFile = File(mapContext.packageCodePath) - if (!cacheFile.exists() || cacheVersion == null || cacheVersion != mapVersion) { - val zipFile = ZipFile(apkFile) - val entry = zipFile.getEntry(path) - if (entry != null) { - copyInputStream(zipFile.getInputStream(entry), FileOutputStream(cacheFile)) - } else { - Log.d(TAG, "Can't load native library: $path does not exist in $apkFile") - } - cacheFileStamp.writeText(mapVersion.toString()) - } - Log.d(TAG, "Loading $name from ${cacheFile.getPath()}") - System.load(cacheFile.absolutePath) + findLibrary(name)?.let { path -> + Log.d(TAG, "Loading $name from $path") + System.load(path) return } } catch (e: Exception) { @@ -64,17 +37,10 @@ class MultiArchLoader(private val mapContext: Context, private val appContext: C System.loadLibrary(name) } - @Throws(IOException::class) - private fun copyInputStream(inp: InputStream, out: OutputStream) { - val buffer = ByteArray(1024) - var len: Int = inp.read(buffer) - while (len >= 0) { - out.write(buffer, 0, len) - len = inp.read(buffer) - } - - inp.close() - out.close() + private fun findLibrary(name: String): String? { + val method = ClassLoader::class.java.getDeclaredMethod("findLibrary", String::class.java) + method.isAccessible = true + return method.invoke(mapContext.classLoader, name) as? String } companion object { diff --git a/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java b/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java index aac634ecf0..4577b3d4a3 100644 --- a/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java +++ b/play-services-maps/src/main/java/org/microg/gms/maps/MapsContextLoader.java @@ -68,16 +68,14 @@ public static ICreator getCreator(Context context, @Nullable MapsInitializer.Ren try { Context mapsContext = getMapsContext(context, preferredRenderer); Class clazz = mapsContext.getClassLoader().loadClass("com.google.android.gms.maps.internal.CreatorImpl"); - creator = ICreator.Stub.asInterface((IBinder) clazz.getConstructor(Context.class).newInstance(mapsContext)); + creator = ICreator.Stub.asInterface((IBinder) clazz.newInstance()); creator.initV2(ObjectWrapper.wrap(mapsContext.getResources()), Constants.GMS_VERSION_CODE); } catch (ClassNotFoundException e) { throw new IllegalStateException("Unable to find dynamic class com.google.android.gms.maps.internal.CreatorImpl"); } catch (IllegalAccessException e) { - throw new IllegalStateException("Unable to call a constructor of com.google.android.gms.maps.internal.CreatorImpl"); + throw new IllegalStateException("Unable to call the default constructor of com.google.android.gms.maps.internal.CreatorImpl"); } catch (InstantiationException e) { throw new IllegalStateException("Unable to instantiate the dynamic class com.google.android.gms.maps.internal.CreatorImpl"); - } catch (ReflectiveOperationException e) { - throw new IllegalStateException("Unable to invoke the constructor of com.google.android.gms.maps.internal.CreatorImpl", e); } catch (RemoteException e) { throw new RuntimeRemoteException(e); } From 19b9d394ed3c849cc5d46cbc710729d494cc2997 Mon Sep 17 00:00:00 2001 From: brian Date: Thu, 16 Apr 2026 10:54:26 -0400 Subject: [PATCH 5/9] Stop rebuilding Mapbox state from package context --- .../google/android/gms/maps/internal/CreatorImpl.java | 9 ++++++++- .../kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt | 3 --- .../org/microg/gms/maps/mapbox/utils/MapContext.kt | 10 ++++++++-- .../microg/gms/maps/mapbox/utils/MultiArchLoader.kt | 8 +++----- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java index ef6dd0dd47..54143a00dc 100644 --- a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java +++ b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java @@ -29,6 +29,7 @@ import com.google.android.gms.maps.GoogleMapOptions; import com.google.android.gms.maps.StreetViewPanoramaOptions; import com.google.android.gms.maps.model.internal.IBitmapDescriptorFactoryDelegate; +import com.mapbox.mapboxsdk.LibraryLoader; import org.microg.gms.maps.mapbox.CameraUpdateFactoryImpl; import org.microg.gms.maps.mapbox.MapFragmentImpl; @@ -37,6 +38,7 @@ import org.microg.gms.maps.mapbox.StreetViewPanoramaViewImpl; import org.microg.gms.maps.mapbox.model.BitmapDescriptorFactoryImpl; import org.microg.gms.maps.mapbox.utils.MapContext; +import org.microg.gms.maps.mapbox.utils.MultiArchLoader; @Keep public class CreatorImpl extends ICreator.Stub { @@ -70,8 +72,13 @@ public IBitmapDescriptorFactoryDelegate newBitmapDescriptorFactoryDelegate() { @Override public void initV2(IObjectWrapper resources, int flags) { Resources mapResources = ObjectWrapper.unwrapTyped(resources, Resources.class); + ClassLoader moduleClassLoader = CreatorImpl.class.getClassLoader(); + if (moduleClassLoader == null) { + throw new IllegalStateException("Mapbox backend class loader is not available"); + } BitmapDescriptorFactoryImpl.INSTANCE.initialize(mapResources, null); - MapContext.setModuleResources(mapResources); + MapContext.setModuleEnvironment(mapResources, moduleClassLoader); + LibraryLoader.setLibraryLoader(new MultiArchLoader(moduleClassLoader)); Log.d(TAG, "initV2 " + flags); } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index 2ffc79c4fa..c09d3d3fbf 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -36,7 +36,6 @@ import com.google.android.gms.maps.internal.* import com.google.android.gms.maps.model.* import com.google.android.gms.maps.model.CircleOptions import com.google.android.gms.maps.model.internal.* -import com.mapbox.mapboxsdk.LibraryLoader import com.mapbox.mapboxsdk.Mapbox import com.mapbox.mapboxsdk.R import com.mapbox.mapboxsdk.camera.CameraUpdate @@ -60,7 +59,6 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory import com.mapbox.mapboxsdk.location.engine.* import com.mapbox.mapboxsdk.maps.OnMapReadyCallback import org.microg.gms.maps.mapbox.model.* -import org.microg.gms.maps.mapbox.utils.MultiArchLoader import org.microg.gms.maps.mapbox.utils.toGms import org.microg.gms.maps.mapbox.utils.toMapbox import java.util.concurrent.atomic.AtomicBoolean @@ -130,7 +128,6 @@ class GoogleMapImpl(context: Context, var options: GoogleMapOptions) : AbstractG init { BitmapDescriptorFactoryImpl.initialize(mapContext.resources, context.resources) - LibraryLoader.setLibraryLoader(MultiArchLoader(mapContext)) runOnMainLooper { Mapbox.getInstance(mapContext, BuildConfig.MAPBOX_KEY, WellKnownTileServer.Mapbox) } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt index 252467c2d9..19dd9416c2 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt @@ -59,7 +59,7 @@ class MapContext(private val context: Context) : ContextWrapper(context) { } override fun getClassLoader(): ClassLoader { - return MapContext::class.java.classLoader!! + return getModuleClassLoader() } override fun getSharedPreferences(name: String?, mode: Int): SharedPreferences { @@ -86,16 +86,22 @@ class MapContext(private val context: Context) : ContextWrapper(context) { companion object { private var moduleResources: Resources? = null + private var moduleClassLoader: ClassLoader? = null @JvmStatic - fun setModuleResources(resources: Resources) { + fun setModuleEnvironment(resources: Resources, classLoader: ClassLoader) { moduleResources = resources + moduleClassLoader = classLoader } private fun getModuleResources(): Resources { return moduleResources ?: throw IllegalStateException("Resources have not been initialized") } + private fun getModuleClassLoader(): ClassLoader { + return moduleClassLoader ?: throw IllegalStateException("Class loader has not been initialized") + } + val TAG = "GmsMapContext" } } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt index 4ad143fe14..e352879f6a 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt @@ -17,11 +17,11 @@ package org.microg.gms.maps.mapbox.utils import android.annotation.SuppressLint -import android.content.Context import android.util.Log import com.mapbox.mapboxsdk.LibraryLoader +import dalvik.system.BaseDexClassLoader -class MultiArchLoader(private val mapContext: Context) : LibraryLoader() { +class MultiArchLoader(private val moduleClassLoader: ClassLoader) : LibraryLoader() { @SuppressLint("UnsafeDynamicallyLoadedCode") override fun load(name: String) { try { @@ -38,9 +38,7 @@ class MultiArchLoader(private val mapContext: Context) : LibraryLoader() { } private fun findLibrary(name: String): String? { - val method = ClassLoader::class.java.getDeclaredMethod("findLibrary", String::class.java) - method.isAccessible = true - return method.invoke(mapContext.classLoader, name) as? String + return (moduleClassLoader as? BaseDexClassLoader)?.findLibrary(name) } companion object { From 9c99c7a6bd9cf4a917d6d3bb6c7cba0fbd76249f Mon Sep 17 00:00:00 2001 From: brian Date: Fri, 17 Apr 2026 00:50:57 -0400 Subject: [PATCH 6/9] remove leftover import --- .../src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt index 281ebbfe48..69135ab299 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt @@ -17,7 +17,6 @@ package org.microg.gms.maps.mapbox import android.app.Activity -import android.content.Context import android.os.Bundle import android.os.Parcel import android.util.Base64 From df278ba779f8803477d2596ba2be76fa6effb166 Mon Sep 17 00:00:00 2001 From: brian Date: Fri, 17 Apr 2026 06:00:30 -0400 Subject: [PATCH 7/9] fix "Resources$NotFoundException" startup error --- .../gms/maps/internal/CreatorImpl.java | 2 +- .../gms/maps/mapbox/utils/MapContext.kt | 87 +++++++++++++++---- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java index 54143a00dc..3526c1e810 100644 --- a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java +++ b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java @@ -77,7 +77,7 @@ public void initV2(IObjectWrapper resources, int flags) { throw new IllegalStateException("Mapbox backend class loader is not available"); } BitmapDescriptorFactoryImpl.INSTANCE.initialize(mapResources, null); - MapContext.setModuleEnvironment(mapResources, moduleClassLoader); + MapContext.setModuleEnvironment(moduleClassLoader); LibraryLoader.setLibraryLoader(new MultiArchLoader(moduleClassLoader)); Log.d(TAG, "initV2 " + flags); } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt index 19dd9416c2..c2324e825a 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt @@ -16,16 +16,15 @@ package org.microg.gms.maps.mapbox.utils -import android.content.res.AssetManager import android.content.Context import android.content.ContextWrapper import android.content.Intent -import android.content.res.Resources import android.content.SharedPreferences +import dalvik.system.BaseDexClassLoader import android.view.LayoutInflater import java.io.File -class MapContext(private val context: Context) : ContextWrapper(context) { +class MapContext(private val context: Context) : ContextWrapper(createModuleContext(context)) { private var layoutInflater: LayoutInflater? = null private val appContext: Context get() = context.applicationContext ?: context @@ -50,14 +49,6 @@ class MapContext(private val context: Context) : ContextWrapper(context) { return appContext.packageName } - override fun getResources(): Resources { - return getModuleResources() - } - - override fun getAssets(): AssetManager { - return resources.assets - } - override fun getClassLoader(): ClassLoader { return getModuleClassLoader() } @@ -85,23 +76,83 @@ class MapContext(private val context: Context) : ContextWrapper(context) { } companion object { - private var moduleResources: Resources? = null private var moduleClassLoader: ClassLoader? = null + private var modulePackageName: String? = null @JvmStatic - fun setModuleEnvironment(resources: Resources, classLoader: ClassLoader) { - moduleResources = resources + fun setModuleEnvironment(classLoader: ClassLoader) { moduleClassLoader = classLoader } - private fun getModuleResources(): Resources { - return moduleResources ?: throw IllegalStateException("Resources have not been initialized") - } - private fun getModuleClassLoader(): ClassLoader { return moduleClassLoader ?: throw IllegalStateException("Class loader has not been initialized") } + private fun createModuleContext(context: Context): Context { + return context.createPackageContext(getModulePackageName(context), Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY) + } + + private fun getModulePackageName(context: Context): String { + return modulePackageName ?: resolveModulePackageName(context).also { modulePackageName = it } + } + + private fun resolveModulePackageName(context: Context): String { + val dexPaths = getModuleDexPaths() + val applicationInfos = context.packageManager.getInstalledApplications(0) + for (applicationInfo in applicationInfos) { + if (applicationInfo.sourceDir in dexPaths || applicationInfo.publicSourceDir in dexPaths) { + return applicationInfo.packageName + } + val splitSourceDirs = applicationInfo.splitSourceDirs ?: continue + if (splitSourceDirs.any { it in dexPaths }) { + return applicationInfo.packageName + } + } + throw IllegalStateException("Package name could not be resolved from the backend class loader") + } + + private fun getModuleDexPaths(): Set { + val classLoader = getModuleClassLoader() + val dexPaths = linkedSetOf() + if (classLoader is BaseDexClassLoader) { + try { + val pathListField = BaseDexClassLoader::class.java.getDeclaredField("pathList") + pathListField.isAccessible = true + val pathList = pathListField.get(classLoader) + val dexElementsField = pathList.javaClass.getDeclaredField("dexElements") + dexElementsField.isAccessible = true + val dexElements = dexElementsField.get(pathList) as Array<*> + for (dexElement in dexElements) { + if (dexElement == null) continue + findDexPath(dexElement)?.let { dexPaths.add(it) } + } + } catch (_: Exception) { + } + } + if (dexPaths.isNotEmpty()) { + return dexPaths + } + val dexPathPattern = Regex("zip file \"([^\"]+)\"") + dexPathPattern.findAll(classLoader.toString()).forEach { dexPaths.add(it.groupValues[1]) } + if (dexPaths.isNotEmpty()) { + return dexPaths + } + throw IllegalStateException("Dex paths could not be resolved from the backend class loader") + } + + private fun findDexPath(dexElement: Any): String? { + for (fieldName in arrayOf("path", "zip", "file")) { + try { + val field = dexElement.javaClass.getDeclaredField(fieldName) + field.isAccessible = true + val value = field.get(dexElement) ?: continue + return if (value is File) value.path else value.toString() + } catch (_: NoSuchFieldException) { + } + } + return null + } + val TAG = "GmsMapContext" } } From f7668460ea10fe689552f4abf4bd5ff7db5609a2 Mon Sep 17 00:00:00 2001 From: brian Date: Fri, 17 Apr 2026 22:32:37 -0400 Subject: [PATCH 8/9] Revert dynamic MapContext package resolution --- .../gms/maps/mapbox/utils/MapContext.kt | 66 +------------------ 1 file changed, 2 insertions(+), 64 deletions(-) diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt index c2324e825a..2f4ea8613b 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt @@ -20,8 +20,8 @@ import android.content.Context import android.content.ContextWrapper import android.content.Intent import android.content.SharedPreferences -import dalvik.system.BaseDexClassLoader import android.view.LayoutInflater +import org.microg.gms.common.Constants import java.io.File class MapContext(private val context: Context) : ContextWrapper(createModuleContext(context)) { @@ -77,7 +77,6 @@ class MapContext(private val context: Context) : ContextWrapper(createModuleCont companion object { private var moduleClassLoader: ClassLoader? = null - private var modulePackageName: String? = null @JvmStatic fun setModuleEnvironment(classLoader: ClassLoader) { @@ -89,68 +88,7 @@ class MapContext(private val context: Context) : ContextWrapper(createModuleCont } private fun createModuleContext(context: Context): Context { - return context.createPackageContext(getModulePackageName(context), Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY) - } - - private fun getModulePackageName(context: Context): String { - return modulePackageName ?: resolveModulePackageName(context).also { modulePackageName = it } - } - - private fun resolveModulePackageName(context: Context): String { - val dexPaths = getModuleDexPaths() - val applicationInfos = context.packageManager.getInstalledApplications(0) - for (applicationInfo in applicationInfos) { - if (applicationInfo.sourceDir in dexPaths || applicationInfo.publicSourceDir in dexPaths) { - return applicationInfo.packageName - } - val splitSourceDirs = applicationInfo.splitSourceDirs ?: continue - if (splitSourceDirs.any { it in dexPaths }) { - return applicationInfo.packageName - } - } - throw IllegalStateException("Package name could not be resolved from the backend class loader") - } - - private fun getModuleDexPaths(): Set { - val classLoader = getModuleClassLoader() - val dexPaths = linkedSetOf() - if (classLoader is BaseDexClassLoader) { - try { - val pathListField = BaseDexClassLoader::class.java.getDeclaredField("pathList") - pathListField.isAccessible = true - val pathList = pathListField.get(classLoader) - val dexElementsField = pathList.javaClass.getDeclaredField("dexElements") - dexElementsField.isAccessible = true - val dexElements = dexElementsField.get(pathList) as Array<*> - for (dexElement in dexElements) { - if (dexElement == null) continue - findDexPath(dexElement)?.let { dexPaths.add(it) } - } - } catch (_: Exception) { - } - } - if (dexPaths.isNotEmpty()) { - return dexPaths - } - val dexPathPattern = Regex("zip file \"([^\"]+)\"") - dexPathPattern.findAll(classLoader.toString()).forEach { dexPaths.add(it.groupValues[1]) } - if (dexPaths.isNotEmpty()) { - return dexPaths - } - throw IllegalStateException("Dex paths could not be resolved from the backend class loader") - } - - private fun findDexPath(dexElement: Any): String? { - for (fieldName in arrayOf("path", "zip", "file")) { - try { - val field = dexElement.javaClass.getDeclaredField(fieldName) - field.isAccessible = true - val value = field.get(dexElement) ?: continue - return if (value is File) value.path else value.toString() - } catch (_: NoSuchFieldException) { - } - } - return null + return context.createPackageContext(Constants.GMS_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY) } val TAG = "GmsMapContext" From 3c59f775e0f202ac1fd74d8e53636bafc7c060f5 Mon Sep 17 00:00:00 2001 From: brian Date: Fri, 24 Apr 2026 12:32:11 -0400 Subject: [PATCH 9/9] Revert explicit MapContext --- .../android/gms/maps/internal/CreatorImpl.java | 1 - .../org/microg/gms/maps/mapbox/utils/MapContext.kt | 13 +------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java index 3526c1e810..c18d24d110 100644 --- a/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java +++ b/play-services-maps/core/mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java @@ -77,7 +77,6 @@ public void initV2(IObjectWrapper resources, int flags) { throw new IllegalStateException("Mapbox backend class loader is not available"); } BitmapDescriptorFactoryImpl.INSTANCE.initialize(mapResources, null); - MapContext.setModuleEnvironment(moduleClassLoader); LibraryLoader.setLibraryLoader(new MultiArchLoader(moduleClassLoader)); Log.d(TAG, "initV2 " + flags); } diff --git a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt index 2f4ea8613b..c47c92c721 100644 --- a/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt +++ b/play-services-maps/core/mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MapContext.kt @@ -50,7 +50,7 @@ class MapContext(private val context: Context) : ContextWrapper(createModuleCont } override fun getClassLoader(): ClassLoader { - return getModuleClassLoader() + return MapContext::class.java.classLoader!! } override fun getSharedPreferences(name: String?, mode: Int): SharedPreferences { @@ -76,17 +76,6 @@ class MapContext(private val context: Context) : ContextWrapper(createModuleCont } companion object { - private var moduleClassLoader: ClassLoader? = null - - @JvmStatic - fun setModuleEnvironment(classLoader: ClassLoader) { - moduleClassLoader = classLoader - } - - private fun getModuleClassLoader(): ClassLoader { - return moduleClassLoader ?: throw IllegalStateException("Class loader has not been initialized") - } - private fun createModuleContext(context: Context): Context { return context.createPackageContext(Constants.GMS_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY) }