cometchat-android-v5-calls — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited cometchat-android-v5-calls (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Production-grade voice + video calling for native Android v5. Loaded by the cometchat-calls dispatcher when the project is Android v5 (detected from chat-sdk-android:4.x / chatuikit-android:5.x or asked when greenfield). Operates in two modes:
chatuikit-android UI Kit; just chat-sdk-android (for signaling) + calls-sdk-android + your own UI surfaces (CallButton on profile, CallLogsActivity, OngoingCallActivity).CometChatMessageHeader already exposes call buttons; this skill wires them to the Calls SDK and mounts the global IncomingCall listener at app root.Read these other skills first:
cometchat-calls — the dispatcher (mode selection, hard rules, anti-patterns)cometchat-android-v5-core — Chat SDK init, login order, local.properties + BuildConfig credential conventions, Application class wiringGround truth:
calls-sdk-android-5/sdk/calls-sdk-android-5/samples/references/ in this skill (16 docs, ~2300 lines, audited against [email protected] (v5 GA))This SKILL.md is the index + hard rules + Android-specific gotchas. Deep topic content lives in references/. Always read this file end-to-end first; load references on demand.
| Topic | Reference file | When to load |
|---|---|---|
| SDK setup (Cloudsmith, init, permissions, Jetifier) | references/setup.md | Step 1 of every integration |
Joining a session (SessionSettingsBuilder, voice vs video) | references/join-session.md | Step 2 — every integration |
| Dual-SDK ringing (Chat SDK + Calls SDK together) | references/ringing-integration.md | Standalone or additive — every integration with peer-to-peer call flow |
All SessionSettingsBuilder options (layouts, mode, hide buttons) | references/session-settings.md | When customizing in-call UI behavior |
| Event listeners (status, participant, media, button-click, layout) | references/event-listeners.md | When wiring call lifecycle to app state |
| Call history list | references/call-logs.md | When adding /calls route or in-app history |
| Recording (auto-start, recording events) | references/recording.md | Feature add |
| Screen sharing (viewer + presenter status) | references/screen-sharing.md | Feature add |
| Picture-in-picture | references/picture-in-picture.md | Feature add |
| Foreground service for ongoing calls | references/background-handling.md | Required — every standalone integration on Android 14+ |
| VoIP push (ConnectionService + FCM high-priority + PhoneAccount) | references/voip-calling.md | Required — every standalone integration; optional but strongly recommended in additive |
| Audio controls (mute/unmute, device switching) | references/audio-controls.md | Default UI customization |
| Video controls (camera on/off, switch camera) | references/video-controls.md | Default UI customization |
| Participant management (mute/kick/raise hand) | references/participant-management.md | Group calls / moderator features |
| Custom UI (control panel, participant list, layout) | references/custom-ui.md | When the default UI doesn't fit |
| In-call chat (messaging during active session) | references/in-call-chat.md | Feature add |
references/README.md is a skim-friendly index of the same.
These are the production-grade non-negotiables from the cometchat-calls dispatcher, specialized for Android v5. Every integration this skill writes must satisfy all seven.
The v5 Calls SDK has its own auth state, separate from the Chat SDK. After CometChat.login(uid, AUTH_KEY) succeeds, you MUST also call CometChatCalls.login(uid, AUTH_KEY, ...) — without it, the FIRST calls API call (initiateCall, joinSession, generateToken) throws "auth token cannot be null".
import com.cometchat.calls.core.CometChatCalls
import com.cometchat.calls.exceptions.CometChatException as CallsException
import com.cometchat.calls.model.CallUser // ← callback type, NOT chat User
// ✓ RIGHT — chat login first, then calls login
CometChat.login(uid, AUTH_KEY, object : CometChat.CallbackListener<User>() {
override fun onSuccess(user: User) {
CometChatCalls.login(uid, AUTH_KEY,
object : CometChatCalls.CallbackListener<CallUser>() {
override fun onSuccess(callUser: CallUser) { /* both ready */ }
override fun onError(e: CallsException) { /* surface */ }
})
}
override fun onError(e: CometChatException) { /* surface */ }
})Surprises:
User object does NOT expose authToken as a Kotlin property or Java getter on Android. Don't try user.authToken — use the (uid, apiKey) overload for dev, or fetch the auth token from your backend for production.com.cometchat.calls.model.CallUser, NOT com.cometchat.chat.models.User. Importing the wrong type produces "Type mismatch" at compile time.CometChat.getLoggedInUser() returns a non-null user on cold start, you still need to call CometChatCalls.login again before any calls API works.This trapped a real smoke run. The chat skill's loginAfter pattern doesn't transfer to calls; this is calls-specific.
Call lives in two placesThe Chat SDK initiates ringing (CometChat.initiateCall(...)); the Calls SDK runs the WebRTC session (CometChatCalls.joinSession(...)). They are NOT interchangeable, and there are two `Call` classes with the same simple name:
initiateCall, acceptCall, rejectCall. Carries sessionId, receiver, receiverType, callType. This is the one you almost always want.BaseMessage, so it surfaces in conversation/message-list contexts too). There is no com.cometchat.chat.models.Call — do not import that path (it does not exist).// ✓ RIGHT — initiate ringing
import com.cometchat.chat.core.Call
import com.cometchat.chat.core.CometChat
val outgoing = Call(receiverUid, CometChatConstants.RECEIVER_TYPE_USER, CometChatConstants.CALL_TYPE_VIDEO)
CometChat.initiateCall(outgoing, object : CometChat.CallbackListener<Call>() {
override fun onSuccess(initiated: Call) {
// initiated.sessionId is what the Calls SDK will join
}
override fun onError(e: CometChatException) { /* surface to UI */ }
})// ✓ RIGHT — join the WebRTC session after the receiver accepts
import com.cometchat.calls.core.CometChatCalls
import com.cometchat.calls.core.CallSession
import com.cometchat.calls.model.SessionType
// SessionSettingsBuilder is a NESTED class on CometChatCalls — access it via
// CometChatCalls.SessionSettingsBuilder, NOT a top-level import.
// `import com.cometchat.calls.core.SessionSettingsBuilder` fails: no such class.
// Audio vs video is set via setSessionType(SessionType.VOICE | SessionType.VIDEO).
// There is NO setIsAudioOnly() method on SessionSettingsBuilder — earlier
// drafts of this skill cited it; that was wrong. Confirmed against
// calls-sdk-android 5.0.x (ENG-35698 fix).
val settings = CometChatCalls.SessionSettingsBuilder()
.setSessionType(SessionType.VIDEO) // or SessionType.VOICE for audio-only
.build()
// 4-arg signature: sessionId (or token), settings, RelativeLayout container, callback.
CometChatCalls.joinSession(sessionId, settings, callContainer,
object : CometChatCalls.CallbackListener<CallSession>() {
override fun onSuccess(callSession: CallSession) {
// Hold onto callSession — in-call APIs (mute, video, layout, leave) live on it.
}
override fun onError(e: CometChatException) { /* surface */ }
})// ✗ WRONG — wrong Call class
import com.cometchat.chat.core.Call // the ONLY Call class (extends BaseMessage)
val c = Call(...) // compile-time errors on shape; or worse, runtime ambiguityStandalone-mode integration must ship working VoIP push: ConnectionService + FCM high-priority data messages + a registered PhoneAccount. Without it, missed calls don't ring → the integration isn't a product.
The full implementation is in references/voip-calling.md (~526 lines, the deepest doc in this skill). This skill's standalone-mode scaffold (Section 4) writes:
MyConnectionService extending android.telecom.ConnectionServicePhoneAccountHandle registered in Application.onCreate()FirebaseMessagingService that listens for incoming-call data messagesMANAGE_OWN_CALLS + BIND_TELECOM_CONNECTION_SERVICE permissions in AndroidManifest.xmlplaceIncomingCall flow that hands off to ConnectionService so the OS rings (lock-screen UI, hardware buttons)In additive mode, this is opt-in but strongly recommended — without it, the app must be foregrounded for incoming calls to ring, which contradicts user expectations.
Android 14+ silently terminates ongoing-call foreground services that don't declare a correct foregroundServiceType. The Calls SDK ships CometChatOngoingCallService, but the integration must register it correctly:
<!-- AndroidManifest.xml -->
<service
android:name="com.cometchat.calls.services.CometChatOngoingCallService"
android:foregroundServiceType="phoneCall|microphone|camera"
android:exported="false" />Common failure mode: copying older sample-app manifests that omit phoneCall (the type that allows the OS to keep the service alive in low-memory). On Android 14+, the call dies with ForegroundServiceStartNotAllowedException — visible in adb logcat, invisible in-app.
Full background-handling guide in references/background-handling.md.
The Calls SDK consumes the same auth token the Chat SDK uses. In dev, an Auth Key is fine. In production:
CometChat.login(authToken, callback) (Chat SDK) — Calls SDK reads the same auth contextcometchat-android-v5-production) replaces it with the token-endpoint pattern.This rule mirrors the chat dispatcher's auth rule — cometchat-android-v5-core already enforces it.
The most common "looks fine in dev, fails review" bug: camera light stays on after hangup, or microphone keeps recording until the activity is destroyed.
Required teardown when ending a call:
override fun onCallEnded(call: CallSession) {
// 1. End the Calls SDK session. Canonical v5 teardown is the CallSession
// INSTANCE method leaveSession(), guarded by isSessionActive — exactly
// what both calls-sdk-android sample apps use and what the v4→v5 migration
// guide maps the old static CometChatCalls.endSession() to. Matches this
// skill's references/call-session.md.
val session = CallSession.getInstance()
if (session.isSessionActive) session.leaveSession()
callContainer?.removeAllViews() // 2. Detach the WebRTC view
audioManager?.mode = AudioManager.MODE_NORMAL // 3. Release audio routing
audioManager?.abandonAudioFocusRequest(focusReq) // 4. Abandon audio focus
stopService(Intent(this, MyOngoingCallService::class.java)) // 5. Stop foreground service
finish() // 6. Pop the call activity
}Skipping any of these strands a system resource. The verification step (Section 9) checks that all six are present in the call-end path.
Standalone-mode integration prompts for four permissions, each with a rationale string:
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android 13+ -->
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" /> <!-- VoIP -->
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
tools:ignore="ProtectedPermissions" /> <!-- VoIP -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" /> <!-- Android 14+ -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" /> <!-- Android 14+ -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" /> <!-- Android 14+ -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />The runtime request (ActivityResultContracts.RequestMultiplePermissions) must include a denial-rationale dialog — Android lints this.
IncomingCall mounted at app root in standalone modeIn standalone mode, the CometChat.addCallListener(...) registration must happen in the Application.onCreate() (not in the foreground activity), so calls ring even when the app is backgrounded or the foreground activity has been destroyed. The listener routes to the ConnectionService (rule 1.2) which presents the OS-level incoming-call UI.
In additive mode (alongside chat), the listener can live in a CallsLifecycleObserver registered with the application's LifecycleOwner — the chat integration's CometChatActivity may be destroyed across configuration changes, but the observer survives.
Detailed walkthrough in references/setup.md. Summary:
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://dl.cloudsmith.io/public/cometchat/cometchat/maven/") }
}
}
// app/build.gradle.kts
dependencies {
implementation("com.cometchat:chat-sdk-android:4.0.+") // signaling
implementation("com.cometchat:calls-sdk-android:5.0.+") // WebRTC session
// additive mode also has chatuikit-android already on the classpath; do not re-add
}
// gradle.properties
android.useAndroidX=true
android.enableJetifier=trueInit in Application.onCreate():
class App : Application() {
override fun onCreate() {
super.onCreate()
// 1. Chat SDK init (signaling — required for ringing)
val appSettings = AppSettings.AppSettingsBuilder()
.subscribePresenceForAllUsers()
.setRegion(BuildConfig.COMETCHAT_REGION)
.build()
CometChat.init(this, BuildConfig.COMETCHAT_APP_ID, appSettings, /* callback */)
// 2. Calls SDK init — must come after Chat SDK init.
//
// The builder is the NESTED CallAppSettings.CallAppSettingBuilder
// (note: singular "Setting", not plural). It is NOT a top-level
// CallAppSettingsBuilder class — earlier drafts of this skill cited
// that wrong name; verified against calls-sdk-android 5.0.x (ENG-35698).
val callAppSettings = CallAppSettings.CallAppSettingBuilder()
.setAppId(BuildConfig.COMETCHAT_APP_ID)
.setRegion(BuildConfig.COMETCHAT_REGION)
.build()
CometChatCalls.init(this, callAppSettings, /* callback */)
// 3. Standalone mode: register PhoneAccount + global call listener (rules 1.2 + 1.7)
registerPhoneAccount()
CometChat.addCallListener(LISTENER_ID, GlobalCallListener)
}
}The Calls SDK ships these primitives. Names are stable across v5.x:
| Class / type | Purpose | Where it lives |
|---|---|---|
CometChatCalls | Top-level facade — init, joinSession, generateToken (session teardown is the instance method CallSession.leaveSession(), not a static here) | com.cometchat.calls.core |
CallAppSettings.CallAppSettingBuilder | Init-time config (appId, region, host) — nested class, singular "Setting" (NOT CallAppSettingsBuilder) | com.cometchat.calls.core |
GenerateToken | Wrapper returned by CometChatCalls.generateToken — extract the JWT via .token (NOT a raw String) | com.cometchat.calls.model |
CometChatCalls.SessionSettingsBuilder | Per-session config (layout, type, hide buttons, audio mode, recording) — nested class, access as CometChatCalls.SessionSettingsBuilder() | |
CometChatOngoingCallService | Foreground service for active calls | com.cometchat.calls.services |
SessionType | VOICE / VIDEO (NOT "AUDIO") | com.cometchat.calls.model |
LayoutType | TILE / SIDEBAR / SPOTLIGHT | com.cometchat.calls.model |
CallLogRequest.CallLogRequestBuilder | Paginated call history fetcher | com.cometchat.calls.core |
SessionStatusListener / ParticipantEventListener / MediaEventsListener / ButtonClickListener / LayoutListener | The five v5 call-event listeners — register each on the CallSession instance (callSession.addSessionStatusListener(...) etc.). v5 split the old monolithic CometChatCallsEventsListener into these. See references/event-listeners.md. | com.cometchat.calls.listeners |
In additive mode, chatuikit-android's CometChatMessageHeader already exposes call buttons — see cometchat-android-v5-components for the kit-side view.
Component-level deep dives: references/audio-controls.md, references/video-controls.md, references/participant-management.md, references/custom-ui.md.
When product === "voice-video" and there is no existing chat integration.
Split by calling mode:
Calls SDK ONLY. NO Chat SDK, NO ConnectionService, NO FCM-for-VoIP. Matches calls-sdk-android-5/samples/sample-app/. Scaffold:
CometChatCalls.init(...) ONLY in onCreate. No CometChat.init, no PhoneAccount, no ConnectionService.CometChatCalls.joinSession(sessionId, sessionSettings, container, CallbackListener). SessionStatusListener + ButtonClickListener registered on the CallSession from onSuccess. CometChatOngoingCallService.launch/abort. See references/call-session.md.FOREGROUND_SERVICE_MICROPHONE/CAMERA + CometChatOngoingCallService registration. NO ConnectionService.https://yourapp.com/meet/<sessionId> deep-link routing.Why no ConnectionService / no FCM-VoIP: session mode never receives an incoming-call FCM payload. No ringing.
Dual-SDK + telecom + push. Scaffold:
CometChat.initiateCall(...) on tap, navigates to OutgoingCallActivity.OngoingCallActivity when the receiver accepts (OutgoingCallStatusListener).CometChatCalls.joinSession(sessionId, settings, container, callback), handles all in-call controls (mute/camera/end), implements rule 1.5 teardown./calls equivalent — paginated history via CallLogRequest.CallLogRequestBuilder. Tap a row → re-call.foregroundServiceType (rule 1.3).ActivityResultContracts.RequestMultiplePermissions with denial-rationale strings (rule 1.6).Detailed walkthrough: references/ringing-integration.md (dual-SDK setup), references/voip-calling.md (push end-to-end), references/background-handling.md (foreground service).
When the project already has chatuikit-android integrated. The skill:
CometChatCalls.init(...) after the existing CometChat.init(...) block. Adds the global call listener.CometChatOngoingCallService registration with the correct type (rule 1.3).CometChat.initiateCall (rule 1.1).CometChatConversationsFragment in the existing tab/activity structure.Important: do NOT re-add chat-sdk-android or chatuikit-android to build.gradle.kts — they're already there. Only add calls-sdk-android.
com.cometchat.chat.models.Call import — the only Call class is com.cometchat.chat.core.Call. Cross-reference rule 1.1.SessionType.VOICE. Listed here because it's the most common cargo-culted mistake — the iOS SDK uses "audio" terminology.androidx.legacy.support artifacts inside the SDK throw Duplicate class android.support.v4.* at build. Rule lives in setup.CometChatCalls.init() is process-scoped — call it once in Application.onCreate(). Re-init does not "refresh" the auth context; logout-handling lives in rule 1.4's token replay.const val LISTENER_ID = "global-call-listener").After scaffolding, verify (the skill writes Espresso-style smoke tests where possible; otherwise prompts the user to confirm):
Static (the agent checks before claiming done):
calls-sdk-android in app/build.gradle.kts dependenciessettings.gradle.ktsandroid.useAndroidX=true and android.enableJetifier=true in gradle.propertiesCometChat.init followed by CometChatCalls.init in Application.onCreateRECORD_AUDIO / CAMERA / POST_NOTIFICATIONS / MANAGE_OWN_CALLS permissions in AndroidManifest.xmlFOREGROUND_SERVICE_* Android 14+ permissions in AndroidManifest.xmlCometChatOngoingCallService with foregroundServiceType="phoneCall|microphone|camera"CallSession.getInstance().leaveSession() (guarded by isSessionActive) + removeAllViews() + audio-focus abandon + service stop + finish (rule 1.5)ConnectionService subclass + PhoneAccount registration + FCM MessagingService for incoming-call data messagesApplication.onCreate, not an activityRuntime (real device — the skill prompts the user):
cometchat-calls — the dispatcher that loads this skillcometchat-android-v5-core — Chat SDK init, login, env conventionscometchat-android-v5-components — CometChatMessageHeader call buttons (additive mode)cometchat-android-v5-push — FCM setup, notification handling (overlap with VoIP push but distinct)cometchat-android-v5-production — server-minted auth tokens, ProGuard rules for the Calls SDKcometchat-android-v5-troubleshooting — symptom-to-cause for the common failure modes (Jetifier, foregroundServiceType, MANAGE_OWN_CALLS not granted)~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.