CallScreeningService API

지프치프·5일 전
0

Android

목록 보기
91/91
post-thumbnail

“Android 로봇은 Google에서 제작하여 공유한 저작물을 복제하거나 수정한 것으로 Creative Commons 3.0 저작자 표시 라이선스의 약관에 따라 사용되었습니다.”


개요

안드로이드에는 CallScreeningService라는 API를 제공한다.

API Level 29 부터 지원하며 Service 구현 만으로 통화필터링을 할 수 있도록 도와준다.

기존에는 통화필터링을 브로드캐스트 리시버를 구현하여 넘어오는 인텐트를 통해 이벤트를 처리했지만
이 CallScreeningService는 시스템에서 전화 화면(CallScreen)이 전환되는 시점에
서비스를 바인딩하는 방식으로 처리하게 된다.

에제소스

class ScreeningService : CallScreeningService() {
    override fun onScreenCall(callDetails: Call.Details) {
        if(callDetails.callDirection == Call.Details.DIRECTION_OUTGOING) {
        	// 발신 전화는 리턴
            return
        }
        
        // 수신된 전화번호 가져오기
        val incomingNumber = callDetail.handle.schemeSpecificPart

		// 수신된 전화번호가 등록된 스팸번호인지 체크
        // checkSpamNumber() 함수는 예제를 위해 임의로 작성한 함수입니다.
        if(checkSpamNumber(incomingNumber)) {
        	// 전화 차단 시 수행할 작동들 구현
            
            // 시스템에 전달할 수신전화에 대한 상호작용 설정
            val response = CallResponse.Builder()
                    .setDisallowCall(boolean) // 전화 거부
                    .setRejectCall(boolean)   // 발신자에게 수신거절에 대한 피드백 전달 여부
                    .setSkipCallLog(boolean) // 통화기록 남김 여부
                    .setSkipNotification(boolean) // 통화알림(부재중) 남김 여부
                    .build()
                    
           // 시스템에 수신전화 전달
           respondToCall(callDetail, response)
        }
    }
}

setRejectCall(boolean)의 경우 자세히 설명해보자면 아래와 같다.
단말기에 전화가 수신되면 발신자에게 거절 피드백을 즉시 전달할지 여부를 설정하는 것으로
거절 피드백을 즉시 전달하면 발신자 입장에선 전화가 바로 거절된 것처럼 보이고
즉시 전달하지 않으면 수신자에겐 전화가 차단되었지만 발신자에겐 전화가 끊어지지 않고 통화연결음이 지속된다.

Service 클래스를 작성했다면 이제 AndroidManifest에 등록해주자.

<service
    android:name=".ScreeningService"
    android:permission="android.permission.BIND_SCREENING_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.CallScreeningService" />
    </intent-filter>
</service>


주의할점

스팸차단 기본앱 설정 필요

RoleManager를 통해 '스팸차단 기본앱'으로 설정이 필요하다.
아래 예제 소스를 참고해서 기본앱으로 설정하는 로직을 구현하면 된다.

private lateinit var requestRoleLauncher: ActivityResultLauncher<Intent>
private fun requestRole() {
        Logger.log("requestRole()")

        val roleManager = getSystemService(RoleManager::class.java)
        fun request() = requestRoleLauncher.launch(roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING))

        requestRoleLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            Logger.log("it result >> ${it.resultCode}")

            if(it.resultCode == RESULT_CANCELED) {
                AlertDialog.Builder(this)
                    .setMessage("기본 앱으로 지정하셔야 정상작동할 수 있습니다.")
                    .setPositiveButton("설정하기") { _, _ ->
                        request()
                    }
                    .setNegativeButton("앱 종료") { _, _ -> finishAffinity() }
                    .setCancelable(false)
                    .show()
            } else requestPermission()
        }

        request()
    }


'READ_CONTACTS' 권한 필요할 떄가 있음

기본적으로 CallScreeningService는 저장되지 않는 연락처에 한해서만 서비스가 바인딩된다.
단, READ_CONTACTS 권한이 허용된 경우 연락처에 있는 번호도 서비스가 바인딩된다.

저장되지 않은 번호 뿐만 아니라 사용자 주소록에 저장된 전화번호도 포함해서
서비스를 바인딩하려면 READ_CONTACTS 사용자에게 요청하도록 하자.

발신전화도 제어가 가능

위 예제 소스에서 눈치를 챘을 수도 있겠지만 아래 소스와 같이 발신전화에 대한 예외처리가 있었는데

if(callDetails.callDirection == Call.Details.DIRECTION_OUTGOING) {
	// 발신 전화는 리턴
    return
}

CallScreeningService는 전화화면이 전환될 때 서비스가 바인딩되는 형식이니
발신전화 또한 제어가 가능하다.

만약 전화상태(발신, 수신)를 다양하게 제어하고 싶다면 onScreenCall() 콜백 내에서 아래와 같이 분기처리를 해주면 될 것이다.

when(callDetail.callDirection) {
    Call.Details.DIRECTION_INCOMING -> {
        // 전화 수신
    }
    Call.Details.DIRECTION_OUTGOING -> {
        // 전화 발신
    }
    Call.Details.DIRECTION_UNKNOWN -> {
        // 알수없는 전화 방향
    }
}

5초 이내에 로직이 완료되어야 함

onScreenCall() 콜백이 호출된 후 5초 이내에 responseToCall() 함수를 호출하여 시스템에 응답을 전달해야 정상적으로 작동한다.

5초를 초과할 경우 respondToCall()함수를 호출해도 시스템으로 전달되지 않는다.
이로인해 발생하는 동작은 아래와 같다.

  • respondToCall() 함수 호출 무시
  • 수신 거부 대상인 번호이지만 시스템으로 설정을 전달하지 못했기 때문에 전화가 수신됨
  • 시스템에서 서비스의 바인딩을 강제로 끊음

그래서 onScreenCall() 콜백에서 오버헤드가 많은 작업은 가급적 피하는 것이 좋을 것 같다.

개인적으로 공부했던 것을 바탕으로 작성하다보니
잘못된 정보가 있을수도 있습니다.
인지하게 되면 추후 수정하겠습니다.
피드백은 언제나 환영합니다.
읽어주셔서 감사합니다.

profile
지프처럼 거침없는 개발을 하고싶은 개발자

0개의 댓글

관련 채용 정보