2

I am making an app that make something happen when the phone gets a call, so after a lot of searching I discover that I can use a BroadcastReceiver and a Service so I created a Service called PhoneStateService:

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.telephony.TelephonyManager;
import android.widget.Toast;

public class PhoneStateService extends Service {

    private static BroadcastReceiver broadcastReceiver;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {
        registerBroadcastReceiver();
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(broadcastReceiver);
        broadcastReceiver = null;
    }

    private void registerBroadcastReceiver() {
        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
                if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
                    new PhoneStateHandler().onIncomingCallStarted(context);
                }
            }
        };
        IntentFilter filter = new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(broadcastReceiver, filter);
    }
}

This is the code for starting PhoneStateService(inside MainActivity):

if(PermissionChecker.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PermissionChecker.PERMISSION_GRANTED) {
        startService(new Intent(getApplicationContext(), PhoneStateService.class));
    }

part from the Manifest file:

<service android:name=".Service.PhoneStateService" />

When the phone gets a call and the app is running (it's still didn't Destroy) everything is working good but after I close the app and the phone gets a call the app crash and give me the message that the app has stop working.

please tell me what is the problem, I didn't find answer.

Edit:

The Exception:

09-11 10:26:30.598 29461-29461/? E/AndroidRuntime: FATAL EXCEPTION: main
                                               Process: com.multiplies.multiring, PID: 29461
                                               java.lang.RuntimeException: Error receiving broadcast Intent { act=android.intent.action.SCREEN_OFF flg=0x50000010 } in com.multiplies.multiring.Service.PhoneStateService$1@1924f18
                                                   at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:891)
                                                   at android.os.Handler.handleCallback(Handler.java:739)
                                                   at android.os.Handler.dispatchMessage(Handler.java:95)
                                                   at android.os.Looper.loop(Looper.java:148)
                                                   at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                   at java.lang.reflect.Method.invoke(Native Method)
                                                   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.os.Bundle.getString(java.lang.String)' on a null object reference
                                                   at com.multiplies.multiring.Service.PhoneStateService$1.onReceive(PhoneStateService.java:36)
                                                   at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:881)
                                                   at android.os.Handler.handleCallback(Handler.java:739) 
                                                   at android.os.Handler.dispatchMessage(Handler.java:95) 
                                                   at android.os.Looper.loop(Looper.java:148) 
                                                   at android.app.ActivityThread.main(ActivityThread.java:5417) 
                                                   at java.lang.reflect.Method.invoke(Native Method) 
                                                   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
                                                   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

Edit:

I did what some people said:

String stateStr = intent.getExtras() != null ? intent.getExtras().getString(TelephonyManager.EXTRA_STATE) : "";

put the this doesn't fix the problem. I thought it won't work because if the intent.getExtras() is returning null, then the if statement won't work because it's returning null and it's want TelephonyManager.EXTRA_STATE_RINGING:

if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
                new PhoneStateHandler().onIncomingCallStarted(context);
}

but thank you for your help.

HUGE EDIT:

part of the Manifest:

<service android:name=".PhoneState.PhoneStateService" />
    <receiver android:name=".PhoneState.PhoneStateBroadcast">
    <intent-filter>
        <action android:name="android.intent.action.PHONE_STATE" />
    </intent-filter>
    </receiver>

PhoneStateBroadcast:

    import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.util.Log;

public class PhoneStateBroadcast extends BroadcastReceiver {

    Context context;

    public void onReceive(Context context, Intent intent) {
        this.context = context;

        try {

            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);

            switch (telephonyManager.getCallState()) {

                case TelephonyManager.CALL_STATE_RINGING:
                    context.startService(new Intent(context, PhoneStateService.class));
                    break;
            }


        } catch (Exception e) {
            Log.e("Phone Receive Error", " " + e);
        }

    }
}

PhoneStateService:

    import android.app.IntentService;
import android.content.Intent;

public class PhoneStateService extends IntentService {

    public PhoneStateService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        new PhoneStateHandler().onIncomingCallStarted(getApplicationContext());
    }
}

after this edit I am still getting crash.

2 Answers2

3

For some reason the extra's are empty. You are doing intent.getExtras().XXX but intent.getExtras() returns null, calling .XXX on it throws an error.

Changing

String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);

into

String stateStr = intent.getExtras() != null ? intent.getExtras().getString(TelephonyManager.EXTRA_STATE) : "";

will stop the crash from happening, but you might want to look into what the reason is why you expect extra's but there aren't any in the intent.

Frank
  • 12,010
  • 8
  • 61
  • 78
  • thank you, but this mean that for some reason when the app is closed its return null, and that mean the service currently can't work if the app is closed, so if know anything can be useful please tell me. and thank you again. –  Sep 11 '17 at 07:59
  • you might want to try this first, maybe the broadcast receiver gets called multiple times for different calling states and the above ignores some states while successfully processing the one you are looking for. – Frank Sep 11 '17 at 08:02
2

instead of Running a service All the time, make it a BroadcastReciever which is fired whenever the call state is changed.(Running service for a long period of time is not a good practice) thus by using Broadcast receiver, your service would run exactly when you want.

public class PhoneStateService extends BroadcastReceiver{

       public void onReceive(Context context, Intent intent) {
            mContext = context;

                try {

                    TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
                    String deviceId=tm.getDeviceId();

                    switch (tm.getCallState()) {

                        case TelephonyManager.CALL_STATE_RINGING:  // incoming call

                     //do something on Incoming call event

                            break;

                        case TelephonyManager.CALL_STATE_IDLE:  //call Ends

                             //do something on Idle phone state i.e set your logic if an incoming call is complete then this block would execute.


                    }


                } catch (Exception e) {
                    Log.e("Phone Receive Error", " " + e);
                }

        }


    }

Then add this broadcastReciever to your Manifest under application tag

  <receiver android:name="orbitsys.com.prospect_call.CallerState">
                <intent-filter>
                    <action android:name="android.intent.action.PHONE_STATE" />
                </intent-filter>
abhi rathi
  • 59
  • 3