# Handling incoming calls In the previous section we added the capability of initiating the call. This section describes how to receive notifications and handle incoming calls. If you haven't yet initiated a call, go back and follow the steps in that [section](/docs/in-app-calling/getting-started/android/make-call). Note: To test if the call succeeded and audio connection is established, it's best to have 2 physical Android devices however you can initiate the call on the emulator. ## Setting up FCM push notifications To receive notifications about incoming calls, you must set up your application to work with either FCM or HMS Push Notification Platform. Information about how to add this capability and comparison of these platforms can be found [here](https://developers.sinch.com/docs/in-app-calling/android/push-notifications/). For this step-by-step guide we showcase FCM and assume that you have your google-services.json file generated and downloaded. 1. Copy your 'google-services.json' file to `/app/` directory. 2. Create `FCMService` class that extends FirebaseMessagingService. Declare the service inside AndroidManifest.xml file. *app/src/main/AndroidManifest.xml* ```xml ``` 3. Inside service's `onMessageReceived` callback make sure the payload received indicates an incoming call. If it does, pass it to bound SinchService and let the Sinch client handle notifying the observers. *app/src/main/java/com/sinch/rtc/sample/push/fcm/FcmListenerService.kt* ```kotlin override fun onMessageReceived(remoteMessage: RemoteMessage) { val data = remoteMessage.data if (!isSinchPushPayload(data)) { Log.d(TAG, "Non Sinch push payload received. Ignoring.") return } val result = try { queryPushNotificationPayload(applicationContext, data) } catch (e: Exception) { Log.e(TAG, "Error while executing queryPushNotificationPayload", e) return } object : ServiceConnection { private var callNotificationResult: CallNotificationResult? = null override fun onServiceConnected(name: ComponentName, service: IBinder) { callNotificationResult?.let { val sinchService = service as SinchService.SinchServiceInterface try { sinchService.relayRemotePushNotificationPayload(it) } catch (e: Exception) { Log.e(TAG, "Error while executing relayRemotePushNotificationPayload", e) } } callNotificationResult = null } override fun onServiceDisconnected(name: ComponentName) {} fun relayCallNotification(callNotificationResult: CallNotificationResult) { this.callNotificationResult = callNotificationResult createNotificationChannel(NotificationManager.IMPORTANCE_MAX) applicationContext.bindService( Intent(applicationContext, SinchService::class.java), this, BIND_AUTO_CREATE ) } }.relayCallNotification(result) } ``` See the implementation of `FcmListenerService` inside `sinch-rtc-sample-push` sample for a complete implementation of FCM messaging service that covers others aspects of Firebase messaging functionality. 4. Modify SinchService binder by adding method forwarding push payload to Sinch client. *app/src/main/java/com/sinch/rtc/sample/push/SinchService.kt* ```kotlin fun relayRemotePushNotificationPayload(result: CallNotificationResult) { ... createClientIfNecessary() sinchClient?.relayRemotePushNotification(result) } ``` ## Listening for incoming calls 1. Inside the Service create client method add logic responsible for adding a `CallControllerListener` to call client: *app/src/main/java/com/sinch/rtc/demovvsdk/SinchService.kt* ```kotlin private fun createClient(username: String) { // Client creation steps. sinchClient?.callController?.addCallControllerListener(SinchCallControllerListener()) } ``` 2. Implement `onIncomingCall` callback asking user if he wants to accept or decline the call: *app/src/main/java/com/sinch/rtc/sample/push/SinchService.kt* ```kotlin override fun onIncomingCall(callController: CallController, call: Call) { val intent = Intent(applicationContext, IncomingCallScreenActivity::class.java) .apply { putExtra( IncomingCallScreenActivity.EXTRA_ID, IncomingCallScreenActivity.MESSAGE_ID ) putExtra(CALL_ID, call.callId) } val inForeground = isAppOnForeground(applicationContext) if (!inForeground) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) } else { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !inForeground) { (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).notify( IncomingCallScreenActivity.MESSAGE_ID, createIncomingCallNotification(call.remoteUserId, intent) ) } else { applicationContext.startActivity(intent) } } ``` 3. Build and run the application on both devices. Log into the application providing 2 different usernames (TestCaller and TestCallee). 4. On the first device enter TestCallee as the 'callee name' and press **CALL**. 5. On the second device you should see a screen asking if you want to accept or decline the call. 6. Press **Accept** and begin your conversation! ## Ending the call Once the connection is established users should be able to finish the conversation. We will implement this feature by adding a simple button visible only after answering the call. 1. Inside `callscreen.xml` file define a button that stays initially hidden: *app/src/main/res/layout/callscreen.xml* ```xml ``` 2. Add the method responsible for handling click of the hangup button inside `CallScreenActivity`. *app/src/main/java/com/sinch/rtc/sample/push/CallScreenActivity.kt* ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... endCallButton.setOnClickListener { endCall() } } private fun endCall() { ... call?.hangup() finish() } ``` 3. Finally on the callee side, after answering the call we need to add an instance of the call listener to the call object itself. It's important to understand that in that case there are 2 call listeners alive. First one at the caller site (added via `callController.addCallControllerListener(SinchCallControllerListener())` method invocation) notifying about state of outgoing call and the second one, on the callee site added once the call is answered: *app/src/main/java/com/sinch/rtc/sample/push/IncomingCallScreenActivity.kt* ```kotlin override fun onServiceConnected() { val call = call if (call != null) { // Adding call specific listener. call.addCallListener(SinchCallListener()) remoteUserTextView.text = call.remoteUserId if (ACTION_ANSWER == action) { answerClicked() } else if (ACTION_IGNORE == action) { declineClicked() } } else { Log.e(TAG, "Started with invalid callId, aborting") finish() } } ``` ## Next steps Now that you've built a simple app to make and receive calls, learn more about the [Android SDK](https://developers.sinch.com/docs/in-app-calling/android-cloud/).