简体   繁体   中英

Unable to receive SIP calls when app is killed

What should I do to receive SIP calls even when app is in background, exits or restarts? Currently we're running a foreground service with sticky notification to keep connected with our sip server. This solution isn't completely working Please help me to achieve this, Thanks in advance!

Firstly, you want a Mainservice that never stops (Even when the device booted, with the help of a bootup receiver). You have to initialise your sip manager, sip profile and call related codes in the MainService and not in the MainActivity.

Then, You need a BroadCast receiver that receives incoming sip calls. see below example,

public class YourReceiver extends BroadcastReceiver {

SipAudioCall incomingCall   = null;
private static YourReceiver instance;
MainService mainService;
public Intent incomingCallIntent;

public static YourReceiver getInstance() {
    return instance;
}

/**
 * Processes the incoming call, answers it, and hands it over to the
 * MainActivity.
 * @param context The context under which the receiver is running.
 * @param intent The intent being received.
 */
@Override
public void onReceive(final Context context, Intent intent) {
    Log.i(TAG, "onReceive: ");
    instance    = this;
    mainService = MainService.getInstance();

    try {
        SipAudioCall.Listener listener = new SipAudioCall.Listener() {
            @Override
            public void onRinging(SipAudioCall call, SipProfile caller) {
                Log.i(TAG, "onRinging: ");


            }

            // extra added
            @Override
            public void onRingingBack(SipAudioCall call) {
                Log.i(TAG, "onRingingBack: ");
                super.onRingingBack(call);
            }

            @Override
            public void onReadyToCall(SipAudioCall call) {
                Log.i(TAG, "onReadyToCall: ");
                super.onReadyToCall(call);
            }

            @Override
            public void onError(SipAudioCall call, int errorCode, String errorMessage) {
                Log.e(TAG, "onError: errorCode = " + errorCode + ", errorMessage = " + errorMessage);
                super.onError(call, errorCode, errorMessage);
            }

            @Override
            public void onChanged(SipAudioCall call) {
                Log.i(TAG, "onChanged: ");
                super.onChanged(call);
            }

            @Override
            public void onCalling(SipAudioCall call) {
                Log.i(TAG, "onCalling: ");
                super.onCalling(call);
            }

            @Override
            public void onCallHeld(SipAudioCall call) {
                Log.i(TAG, "onCallHeld: ");
                super.onCallHeld(call);
            }

            @Override
            public void onCallBusy(SipAudioCall call) {
                Log.i(TAG, "onCallBusy: ");
                super.onCallBusy(call);
            }

            @Override
            public void onCallEnded(SipAudioCall call) {
                Log.i(TAG, "onCallEnded: ");


            }

            @Override
            public void onCallEstablished(SipAudioCall call) {
                Log.i(TAG, "onCallEstablished: ");
                super.onCallEstablished(call);
            }
        };


        mainService  = MainService.getInstance();
        incomingCall = mainService.manager.takeAudioCall(intent,listener);
        if(mainService.manager.isIncomingCallIntent(intent)){
            incomingCallIntent = intent;
        }

        //starting call screen activity when the receiver receives incoming call
        Intent i    = new Intent(context, CallActivity.class);
        i.putExtra("name", peerName);
        i.putExtra("number", peerNumber);
        i.putExtra("callType", "Incoming");
        context.startActivity(i);

//Sip call received by YourReceiver is assigned to MainService.call so that the MainService can do the further processes.

        mainService.call = incomingCall;

    } catch (Exception e) {
        if (incomingCall != null) {
            incomingCall.close();
        }
    }
}

}

Running a service is the right way. You just have to start the activity which will handle your incoming call from the service. The activity can then bind to your service and request all it needs to take over the call. That's actually all you need.

For making a Service run continuously, start a Job scheduler to make this job be a part of android system jobs.

package com.xxx;

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class XXJobService extends JobService {
    private String TAG = "XXJobService";
    private Context context;

    public XXJobService() {
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        context = this;

        try {
            if (!MyApp.isServiceRunning(context, MainService.class.getName())) {
                Log.i(TAG, "onStartJob : MainService not running so start");

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    context.getApplicationContext()
                            .startForegroundService(new Intent(context, MainService.class)
                                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
                } else {
                    context.getApplicationContext()
                            .startService(new Intent(context, MainService.class)
                                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Exception - MainService not running: " + e.getLocalizedMessage());
        }


        Log.i(TAG, "onStartJob, returnValue = " + returnValue);
        return returnValue;
    }

    @Override
    public boolean onStopJob(JobParameters params) {

        boolean returnValue = true;

        Log.i(TAG, "onStopJob, returnValue = " + returnValue);
        return returnValue;
    }
}

Create an Application class in your project that extends Application and define it in your manifest also.

import android.app.ActivityManager;
import android.app.Application;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import java.util.Arrays;
import java.util.List;

public class MyApp extends Application {

    Context context;
    private static MyApp instance;
    static String TAG = "MyApp";
    MainService mainService;
    JobScheduler jobScheduler;
    private static final int JOB_ID = 1;

    public static MyApp getInstance() {
        return instance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        context  = this;
        instance = this;
        Log.i(TAG, "onCreate: ");

        // Job scheduler for android version Lolipop(Android 5.0) and above
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            try {
                jobScheduler             = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
                ComponentName jobService = new ComponentName(getPackageName(), XXJobService.class.getName());
                Log.e(TAG, "onCreate: ComponentName : " + jobService );
                JobInfo jobInfo          = new JobInfo.Builder(JOB_ID, jobService)
                        .setPersisted(true)
                        .setPeriodic(5000)
                        .build();
                int jobId                = jobScheduler.schedule(jobInfo);

                if (jobId == JobScheduler.RESULT_SUCCESS) {
                    Log.e(TAG, "JobScheduler RESULT_SUCCESS");
                    // Toast.makeText(context, "Successfully scheduled job : " + jobId, Toast.LENGTH_SHORT).show();
                } else {
                    Log.e(TAG, "JobScheduler RESULT_FAILURE: " + jobId);
                    // Toast.makeText(context, "RESULT_FAILURE: " + jobId, Toast.LENGTH_SHORT).show();
                }
            } catch (Exception e) {
                Log.e(TAG, "JobScheduler Exception : " + e.getLocalizedMessage());
            }
        }

        if (!isServiceRunning(context, MainService.class.getName())) {
            try {
                Intent intent = new Intent(context, MainService.class);

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    startForegroundService(intent);
                } else {
                    startService(intent);
                }
            } catch (Exception e) {
                Log.e(TAG, "Exception startService : " + e.getLocalizedMessage());
            }
        }

        mainService = MainService.getInstance();
        Log.e(TAG," MainSerivice : " + mainService);

        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                handleUncaughtException(t, e);
            }
        });
    }


    public static boolean isServiceRunning(Context context, String serviceClassName) {
        if (context == null || serviceClassName.equalsIgnoreCase("")) {
            Log.i(TAG, "isServiceRunning called with context = null or service name blank");
            return false;
        }

        ActivityManager activityManager                     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> services   = activityManager.getRunningServices(Integer.MAX_VALUE);

        for (ActivityManager.RunningServiceInfo runningServiceInfo : services) {
            if (runningServiceInfo.service.getClassName().equals(serviceClassName))
                return true;
        }

        return false;
    }

}

Finally, put a BootUpReceiver to automatically start the notofication and services whenever the phone is restarted. It should have the permission

 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />


<receiver
            android:name=".StartupReceiver"
            android:enabled="true">
            <intent-filter>
                <action android:name="com.xx.MainService" />
                <action android:name="android.intent.action.ACTION_SHUTDOWN" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />

                <category android:name="android.intent.category.HOME" />
            </intent-filter>
        </receiver>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM