Skip to content

Android crash: NullPointerException in RoadSnappedLocationProvider.LocationListener.onRawLocationUpdate #560

@christian-apollo

Description

@christian-apollo

Bug description

Calling stopUpdatingLocation (which calls removeLocationListener) can cause a fatal NullPointerException on Android. The crash occurs inside the Navigation SDK's internal code when onRawLocationUpdate is invoked on a listener reference that has been nulled during concurrent removal.

Stack trace

Fatal Exception: java.lang.NullPointerException: Attempt to invoke interface method
  'void com.google.android.libraries.navigation.RoadSnappedLocationProvider$LocationListener
  .onRawLocationUpdate(android.location.Location)' on a null object reference
    at com.google.android.libraries.navigation.internal.aas.gz.b(PG:1)
    at com.google.android.libraries.navigation.internal.xz.e.a(PG:9)
    at com.google.android.libraries.navigation.internal.io.l.b(PG:8)
    at com.google.android.libraries.navigation.internal.io.i.h(PG:1)
    at com.google.android.libraries.navigation.internal.io.n.run(n.java:1)
    at android.os.Handler.handleCallback(Handler.java:995)
    at android.os.Handler.dispatchMessage(Handler.java:103)
    at android.os.Looper.loopOnce(Looper.java:273)
    at android.os.Looper.loop(Looper.java:363)
    at android.app.ActivityThread.main(ActivityThread.java:9939)

Root cause

There is a race condition between the SDK's internal location-delivery thread and NavModule.removeLocationListener():

  1. The SDK's internal thread prepares to invoke listener.onRawLocationUpdate(location)
  2. Concurrently, stopUpdatingLocationremoveLocationListener() calls mRoadSnappedLocationProvider.removeLocationListener(mLocationListener), which nulls the SDK's internal reference to the listener
  3. The SDK's internal thread then tries to invoke the method on the now-null reference → NPE

The entire crash stack is within com.google.android.libraries.navigation.internal.* (obfuscated), confirming this is a thread-safety issue in the SDK's listener dispatch.

Suggested fix

The onLocationChanged and onRawLocationUpdate callbacks in NavModule already guard on the mIsListeningRoadSnappedLocation flag and are no-ops when it is false. Therefore, it is safe to not call removeLocationListener() during normal start/stop cycles — just toggle the flag. The listener should only be fully removed during cleanup().

// Before (crashes):
@Override
public void stopUpdatingLocation(final Promise promise) {
    mIsListeningRoadSnappedLocation = false;
    removeLocationListener();  // ← triggers SDK race condition
    promise.resolve(null);
}

// After (safe):
@Override
public void stopUpdatingLocation(final Promise promise) {
    mIsListeningRoadSnappedLocation = false;
    // Don't remove the listener — callbacks are already no-ops when flag is false.
    promise.resolve(null);
}

Similarly, registerLocationListener() should skip the remove-and-recreate cycle if a listener is already registered:

private void registerLocationListener() {
    if (mLocationListener != null) {
        return;  // Already registered; just flip the flag in startUpdatingLocation
    }
    // ... create and register new listener ...
}

Environment

  • @googlemaps/react-native-navigation-sdk: 0.14.2
  • com.google.android.libraries.navigation:navigation: 7.4.0
  • Platform: Android
  • Crash observed on production devices via Crashlytics

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions