Unable to start receiver AlarmManagerSchedulerBroadcastReceiver: Attempt to invoke method 'android.net.Uri.getQueryParameter()' on a null object

Viewed 781

I recently started getting this crash for my application in production.

Full StackTrace is as following :

Fatal Exception: java.lang.RuntimeException: Unable to start receiver com.google.android.datatransport.runtime.scheduling.jobscheduling.AlarmManagerSchedulerBroadcastReceiver: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.getQueryParameter(java.lang.String)' on a null object reference
   at android.app.ActivityThread.handleReceiver(ActivityThread.java:2735)
   at android.app.ActivityThread.-wrap14(ActivityThread.java)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1424)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:148)
   at android.app.ActivityThread.main(ActivityThread.java:5421)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.getQueryParameter(java.lang.String)' on a null object reference
   at com.google.android.datatransport.runtime.scheduling.jobscheduling.AlarmManagerSchedulerBroadcastReceiver.onReceive(AlarmManagerSchedulerBroadcastReceiver.java:31)
   at android.app.ActivityThread.handleReceiver(ActivityThread.java:2728)
   at android.app.ActivityThread.-wrap14(ActivityThread.java)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1424)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:148)
   at android.app.ActivityThread.main(ActivityThread.java:5421)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

I could reach to the code of AlarmManagerSchedulerBroadcastReceiver and it is as follows :

package com.google.android.datatransport.runtime.scheduling.jobscheduling;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Base64;
import com.google.android.datatransport.runtime.TransportContext;
import com.google.android.datatransport.runtime.TransportRuntime;
import com.google.android.datatransport.runtime.TransportContext.Builder;
import com.google.android.datatransport.runtime.util.PriorityMapping;

public class AlarmManagerSchedulerBroadcastReceiver extends BroadcastReceiver {
   public AlarmManagerSchedulerBroadcastReceiver() {
    }

public void onReceive(Context context, Intent intent) {
    String backendName = intent.getData().getQueryParameter("backendName");
    String extras = intent.getData().getQueryParameter("extras");
    int priority = Integer.valueOf(intent.getData().getQueryParameter("priority"));
    int attemptNumber = intent.getExtras().getInt("attemptNumber");
    TransportRuntime.initialize(context);
    Builder transportContext = TransportContext.builder().setBackendName(backendName).setPriority(PriorityMapping.valueOf(priority));
    if (extras != null) {
        transportContext.setExtras(Base64.decode(extras, 0));
    }

    TransportRuntime.getInstance().getUploader().upload(transportContext.build(), attemptNumber, AlarmManagerSchedulerBroadcastReceiver$$Lambda$1.lambdaFactory$());
}

}

I searched the entire codebase-scope and found that This BroadcastReceiver is being used from :

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.datatransport.runtime" >

<uses-sdk
    android:minSdkVersion="14"
    android:targetSdkVersion="29" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Although the *SdkVersion is captured in gradle build files, this is required for non gradle builds -->
<!-- <uses-sdk android:minSdkVersion="14"/> -->
<application>
    <service
        android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.JobInfoSchedulerService"
        android:exported="false"
        android:permission="android.permission.BIND_JOB_SERVICE" >
    </service>

    <receiver
        android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.AlarmManagerSchedulerBroadcastReceiver"
        android:exported="false" />

    <service
        android:name="com.google.android.datatransport.runtime.backends.TransportBackendDiscovery"
        android:exported="false" />
</application>

I could find that AlarmManagerScheduler is triggering this :

package com.google.android.datatransport.runtime.scheduling.jobscheduling;



public class AlarmManagerScheduler implements WorkScheduler {
private static final String LOG_TAG = "AlarmManagerScheduler";
static final String ATTEMPT_NUMBER = "attemptNumber";
static final String BACKEND_NAME = "backendName";
static final String EVENT_PRIORITY = "priority";
static final String EXTRAS = "extras";
private final Context context;
private final EventStore eventStore;
private AlarmManager alarmManager;
private final SchedulerConfig config;
private final Clock clock;

public AlarmManagerScheduler(Context applicationContext, EventStore eventStore, Clock clock, SchedulerConfig config) {
    this(applicationContext, eventStore, (AlarmManager)applicationContext.getSystemService("alarm"), clock, config);
}

@VisibleForTesting
AlarmManagerScheduler(Context applicationContext, EventStore eventStore, AlarmManager alarmManager, Clock clock, SchedulerConfig config) {
    this.context = applicationContext;
    this.eventStore = eventStore;
    this.alarmManager = alarmManager;
    this.clock = clock;
    this.config = config;
}

@VisibleForTesting
boolean isJobServiceOn(Intent intent) {
    return PendingIntent.getBroadcast(this.context, 0, intent, 536870912) != null;
}

public void schedule(TransportContext transportContext, int attemptNumber) {
    Builder intentDataBuilder = new Builder();
    intentDataBuilder.appendQueryParameter("backendName", transportContext.getBackendName());
    intentDataBuilder.appendQueryParameter("priority", String.valueOf(PriorityMapping.toInt(transportContext.getPriority())));
    if (transportContext.getExtras() != null) {
        intentDataBuilder.appendQueryParameter("extras", Base64.encodeToString(transportContext.getExtras(), 0));
    }

    Intent intent = new Intent(this.context, AlarmManagerSchedulerBroadcastReceiver.class);
    intent.setData(intentDataBuilder.build());
    intent.putExtra("attemptNumber", attemptNumber);
    if (this.isJobServiceOn(intent)) {
        Logging.d("AlarmManagerScheduler", "Upload for context %s is already scheduled. Returning...", transportContext);
    } else {
        long backendTime = this.eventStore.getNextCallTime(transportContext);
        long scheduleDelay = this.config.getScheduleDelay(transportContext.getPriority(), backendTime, attemptNumber);
        Logging.d("AlarmManagerScheduler", "Scheduling upload for context %s in %dms(Backend next call timestamp %d). Attempt %d", new Object[]{transportContext, scheduleDelay, backendTime, attemptNumber});
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this.context, 0, intent, 0);
        this.alarmManager.set(3, this.clock.getTime() + scheduleDelay, pendingIntent);
    }
}
}

I don't understand, why intent.getData() is getting null?

Can someone help me in resolving this crash?

2 Answers

It looks like .getData() is returning null, you should look where you are adding this data and why the app are not sending it.

You should check nullability in your method:

public void onReceive(Context context, Intent intent) {
    if(intent.getData() != null){
      String backendName = intent.getData().getQueryParameter("backendName");
      String extras = intent.getData().getQueryParameter("extras");
      int priority = 
      Integer.valueOf(intent.getData().getQueryParameter("priority"));
      int attemptNumber = intent.getExtras().getInt("attemptNumber");
      TransportRuntime.initialize(context);
      Builder transportContext = TransportContext.builder().setBackendName(backendName).setPriority(PriorityMapping.valueOf(priority));
      if (extras != null) {
        transportContext.setExtras(Base64.decode(extras, 0));
      }

    
 TransportRuntime.getInstance().getUploader().upload(transportContext.build(), attemptNumber, AlarmManagerSchedulerBroadcastReceiver$$Lambda$1.lambdaFactory$());
    }

}
Related