package com.inou.clawdnode.calls import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.os.IBinder import android.telecom.Call import android.telecom.CallScreeningService import android.util.Log import com.inou.clawdnode.ClawdNodeApp import com.inou.clawdnode.protocol.CallIncomingEvent import com.inou.clawdnode.service.NodeService /** * Screens incoming calls before they ring. * Sends call info to Gateway for Claude to decide: answer, reject, or let ring. */ class CallScreener : CallScreeningService() { private val tag = "CallScreener" private var nodeService: NodeService? = null private val serviceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { nodeService = (service as NodeService.LocalBinder).getService() } override fun onServiceDisconnected(name: ComponentName?) { nodeService = null } } override fun onCreate() { super.onCreate() Log.i(tag, "CallScreener created") Intent(this, NodeService::class.java).also { intent -> bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) } } override fun onDestroy() { unbindService(serviceConnection) super.onDestroy() } override fun onScreenCall(callDetails: Call.Details) { val number = callDetails.handle?.schemeSpecificPart ?: "Unknown" val callId = callDetails.handle?.toString() ?: System.currentTimeMillis().toString() Log.i(tag, "Screening call from: $number") // Look up contact name val contactName = lookupContact(number) // Store call details for later action ActiveCalls.add(callId, callDetails) // Send event to Gateway val event = CallIncomingEvent( callId = callId, number = number, contact = contactName ) nodeService?.sendEvent(event) ClawdNodeApp.instance.auditLog.logCall( "CALL_INCOMING", number, contactName, "screening" ) // Default: let the call ring through // Gateway can send call_reject or call_silence command // For v0.1, we allow all calls but notify Gateway respondToCall(callDetails, CallResponse.Builder() .setDisallowCall(false) .setSkipCallLog(false) .setSkipNotification(false) .build() ) } private fun lookupContact(number: String): String? { // TODO: Look up in contacts // For now, return null (unknown caller) return null } } /** * Tracks active calls for command handling. */ object ActiveCalls { private val calls = mutableMapOf() fun add(callId: String, details: Call.Details) { calls[callId] = details } fun get(callId: String): Call.Details? = calls[callId] fun remove(callId: String) { calls.remove(callId) } }