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

priority: p0Highest priority. Critical issue. P0 implies highest priority.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions