locked
Implementing sip through service in android RRS feed

  • Question

  • User311057 posted

    I'm developing an android app, which should receive sip calls. The sip connection should always be established, so i'm using service for this.

    I've stuck with some issues:

    1. the service destroys when app is closed.
    2. sipAudioCall works only if I start it right from the broadcast receiver. What I need is to send the call to the service, then open an activity with accept\decline functions, lastly open an activity, where call should be started (and can be finished)- but it doesn't work.
    3. after setting sip for register, registration calls multiple times

    P.S. I've tested a sip connection through main activity+ receiver, it works just fine. The problems started with service+multiple activities implementation

    manifest.xml

          <receiver android:name=".IncomingCallReceiver" android:enabled="true" />
          <service android:name=".SipService" android:enabled="true" />
        </application>
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.USE_SIP" />
        <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>  
      <uses-permission android:name="android.permission.WAKE_LOCK" />
    
      <uses-feature android:name="android.hardware.sip.voip" android:required="true" />
      <uses-feature android:name="android.software.sip" android:required="true" />
        <uses-feature android:name="android.hardware.wifi" android:required="true" />
        <uses-feature android:name="android.hardware.microphone" android:required="true" />
    

    MainActivity - here I check if service isn't running and start it + sip permissions.

         protected override void OnCreate(Bundle savedInstanceState)
            {
                ///stuff
                if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.UseSip) != Permission.Granted ||
                    ContextCompat.CheckSelfPermission(this, Manifest.Permission.RecordAudio) != Permission.Granted ||
                    ContextCompat.CheckSelfPermission(this, Manifest.Permission.ProcessOutgoingCalls) != Permission.Granted)
                    ActivityCompat.RequestPermissions(this, new[] { Manifest.Permission.UseSip, Manifest.Permission.RecordAudio, Manifest.Permission.ProcessOutgoingCalls }, 0);
    
                StartSipService();
            }
    
         private void StartSipService()
           {
             if (!IsServiceRunning(typeof(SipService)))   
               StartService(new Intent(this, typeof(SipService)));             
           }
    
         //its deprecated ,but works for own services
         private bool IsServiceRunning(Type cls)
           {
             var manager = (ActivityManager)GetSystemService(Context.ActivityService);
             return manager.GetRunningServices(int.MaxValue).Any(service => 
                          service.Service.ClassName.Equals(Java.Lang.Class.FromType(cls).CanonicalName));
           }
    

    SipService - it should register for sip + have binding for call accept\decline

    public class SipService : Service, ISipRegistrationListener
        {
            public IBinder Binder { get; private set; }
            public SipManager SipManager;
            public SipProfile SipProfile;
            public IncomingCallReceiver CallReceiver;
            public SipAudioCall AudioCall;
    
            public override void OnCreate()
            {
                RegisterReceiver();
                CreateSipProfile("login", "pass", "domain");  
                base.OnCreate();
            }
    
            public void RegisterReceiver()
            {
                var filter = new IntentFilter();
                filter.AddAction("com.myapp.INCOMING_CALL");
                CallReceiver = new IncomingCallReceiver();
                RegisterReceiver(CallReceiver, filter);
            }
    
            public override IBinder OnBind(Intent intent)
            {
                Binder = new SipBinder(this);
                return new SipBinder(this);
            }
    
            public void StartCall()
            {
                if (AudioCall == null) return;
                AudioCall.AnswerCall(30);
                AudioCall.StartAudio();
                if (AudioCall.IsMuted) AudioCall.ToggleMute();
            }
    
            public void StopCall()
            {
                AudioCall?.Close();
            }
    
            public override void OnDestroy()
            {
                Binder = null;
                CloseLocalSipProfile();
                base.OnDestroy();
            }
    
            public void OnRegistering(string localProfileUri)  {  }
    
            public void OnRegistrationDone(string localProfileUri, long expiryTime) {  }
    
            public void OnRegistrationFailed(string localProfileUri, SipErrorCodes errorCode, string errorMessage)
            {
                CloseLocalSipProfile();
            }
    
    
            private void CreateSipProfile(string username, string password, string domain)
            {
                if (SipManager == null)
                    SipManager = SipManager.NewInstance(this);
                var builder = new SipProfile.Builder(username, domain);
                builder.SetPassword(password);
                SipProfile = builder.Build();
                RegisterSipIncom?all();
            }
    
            private void RegisterSipIncom?all()
            {
                var intent = new Intent();
                intent.SetAction("tattelecom.nateks.INCOMING_CALL");
                var pendingIntent = PendingIntent.GetBroadcast(this, 0, intent, 
                         (PendingIntentFlags)FillInFlags.Data);
                SipManager?.Open(SipProfile, pendingIntent, null);
                SipManager?.SetRegistrationListener(SipProfile.UriString, this);           
            }
    
            public void CloseLocalSipProfile()
            {
                if (SipManager == null) return;
                if (SipProfile != null)
                    SipManager.Close(SipProfile.UriString);
                if (CallReceiver != null)
                    UnregisterReceiver(CallReceiver);
            }
        }
    }
    

    SipBinder

    public class SipBinder : Binder
        {
            public SipService Service { get; private set; }
            public SipBinder(SipService service)
            {
                Service = service;
            }
    
            public void StartCall()
            {
                Service.StartCall();
            }
    
            public void StopCall()
            {
                Service.StopCall();
            }
    
        }
    

    SipServiceConnection

    public class SipServiceConnection : Java.Lang.Object, IServiceConnection
        {
            private CallActivity _activityCall;
            private AcceptanceActivity _activityAcceptance;
            public bool IsConnected { get; private set; }
            public SipBinder Binder { get; private set; }
    
            public SipServiceConnection(CallActivity activity)
            {
                _activityCall= activity;
                IsConnected = false;
                Binder = null;
            }
    
            public SipServiceConnection(AcceptanceActivity activity)
            {
                _activityAcceptance= activity;
                IsConnected = false;
                Binder = null;
            }
    
    
            public void OnServiceConnected(ComponentName name, IBinder service)
            {
                Binder = service as SipBinder;
                IsConnected = Binder != null;
            }
    
            public void OnServiceDisconnected(ComponentName name)
            {
                IsConnected = false;
                Binder = null;
            }
    
            public void StartCall()
            {
                if (IsConnected) Binder?.StartCall();
            }
    
            public void StopCall()
            {
                if (IsConnected) Binder?.StopCall();
            }
    
        }
    }
    

    IncomingCallReceiver - should send call to service and open acceptanceactivity

        public class IncomingCallReceiver : BroadcastReceiver
        {
             public override void OnReceive(Context context, Intent intent)
            {
                SipAudioCall incomingCall = null;
    
                var listener = new SipAudioCall.Listener();
                SipService service= (SipService)context;
    
                incomingCall = service.SipManager.TakeAudioCall(intent, listener);
                if (incomingCall.IsMuted) incomingCall.ToggleMute();
                 service.AudioCall = incomingCall;
    
                var newIntent = new Intent(activity.ApplicationContext,typeof(AcceptanceActivity));
                newIntent.AddFlags(ActivityFlags.NewTask);
                service.StartActivity(newIntent );
    }}}
    

    AcceptanceActivity - binds to service, if user accept call - open call activity. if declines - service.closeCall

    protected override void OnCreate(Bundle savedInstanceState)
            {
                base.OnCreate(savedInstanceState);
                ///stuff
                if (_serviceConnection == null)
                    _serviceConnection = new SipServiceConnection(this);
                Intent serviceToStart = new Intent(this, typeof(SipService));
                BindService(serviceToStart, _serviceConnection, Bind.AutoCreate);
            }
    
            private void CloseOnClick(object sender, EventArgs e)
            {
                _serviceConnection?.StopCall();
                Finish();
            }
    
            private void AnswerOnClick(object sender, EventArgs e)
            {
                var root = new Intent(this, typeof(CallActivity));
                StartActivity(root);
                Finish();
            }
    

    CallActivity - binds to service , start audio Call and close call if needed

    _protected override void OnCreate(Bundle savedInstanceState)
            {
                base.OnCreate(savedInstanceState);
                if (_serviceConnection == null)
                    _serviceConnection = new SipServiceConnection(this);
                Intent serviceToStart = new Intent(this, typeof(SipService));
                BindService(serviceToStart, _serviceConnection, Bind.AutoCreate);
                StartCall();
            }
    
    
            private void CloseOnClick(object sender, EventArgs e)
            {
                _serviceConnection?.StopCall();
            }
    
            private void StartCall()
            {
                _serviceConnection?.StartCall();
            }_
    
    Monday, December 16, 2019 12:49 PM

Answers

  • User311057 posted

    The answer is - you have to add a delay between binding and startCall. I don't know why, but it doesn;t bind immediately

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, December 19, 2019 11:19 AM

All replies

  • User382871 posted

    the service destroys when app is closed To keep service running when the app is closed, try the following code. ``` public class MyService : Service { int NOTIFICATION_ID = 543; public static bool isServiceRunning = false;

    public override void OnCreate()
    {
        base.OnCreate();
    }
    
    public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
    {
    
        if (intent != null && intent.Action.Equals("action.START_SERVICE"))
        {
            startServiceWithNotification();
        }
        else stopMyService();
    
        return StartCommandResult.Sticky;
    }
    
    public override void OnDestroy()
    {
        isServiceRunning = false;
        base.OnDestroy();
    }
    
    void startServiceWithNotification()
    {
        if (isServiceRunning) return;
        isServiceRunning = true;
        //...
    
        StartForeground(NOTIFICATION_ID, notification);
    }
    
    void stopMyService()
    {
        StopForeground(true);
        StopSelf();
        isServiceRunning = false;
    }
    

    } ```

    Monday, December 16, 2019 3:04 PM
  • User311057 posted

    Ok . I figure out that you can't actually keep service running all the time for this particular case - you should wake up using push. Still - accepting the call in CallActivity is not working , although I'm getting AudioCall reference.

    Tuesday, December 17, 2019 8:28 AM
  • User311057 posted

    The answer is - you have to add a delay between binding and startCall. I don't know why, but it doesn;t bind immediately

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, December 19, 2019 11:19 AM